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

0525 uart tx rx systemverilog

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

uart_tx

`timescale 1ns / 1ps

module uart_tx (  // PC
    input        clk,
    input        reset,
    input        start,
    input [7:0]  tx_data,
    output       tx,
    output		 tx_done
);
    wire w_br_tick;

    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)
    );

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

 

schematic

 

tb_uart_tx.sv

`timescale 1ns / 1ps

interface tr_interface;
    logic       clk;
    logic       reset;
    logic       start;
    logic [7:0] tx_data;
    logic       tx;
    logic       tx_done;
endinterface

class transaction;
    rand logic [7:0] tx_data;
    logic            tx;
    logic            tx_done;

    task display(string name);
        $display("[%s] tx_data: %b, tx: %b, tx_done: %b", name, tx_data, tx,
                 tx_done);
    endtask
endclass

class generator;
    transaction trans;
    mailbox #(transaction) gen2drv_mbox;
    event gen_next_event;

    function new(mailbox#(transaction) gen2drv_mbox, event gen_next_event);
        this.gen2drv_mbox   = gen2drv_mbox;
        this.gen_next_event = gen_next_event;
    endfunction  //new()

    task run(int count);
        repeat (count) begin
            trans = new();
            assert (trans.randomize())
            else $error("[GEN] trans.randomize() error!");
            gen2drv_mbox.put(trans);
            trans.display("GEN");
            @(gen_next_event);
        end
    endtask
endclass  //generator

class driver;
    transaction trans;
    mailbox #(transaction) gen2drv_mbox;
    virtual tr_interface tr_intf;
    reg [7:0] data;

    integer count;

    function new(virtual tr_interface tr_intf,
                 mailbox#(transaction) gen2drv_mbox);
        this.tr_intf = tr_intf;
        this.gen2drv_mbox = gen2drv_mbox;
        count = 0;
    endfunction  //new()

    task reset();
        tr_intf.tx_data <= 0;
        tr_intf.reset   <= 1'b1;
        tr_intf.start   <= 1'b1;
        repeat (5) @(posedge tr_intf.clk);
        tr_intf.reset <= 1'b0;
    endtask

    task run();
        forever begin
            gen2drv_mbox.get(trans);
            if (count % 10 == 0) begin
                tr_intf.start = 1'b1;
                data = trans.tx_data;
                tr_intf.tx_data = data;

                @(posedge tr_intf.clk);

                tr_intf.start = 1'b0;
                // $display("IDLE");
            end else if (count % 10 == 9) begin
                //  $display("STOP");
            end else begin
                tr_intf.tx_data = data;
            end
            count++;
            trans.display("DRV");

            //->mon_Next_Event;

        end
    endtask
endclass  //driver

class monitor;
    virtual tr_interface tr_intf;
    mailbox #(transaction) mon2scb_mbox;
    transaction trans;
    //event mon_Next_Event;

    function new(virtual tr_interface tr_intf,
                 mailbox#(transaction) mon2scb_mbox);
        this.tr_intf = tr_intf;
        this.mon2scb_mbox = mon2scb_mbox;
    endfunction  //new()

    task run();
        forever begin
            //@(mon_Next_Event);
            @(posedge tr_intf.clk);  //drvier랑 timing 맞춤
            trans = new();
            trans.tx_data = tr_intf.tx_data;

            repeat (32) @(posedge tr_intf.clk);
            trans.tx = tr_intf.tx;
            repeat (32) @(posedge tr_intf.clk);
            mon2scb_mbox.put(trans);
            trans.display("MON");
        end
    endtask
endclass  //monitor

class scoreboard;
    mailbox #(transaction) mon2scb_mbox;
    transaction trans;
    event gen_next_event;

    reg [7:0] tx_reg;

    int total_cnt, pass_cnt, fail_cnt;

    function new(mailbox#(transaction) mon2scb_mbox, event gen_next_event);
        this.mon2scb_mbox = mon2scb_mbox;
        this.gen_next_event = gen_next_event;
        total_cnt = 0;
        pass_cnt = 0;
        fail_cnt = 0;
        tx_reg = 0;
    endfunction  //new()

    task run();
        forever begin
            mon2scb_mbox.get(trans);

            tx_reg = {trans.tx, tx_reg[7:1]};

            trans.display("SCB");
            if (total_cnt % 10 == 8) begin

                if (trans.tx_data == tx_reg) begin
                    $display(" --> TRANSMIT PASS! %b == %b", trans.tx_data,
                             tx_reg);
                    pass_cnt++;
                end else begin
                    $display(" --> TRANSMIT FAIL! %b == %b", trans.tx_data,
                             tx_reg);
                    fail_cnt++;
                end
            end
            total_cnt++;
            ->gen_next_event;
        end
    endtask
endclass  //scoreboard

class environment;
    generator              gen;
    driver                 drv;
    monitor                mon;
    scoreboard             scb;

    event                  gen_next_event;

    mailbox #(transaction) gen2drv_mbox;
    mailbox #(transaction) mon2scb_mbox;

    function new(virtual tr_interface tr_intf);
        gen2drv_mbox = new();
        mon2scb_mbox = new();

        gen = new(gen2drv_mbox, gen_next_event);
        drv = new(tr_intf, gen2drv_mbox);
        mon = new(tr_intf, mon2scb_mbox);
        scb = new(mon2scb_mbox, gen_next_event);
    endfunction

    task report();
        $display("================================");
        $display("==        Final Report        ==");
        $display("================================");
        $display("Total Test : %d", scb.total_cnt);
        $display("Pass Count : %d", scb.pass_cnt);
        $display("Fail Count : %d", scb.fail_cnt);
        $display("================================");
        $display("==   test bench is finished!  ==");
        $display("================================");
    endtask

    task pre_run();
        drv.reset();
    endtask

    task run();
        fork
            gen.run(1000);
            drv.run();
            mon.run();
            scb.run();
        join_any
        report();
        #10 $finish;
    endtask

    task run_test();
        pre_run();
        run();
    endtask
endclass

module tb_transmitter ();
    environment env;
    tr_interface tr_intf ();

    transmitter dut (
        .clk(tr_intf.clk),
        .reset(tr_intf.reset),
        .start(tr_intf.start),
        .tx_data(tr_intf.tx_data),
        .tx(tr_intf.tx),
        .tx_done(tr_intf.tx_done)
    );

    always #5 tr_intf.clk = ~tr_intf.clk;  //5nsec 간격으로 toggle

    initial begin
        tr_intf.clk = 0;
    end

    initial begin
        env = new(tr_intf);
        env.run_test();
    end
endmodule

 

simulation

 

...

 

uart_rx

`timescale 1ns / 1ps

module uart_rx (  // PC
    input        clk,
    input        reset,
    input        rx,
    output [7:0] rx_data,
    output       rx_done
);
    wire w_br_tick;

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

    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 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

 

schematic

 

tb_uart_rx.sv

`timescale 1ns / 1ps

interface rx_interface;
    logic       clk;
    logic       reset;
    logic       rx;
    logic [7:0] rx_data;
    logic       rx_done;
endinterface

class transaction;
    rand logic       rx;
    logic      [7:0] rx_data;
    logic            rx_done;

    task display(string name);
        $display("[%s] rx_data: %b, rx: %b, rx_done: %b", name, rx_data, rx,
                 rx_done);
    endtask
endclass

class generator;
    transaction trans;
    mailbox #(transaction) gen2drv_mbox;
    event gen_next_event;

    function new(mailbox#(transaction) gen2drv_mbox, event gen_next_event);
        this.gen2drv_mbox   = gen2drv_mbox;
        this.gen_next_event = gen_next_event;
    endfunction  //new()

    task run(int count);
        repeat (count) begin
            trans = new();
            assert (trans.randomize())
            else $error("[GEN] trans.randomize() error!");
            gen2drv_mbox.put(trans);
            trans.display("GEN");
            @(gen_next_event);
        end
    endtask
endclass  //generator

class driver;
    transaction trans;
    mailbox #(transaction) gen2drv_mbox;
    virtual rx_interface rx_intf;

    integer count;

    function new(virtual rx_interface rx_intf,
                 mailbox#(transaction) gen2drv_mbox);
        this.rx_intf = rx_intf;
        this.gen2drv_mbox = gen2drv_mbox;
        count = 0;
    endfunction  //new()

    task reset();
        rx_intf.rx <= 1'b1;
        rx_intf.reset <= 1'b1;
        repeat (5) @(rx_intf.clk);
        rx_intf.reset <= 1'b0;
    endtask

    task run();
    forever begin
        gen2drv_mbox.get(trans);  // Get the transaction from the mailbox

        if (count % 10 == 0) begin
            //rx_intf.rx <= 1'b1;
            //@(posedge rx_intf.clk);
            rx_intf.rx <= 1'b0;
            $display("START");
        end else if (count % 10 == 9) begin
            rx_intf.rx <= 1'b1;  // Stop bit
            $display("STOP");
        end else begin
            rx_intf.rx = trans.rx;  // Data bits from transaction
        end
        count++;
        repeat (64) @(posedge rx_intf.clk);
        trans.display("DRV");  // Display the current transaction details
    end

endtask

endclass  //driver

class monitor;
    virtual rx_interface rx_intf;
    mailbox #(transaction) mon2scb_mbox;
    transaction trans;

    function new(virtual rx_interface rx_intf,
                 mailbox#(transaction) mon2scb_mbox);
        this.rx_intf = rx_intf;
        this.mon2scb_mbox = mon2scb_mbox;
    endfunction  //new()

    task run();
        forever begin
            trans = new();
            //@(posedge rx_intf.clk);
            trans.rx = rx_intf.rx;
            repeat (32) @(posedge rx_intf.clk);
            trans.rx_data = rx_intf.rx_data;
            repeat (32) @(posedge rx_intf.clk);
            trans.rx_done = rx_intf.rx_done;
            mon2scb_mbox.put(trans);
            trans.display("MON");
        end
    endtask
endclass  //monitor

class scoreboard;
    mailbox #(transaction) mon2scb_mbox;
    transaction trans;
    event gen_next_event;

    logic [7:0] rx_reg;

    int total_cnt, pass_cnt, fail_cnt;

    function new(mailbox#(transaction) mon2scb_mbox, event gen_next_event);
        this.mon2scb_mbox = mon2scb_mbox;
        this.gen_next_event = gen_next_event;
        total_cnt = 0;
        pass_cnt = 0;
        fail_cnt = 0;
        rx_reg = 0;
    endfunction  //new()

    task run();
        forever begin
            mon2scb_mbox.get(trans);

            rx_reg = {trans.rx, rx_reg[7:1]};

            trans.display("SCB");
            if (total_cnt % 10 == 8) begin
                if (trans.rx_data == rx_reg) begin
                    $display(" --> PASS! %b == %b", trans.rx_data, rx_reg);
                    pass_cnt++;
                end else begin
                    $display(" --> FAIL! %b == %b", trans.rx_data, rx_reg);
                    fail_cnt++;
                end
            end
            total_cnt++;
            ->gen_next_event;
        end

    endtask

endclass  //scoreboard

class environment;
    generator              gen;
    driver                 drv;
    monitor                mon;
    scoreboard             scb;

    event                  gen_next_event;

    mailbox #(transaction) gen2drv_mbox;
    mailbox #(transaction) mon2scb_mbox;

    function new(virtual rx_interface rx_intf);
        gen2drv_mbox = new();
        mon2scb_mbox = new();

        gen = new(gen2drv_mbox, gen_next_event);
        drv = new(rx_intf, gen2drv_mbox);
        mon = new(rx_intf, mon2scb_mbox);
        scb = new(mon2scb_mbox, gen_next_event);
    endfunction

    task report();
        $display("================================");
        $display("==        Final Report        ==");
        $display("================================");
        $display("Total Test : %d", scb.total_cnt);
        $display("Pass Count : %d", scb.pass_cnt);
        $display("Fail Count : %d", scb.fail_cnt);
        $display("================================");
        $display("==   test bench is finished!  ==");
        $display("================================");
    endtask

    task pre_run();
        drv.reset();
    endtask

    task run();
        fork
            gen.run(1000);
            drv.run();
            mon.run();
            scb.run();
        join_any
        report();
        #10 $finish;
    endtask

    task run_test();
        pre_run();
        run();
    endtask
endclass

module tb_receiver ();
    environment env;
    rx_interface rx_intf ();

    uart_rx dut (
        .clk(rx_intf.clk),
        .reset(rx_intf.reset),
        .rx_data(rx_intf.rx_data),
        .rx(rx_intf.rx),
        .rx_done(rx_intf.rx_done)
    );

    always #5 rx_intf.clk = ~rx_intf.clk;

    initial begin
        rx_intf.clk = 0;
    end

    initial begin
        env = new(rx_intf);
        env.run_test();
    end
endmodule

 

simulation

...