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

0524 uart fifo

내머리속은이런걸로 2024. 7. 9. 17:44

uart fifo schematic

 

uart

`timescale 1ns / 1ps

module uart (  // PC
    input        clk,
    input        reset,
    // Transmitter
    output       tx,
    input        start,
    input  [7:0] tx_data,
    output       tx_done,
    // Receiver
    input        rx,
    output [7:0] rx_data,
    output       rx_done
);
    wire w_br_tick;
    wire [7:0] w_rx_data;

    baudrate_generator U_BAUDRATE_GEN (
        .clk(clk),
        .reset(reset),
        .br_tick(w_br_tick)
    );

    transmitter U_Transmitter (
        .clk(clk),
        .reset(reset),
        .br_tick(w_br_tick),
        .start(start),
        .tx_data(tx_data),
        .tx(tx),
        .tx_done(tx_done)
    );

    receiver U_Receiver (
        .clk(clk),
        .reset(reset),
        .br_tick(w_br_tick),
        .rx(rx),
        .rx_data(rx_data),
        .rx_done(rx_done)
    );

endmodule

module baudrate_generator (
    input  clk,
    input  reset,
    output br_tick
);
    // 16 bit sampling -> baudrate 16 times
    reg [$clog2(100_000_000 / 9600 / 16)-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 / 16 - 1) begin
        //if (counter_reg == 3) 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] tx_data,
    output       tx,
    output       tx_done
);

    localparam IDLE = 0, START = 1, DATA = 2, STOP = 3;

    reg [1:0] state, state_next;
    reg tx_reg, tx_next;
    reg tx_done_reg, tx_done_next;
    reg [7:0] data_tmp_reg, data_tmp_next;
    reg [3:0] br_cnt_reg, br_cnt_next;
    reg [2:0] data_bit_cnt_reg, data_bit_cnt_next;

    assign tx = tx_reg;
    assign tx_done = tx_done_reg;

    always @(posedge clk, posedge reset) begin
        if (reset) begin
            state            <= IDLE;
            tx_reg           <= 1'b0;
            br_cnt_reg       <= 0;
            data_bit_cnt_reg <= 0;
            data_tmp_reg     <= 0;
            //tx_done_reg      <= 1'b0;
        end else begin
            state            <= state_next;
            tx_reg           <= tx_next;
            br_cnt_reg       <= br_cnt_next;
            data_bit_cnt_reg <= data_bit_cnt_next;
            data_tmp_reg     <= data_tmp_next;
            //tx_done_reg      <= tx_done_next;
        end
    end

    always @(*) begin
        state_next        = state;
        data_tmp_next     = data_tmp_reg;
        tx_next           = tx_reg;
        br_cnt_next       = br_cnt_reg;
        data_bit_cnt_next = data_bit_cnt_reg;
        //tx_done_next      = tx_done_reg;
        case (state)
            IDLE: begin
                tx_done_reg = 1'b0;
                tx_next     = 1'b1;
                if (start) begin
                    state_next = START;
                    data_tmp_next = tx_data;
                    br_cnt_next = 0;  // baud rate 16bit sampling
                    data_bit_cnt_next = 0;
                end
            end
            START: begin
                tx_next = 1'b0;
                if (br_tick) begin
                    if (br_cnt_reg == 15) begin
                        state_next  = DATA;
                        br_cnt_next = 0;
                    end else begin
                        br_cnt_next = br_cnt_reg + 1;
                    end
                end
            end
            DATA: begin
                tx_next = data_tmp_reg[0];
                if (br_tick) begin
                    if (br_cnt_reg == 15) begin
                        if (data_bit_cnt_reg == 7) begin
                            state_next  = STOP;
                            br_cnt_next = 0;
                        end else begin
                            data_bit_cnt_next = data_bit_cnt_reg + 1;
                            data_tmp_next     = {1'b0, data_tmp_reg[7:1]};
                            br_cnt_next       = 0;
                        end

                    end else begin
                        br_cnt_next = br_cnt_reg + 1;
                    end
                end
            end
            STOP: begin
                tx_next = 1'b1;
                if (br_tick) begin
                    if (br_cnt_reg == 15) begin
                        tx_done_reg = 1'b1;
                        state_next  = IDLE;
                    end else begin
                        br_cnt_next = br_cnt_reg + 1;
                    end
                end
            end
        endcase
    end

endmodule

module receiver (
    input        clk,
    input        reset,
    input        br_tick,
    input        rx,
    output [7:0] rx_data,
    output       rx_done
);

    localparam IDLE = 0, START = 1, DATA = 2, STOP = 3;

    reg [1:0] state, state_next;
    reg [7:0] rx_data_reg, rx_data_next;
    reg rx_done_reg, rx_done_next;
    reg [3:0] br_cnt_reg, br_cnt_next;
    reg [2:0] data_bit_cnt_reg, data_bit_cnt_next;

    assign rx_data = rx_data_reg;
    assign rx_done = rx_done_reg;

    always @(posedge clk, posedge reset) begin
        if (reset) begin
            state            <= IDLE;
            rx_data_reg      <= 0;
            rx_done_reg      <= 1'b0;
            br_cnt_reg       <= 0;
            data_bit_cnt_reg <= 0;
        end else begin
            state            <= state_next;
            rx_data_reg      <= rx_data_next;
            rx_done_reg      <= rx_done_next;
            br_cnt_reg       <= br_cnt_next;
            data_bit_cnt_reg <= data_bit_cnt_next;
        end
    end

    always @(*) begin
        state_next        = state;
        br_cnt_next       = br_cnt_reg;
        data_bit_cnt_next = data_bit_cnt_reg;
        rx_data_next      = rx_data_reg;
        rx_done_next      = rx_done_reg;
        case (state)
            IDLE: begin
                rx_done_next = 1'b0;
                if (rx == 1'b0) begin
                    br_cnt_next       = 0;
                    data_bit_cnt_next = 0;
                    rx_data_next      = 0;
                    state_next        = START;
                end
            end
            START: begin
                if (br_tick) begin
                    if (br_cnt_reg == 7) begin
                        br_cnt_next = 0;
                        state_next  = DATA;
                    end else begin
                        br_cnt_next = br_cnt_reg + 1;
                    end
                end
            end
            DATA: begin
                if (br_tick) begin
                    if (br_cnt_reg == 15) begin
                        br_cnt_next  = 0;
                        rx_data_next = {rx, rx_data_reg[7:1]};
                        if (data_bit_cnt_reg == 7) begin
                            state_next  = STOP;
                            br_cnt_next = 0;
                        end else begin
                            data_bit_cnt_next = data_bit_cnt_reg + 1;
                        end
                    end else begin
                        br_cnt_next = br_cnt_reg + 1;
                    end
                end
            end
            STOP: begin
                if (br_tick) begin
                    if (br_cnt_reg == 15) begin
                        br_cnt_next  = 0;
                        rx_done_next = 1'b1;
                        state_next   = IDLE;
                    end else begin
                        br_cnt_next = br_cnt_reg + 1;
                    end
                end
            end
        endcase
    end

endmodule

 

fifo.v

`timescale 1ns / 1ps

