[하만]세미콘 아카데미/verilog

0516 UART

내머리속은이런걸로 2024. 6. 22. 11:32

UART

: Universal Asynchronous Receiver/Transmitter

직렬 통신을 위한 하드웨어 장치 또는 인터페이스

 

  • 비동기 통신 : 클럭 신호 없이 데이터를 전송한다 [Master가 없다].
    • 송신기와 수신기는 독립적으로 동작하며, 클럭 신호 대신 baudrate[전송 속도]를 설정하여 동작한다.
  • 직렬 전송 : 데이터가 한 비트씩 순차적으로 전송된다. LSB → MSB 순서로 전송한다.
  • 데이터 프레이밍 : 시작 비트, 데이터 비트, 패리티 비트[옵션], 정지 비트로 구성된다.

UART
8bit Data

데이터 프레이밍에서 봤듯이 Start Bit, Data Bit, Stop Bit가 전달된다.

위의 그림과 아래의 코드에서는 Parity Bit를 포함하지 않는다.

 

  • IDLE : 1 신호를 보내는 1 bit를 계속 보내며 Data를 보내고 있지 않는다는 신호이다.
  • START : Data를 보내겠다는 시작 신호로 0 신호를 보내는 1 bit를 보낸다. 1의 신호를 받다가 0을 받음으로써 신호가 구별된다.
  • DATA : 8bit Data 중 LSB부터 8개의 bit를 차례대로 보낸다.
  • STOP : Data를 모두 보냈다는 신호로 1의 신호 1bit를 보낸다.
  •  
  • Parity bit : 데이터 전송 중 오류를 감지하기 위한 비트. 데이터 전체 비트 수를 짝수 또는 홀수로 맞춘다.  
    • 비트 수를 확인하여 예상한 비트 개수와 다르면 오류를 감지한다.
    • 오류를 감지하지만, 어떤 비트가 잘못되었는지 알 수는 없다.

uart.v

[baudrate : 9600]

module uart (
    input clk,
    input reset,
    input start,
    input [7:0] tx_data,
    output tx_done,
    output txd
);

 buadrate_generator U_BR_Gen(
    .clk(clk),
    .reset(reset),
    .br_tick(br_tick)
);

    transmitter U_TxD (
        .clk(clk),
        .reset(reset),
        .br_tick(br_tick),
        .start(start),
        .data(tx_data),
        .tx_done(tx_done),
        .tx(txd)
    );
endmodule

module buadrate_generator (
    input  clk,
    input  reset,
    output br_tick
);
    reg [$clog2(100_000_000 / 9600)-1:0] counter_reg, counter_next;
    reg tick_reg, tick_next;

    assign br_tick = tick_reg;
    always @(posedge clk, posedge reset) begin
        if (reset) begin
            counter_reg <= 0;
            tick_reg = 1'b0;
        end else begin
            counter_reg <= counter_next;
            tick_reg = tick_next;
        end
    end
       always @(*) begin
            if(counter_reg == 100_000_000 / 9600 -1) begin
           // if(counter_reg == 10-1)begin // for simulation
                counter_next = 0; 
                tick_next = 1'b1;     
            end else begin
                counter_next = counter_reg + 1;   
                tick_next = 1'b0; 
            end
       end
   

endmodule