module fifo #(
    parameter ADDR_WIDTH = 3,
    DATA_WIDTH = 8
) (
    input                     clk,
    input                     reset,
    input                     wr_en,
    output                    full,
    input  [DATA_WIDTH - 1:0] wdata,
    input                     rd_en,
    output [DATA_WIDTH - 1:0] rdata,
    output                    empty
);

    wire [ADDR_WIDTH-1:0] w_waddr, w_raddr;

    register_file #(
        .ADDR_WIDTH(ADDR_WIDTH),
        .DATA_WIDTH(DATA_WIDTH)
    ) U_RegFile (
        .clk  (clk),
        .wr_en(wr_en & ~full),
        .waddr(w_waddr),
        .wdata(wdata),
        .raddr(w_raddr),
        .rdata(rdata)
    );

    fifo_control_unit #(
        .ADDR_WIDTH(ADDR_WIDTH)
    ) U_FIFO_CU (
        .clk  (clk),
        .reset(reset),
        // write
        .wr_en(wr_en),
        .full (full),
        .waddr(w_waddr),
        // read
        .rd_en(rd_en),
        .empty(empty),
        .raddr(w_raddr)
    );

endmodule

module register_file #(
    parameter ADDR_WIDTH = 3,
    DATA_WIDTH = 8
) (
    input                     clk,
    input                     wr_en,
    input  [ADDR_WIDTH - 1:0] waddr,
    input  [DATA_WIDTH - 1:0] wdata,
    input  [ADDR_WIDTH - 1:0] raddr,
    output [DATA_WIDTH - 1:0] rdata
);
    reg [DATA_WIDTH-1:0] mem[0:2**ADDR_WIDTH-1];

    always @(posedge clk) begin
        if (wr_en) mem[waddr] <= wdata;
    end

    assign rdata = mem[raddr];
endmodule

module fifo_control_unit #(
    parameter ADDR_WIDTH = 3
) (
    input                   clk,
    input                   reset,
    // write
    input                   wr_en,
    output                  full,
    output [ADDR_WIDTH-1:0] waddr,
    // read
    input                   rd_en,
    output                  empty,
    output [ADDR_WIDTH-1:0] raddr
);
    reg [ADDR_WIDTH-1:0] wr_ptr_reg, wr_ptr_next;
    reg [ADDR_WIDTH-1:0] rd_ptr_reg, rd_ptr_next;
    reg full_reg, full_next, empty_reg, empty_next;

    assign waddr = wr_ptr_reg;
    assign raddr = rd_ptr_reg;
    assign full  = full_reg;
    assign empty = empty_reg;

    always @(posedge clk, posedge reset) begin
        if (reset) begin
            wr_ptr_reg <= 0;
            rd_ptr_reg <= 0;
            full_reg   <= 1'b0;
            empty_reg  <= 1'b1;
        end else begin
            wr_ptr_reg <= wr_ptr_next;
            rd_ptr_reg <= rd_ptr_next;
            full_reg   <= full_next;
            empty_reg  <= empty_next;
        end
    end

    always @(*) begin
        wr_ptr_next = wr_ptr_reg;
        rd_ptr_next = rd_ptr_reg;
        full_next   = full_reg;
        empty_next  = empty_reg;

        case ({
            wr_en, rd_en
        })
            2'b01: begin  // read
                if (!empty_reg) begin
                    full_next   = 1'b0;
                    rd_ptr_next = rd_ptr_reg + 1;
                    if (rd_ptr_next == wr_ptr_reg) begin
                        empty_next = 1'b1;
                    end
                end
            end
            2'b10: begin  // write
                if (!full_reg) begin
                    empty_next  = 1'b0;
                    wr_ptr_next = wr_ptr_reg + 1;
                    if (wr_ptr_next == rd_ptr_reg) begin
                        full_next = 1'b1;
                    end
                end
            end
            2'b11: begin  // write, read
                if (empty_reg) begin
                    wr_ptr_next = wr_ptr_reg;
                    rd_ptr_next = rd_ptr_reg;
                end else begin
                    wr_ptr_next = wr_ptr_reg + 1;
                    rd_ptr_next = rd_ptr_reg + 1;
                end
            end
        endcase
    end

endmodule

 

 

tb_uart_fifo.sv

`timescale 1ns / 1ps

module tb_uart_fifo ();

    reg        clk;
    reg        reset;
    wire       tx;
    reg        tx_en;
    reg  [7:0] tx_data;
    wire       tx_full;
    reg        rx;
    reg  [7:0] rx_data;
    reg       rx_en;
    reg       rx_empty;
    wire       w_tx_rx_loop;

    uart_fifo dut (
        .clk(clk),
        .reset(reset),
        .tx(w_tx_rx_loop),
        .tx_en(tx_en),
        .tx_data(tx_data),
        .tx_full(tX_full),
        .rx(w_tx_rx_loop),
        .rx_en(rx_en),
        .rx_data(rx_data),
        .rx_empty(rx_empty)
    );

    always #5 clk = ~clk;

    initial begin
        clk   = 0;
        reset = 1'b1;
        tx_en = 1'b0;
        rx_en = 1'b0;
    end

    initial begin
        #80 reset = 1'b0;
        #50;
        @(posedge clk);
        tx_data = "0";
        tx_en   = 1'b1;
        @(posedge clk);
        tx_en = 1'b0; 
        @(posedge clk);
        tx_data = "1";
        tx_en   = 1'b1;
        @(posedge clk);
        tx_en = 1'b0; 
        @(posedge clk);
        tx_data = "2";
        tx_en   = 1'b1;
        @(posedge clk);
        tx_en = 1'b0;
        @(~rx_empty)
        while (!rx_empty) begin
            rx_en = 1'b1;
            @(posedge clk);
        end
    end
endmodule

 

schematic

 

 

simulation

baudrate_generator : if(counter_reg == 3)으로 실행하기

 

constraint

##Buttons

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

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

 

video