module transmitter (
    input clk,
    input reset,
    input br_tick,
    input start,
    input [7:0] data,
    output tx_done,
    output tx
);
    localparam IDLE = 0, START = 1, STOP = 10;
    localparam D0 = 2, D1 = 3, D2 = 4, D3 = 5, D4 = 6, D5 = 7, D6 = 8, D7 = 9;


    reg [3:0] state, state_next;
    reg [7:0] r_data;  //data 임시 저장 공간
    reg tx_reg, tx_next;
    reg tx_done_reg, tx_done_next;

    assign tx = tx_reg;
    assign tx_done = tx_done_reg;

    // state register
    always @(posedge clk, posedge reset) begin
        if (reset) begin
            state  <= IDLE;
            tx_reg <= 1'b0;
            tx_done_reg <= 1'b0;
        end else begin
            state  <= state_next;
            tx_reg <= tx_next;
            tx_done_reg <= tx_done_next;
        end
    end

    // next state combinational logic
    always @(*) begin
        state_next = state;
        case (state)
            IDLE: if (start) state_next = START;
            START: if (br_tick) state_next = D0;
            D0: if (br_tick) state_next = D1;
            D1: if (br_tick) state_next = D2;
            D2: if (br_tick) state_next = D3;
            D3: if (br_tick) state_next = D4;
            D4: if (br_tick) state_next = D5;
            D5: if (br_tick) state_next = D6;
            D6: if (br_tick) state_next = D7;
            D7: if (br_tick) state_next = STOP;
            STOP: if (br_tick) state_next = IDLE;
        endcase
    end

    // output combinational logic
    always @(*) begin
        tx_next = tx_reg;
        tx_done_next = 1'b0;
        case (state)
            IDLE: tx_next = 1'b1;
            START: begin
                tx_next = 1'b0;
                r_data  = data;
            end
            D0:   tx_next = r_data[0];
            D1:   tx_next = r_data[1];
            D2:   tx_next = r_data[2];
            D3:   tx_next = r_data[3];
            D4:   tx_next = r_data[4];
            D5:   tx_next = r_data[5];
            D6:   tx_next = r_data[6];
            D7:   tx_next = r_data[7];
            STOP: begin 
                tx_next = 1'b1;
                if(state_next == IDLE) tx_done_next = 1'b1;
            end
        endcase
    end
endmodule

 

 - DATA State에서 D0부터 D7까지 r_data를 직접 입력해주는 방식이다.

 

button.v

`timescale 1ns / 1ps

module button (
    input  clk,
    input  in,
    output out
);

    localparam N = 8;  // shift 횟수

    reg [N-1 : 0] q_reg, q_next;

    always @(posedge clk) begin
        q_reg <= q_next;
    end

    // next state logic
    always @(q_reg, in) begin
        q_next = {in, q_reg[N-1:1]};
    end

    // output logic
    assign out = (&q_reg[N-1:1] & ~q_reg[0]);

endmodule

 

uart_test

`timescale 1ns / 1ps

module uart_test(
    input clk,
    input reset,
    input btn_start,
    output txd
    );

    wire w_btn_start;
    
    button U_Btn_start(
    .clk(clk),
    .in(btn_start),
    .out(w_btn_start)
    );

      uart U_UART_TX (
    .clk(clk),
    .reset(reset),
    .start(w_btn_start),
    .tx_data(8'h41), // 'A'
    .tx_done(),
    .txd(txd)
);
endmodule

 

tb_uart

`timescale 1ns / 1ps

module tb_uart();
    reg clk;
    reg reset;
    reg start;
    reg [7:0] tx_data;
    wire tx_done;
    wire txd;


    uart dut (
    .clk(clk),
    .reset(reset),
    .start(start),
    .tx_data(tx_data),
    .tx_done(tx_done),
    .txd(txd)
);

    always #5 clk = ~clk;

    initial begin
        clk = 1'b0;
        reset = 1'b1;
        start = 1'b0;
        tx_data = 0;
    end
    initial begin
        #20 reset = 1'b0;
        #20 tx_data = 8'haf; start = 1'b1;
        #10 start = 1'b0;
    end
endmodule

 

simulation

 

constraint

## Clock signal
set_property -dict { PACKAGE_PIN W5   IOSTANDARD LVCMOS33 } [get_ports { clk }]; #IO_L12P_T1_MRCC_34 ,Sch=CLK100MHZ
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports clk]
##Buttons

set_property -dict { PACKAGE_PIN U18  IOSTANDARD LVCMOS33 } [get_ports { reset }]; #IO_L18N_T2_A11_D27_14 ,Sch=BTNC
set_property -dict { PACKAGE_PIN T18  IOSTANDARD LVCMOS33 } [get_ports { btn_start }]; #IO_L17N_T2_A13_D29_14 ,Sch=BTNU
##USB-RS232 Interface

#set_property -dict { PACKAGE_PIN B18  IOSTANDARD LVCMOS33 } [get_ports { RsRx }]; #IO_L19P_T3_16      ,Sch=UART_RXD_IN
set_property -dict { PACKAGE_PIN A18  IOSTANDARD LVCMOS33 } [get_ports { txd }]; #IO_L19N_T3_VREF_16 ,Sch=UART_TXD_OUT

 

video

 - btn_start 버튼을 누르면 'A'가 나타난다.

 


uart.hw

`timescale 1ns / 1ps

module uart(
    input clk,
    input reset,
    input start,
    input [7:0] tx_data,
    output tx_done,
    output txd
);

wire br_tick;

buadrate_generator U_BR_Gen(
    .clk(clk),
    .reset(reset),
    .br_tick(br_tick)
);

transmitter U_TxD(
    .clk(clk),
    .reset(reset),
    .br_tick(br_tick),
    .start(start),
    .data(tx_data),
    .tx_done(tx_done),
    .tx(txd)
);
endmodule

module buadrate_generator(
    input clk,
    input reset,
    output br_tick
);
    reg[$clog2(100_000_000/9600)-1 : 0] counter_reg, counter_next;
    reg tick_reg, tick_next;

    assign br_tick = tick_reg;

    always @(posedge clk, posedge reset) begin
        if(reset) begin
            counter_reg <= 0;
            tick_reg <= 1'b0;
        end else begin
            counter_reg <= counter_next;
            tick_reg <= tick_next;
        end
    end

    always @(*) begin
        counter_next = counter_reg;
        //if (counter_reg == 100_000_000/9600 -1) begin   // button
            if (counter_reg == 10-1) begin //for simulation
            counter_next = 0;
            tick_next = 1'b1;
        end else begin
            counter_next = counter_reg + 1;
            tick_next = 1'b0;
        end
    end
endmodule

module transmitter (
    input clk,
    input reset,
    input br_tick,
    input start,
    input [7:0] data,
    output tx_done,
    output tx
);
    localparam IDLE = 0, START = 1, DATA = 2, STOP = 3;

    reg [1:0] state, state_next;
    reg [7:0] r_data;
    reg tx_reg, tx_next;
    reg tx_done_reg, tx_done_next;
    reg [2:0] bit_count, bit_count_next;

    assign tx = tx_reg;
    assign tx_done = tx_done_reg;

    //start register 
    always @(posedge clk, posedge reset) begin
        if (reset) begin
            state <= IDLE;
            tx_reg <= 1'b0;
            tx_done_reg <= 1'b0;
            bit_count <= 3'b000;
        end else begin
            state <= state_next; 
            tx_reg <= tx_next;
            tx_done_reg <= tx_done_next;
            bit_count <= bit_count_next;
        end
    end

    //next state combinational logic
    always @(*) begin
        state_next = state;
        bit_count_next = bit_count;
        case(state) 
            IDLE : if(start) state_next = START;
            START : if(br_tick) state_next = DATA;
            DATA : if(br_tick) begin   
                if(bit_count == 7) begin
                    bit_count_next = 3'b000;
                    state_next = STOP;
                end else begin
                    bit_count_next = bit_count + 1;
                end
            end
            STOP : if(br_tick) state_next = IDLE;
        endcase
    end

     //output combinational logic
    always @(*) begin
        tx_next = tx_reg;
        tx_done_next = 1'b0;
        //r_data = 0;
        case(state)
            IDLE : tx_next = 1'b1;
            START : begin
                tx_next = 1'b0;
                r_data  = data;
            end
            DATA : tx_next = r_data[bit_count];
            STOP : begin
                tx_next = 1'b1;
                if(state_next == IDLE) tx_done_next = 1'b1;
            end
        endcase
    end

endmodule

- 앞의 uart.v와 달리 bit_count를 사용하여 0부터 7까지 총 8번 반복해서 r_data의 bit를 보내는 방식이다. 

 

schemaitc

 

 

simulation

 

video

- 버튼을 누를 때마다 'A'가 나타난다.

'[하만]세미콘 아카데미 > verilog' 카테고리의 다른 글

0517 uart_tx_rx  (0) 2024.06.22
0517 uart_tx  (0) 2024.06.22
0516 upCounter_FSM  (0) 2024.06.22
0514 upCounter_FND_FSM  (0) 2024.06.22
0514 upCounter_Fnd_moore_machine  (0) 2024.06.22