Wednesday, August 6, 2014

an example of systemVerilog TB with 2 TB environment

//rtl/dut.v
`timescale 1ns/1ps

`define PKT_RECV_IDLE   0
`define PKT_RECV_START  1
`define PKT_RECV_RECV   2
`define PKT_RECV_VALID  3
`define PKT_RECV_END    4

`define PKT_SENT_IDLE   0
`define PKT_SENT_VALID  1
`define PKT_SENT_END    2
`define PKT_SENT_FIN    3

module dut(
    clk    ,
    rst_n  ,

    addr   ,
    din    ,
    rw     ,
    dout   ,

    txd    ,
    tx_vld ,
    rxd    ,
    rx_vld
);

input         clk    ;
input         rst_n  ;

input  [7:0 ] addr  ;
input  [31:0] din   ;
input         rw    ;
output [31:0] dout  ;

input  [7:0 ] rxd    ;
input         rx_vld ;
output [7:0 ] txd    ;
output        tx_vld ;

wire        clk   ;
wire        rst_n ;

wire [7:0 ] addr  ;
wire [31:0] din   ;
wire        rw    ;
reg  [31:0] dout  ;

wire [7:0 ] rxd    ;
wire        rx_vld ;
reg  [7:0 ] txd    ;
reg         tx_vld ;

wire        write ;
wire        read  ;

wire [31:0] config_reg ;
reg         pkt_en;
reg  [31:0] min_pkt_size ;
reg  [31:0] max_pkt_size ;

reg  [7:0 ] mem[0:512];
reg  [9:0 ] mem_wr_ptr;
reg  [9:0 ] mem_rd_ptr;
reg  [9:0 ] need_to_sent_pkt_size;
reg  [9:0 ] sending_pkt_size;

reg  [2:0 ] pkt_recv_status;
reg  [1:0 ] pkt_sent_status;

reg         new_pkt;
reg         one_pkt_sent;

reg  [1:0 ] pkt_no;

assign write = (rw == 1'b0);
assign read  = (rw == 1'b1);

assign config_reg = {31'h0, pkt_en};

always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0) begin
        pkt_en <= 1'b0;
    end
    else begin
        if((addr == 8'h0) && (write == 1'b1)) begin
            pkt_en <= din[0];
        end
    end
end

always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0) begin
        min_pkt_size <= 32'd64;
    end
    else begin
        if((addr == 8'h4) && (write == 1'b1)) begin
            if((din[9:0] >= 64) && (din[9:0] < max_pkt_size[9:0]))
                min_pkt_size[9:0] <= din[9:0];
        end
    end
end

always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0) begin
        max_pkt_size <= 32'd512;
    end
    else begin
        if((addr == 8'h8) && (write == 1'b1)) begin
            if((din[9:0] <= 512) && (din[9:0] > min_pkt_size[9:0]))
                max_pkt_size[9:0] <= din[9:0];
        end
    end
end

always @(*) begin
    case(addr)
        8'h0    : dout = config_reg;
        8'h4    : dout = min_pkt_size;
        8'h8    : dout = max_pkt_size;
        default : dout = 32'h0;
    endcase
end

always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0) begin
        pkt_recv_status <= 'h0;
        pkt_recv_status <= `PKT_RECV_IDLE;
        mem_wr_ptr <= 8'h0;
        new_pkt    <= 1'b0;
    end
    else begin
        case(pkt_recv_status)
            `PKT_RECV_IDLE: begin
                mem_wr_ptr <= 8'h0;
                if((rx_vld == 1'b1) && (pkt_en == 1'b1)) begin
                    mem_wr_ptr <= mem_wr_ptr + 1;
                    pkt_recv_status <= `PKT_RECV_START;
                    mem[mem_wr_ptr] <= rxd;
                end
            end
            `PKT_RECV_START: begin
                if(rx_vld == 1'b1) begin
                    mem_wr_ptr <= mem_wr_ptr + 1;
                    pkt_recv_status <= `PKT_RECV_RECV;
                    mem[mem_wr_ptr] <= rxd;
                end
                else begin
                    pkt_recv_status <= `PKT_RECV_END;
                end
            end
            `PKT_RECV_RECV: begin
                if(rx_vld == 1'b1) begin
                    if(mem_wr_ptr == max_pkt_size) begin
                        pkt_recv_status <= `PKT_RECV_END;
                    end
                    else begin
                        mem_wr_ptr <= mem_wr_ptr + 1;
                        pkt_recv_status <= `PKT_RECV_RECV;
                        mem[mem_wr_ptr] <= rxd;
                    end
                end
                else begin
                    pkt_recv_status <= `PKT_RECV_VALID;
                end
            end
            `PKT_RECV_VALID: begin
                if(mem_wr_ptr >= min_pkt_size) begin
                    new_pkt <= 1'b1;
                    need_to_sent_pkt_size <= mem_wr_ptr;
                end
                pkt_recv_status <= `PKT_RECV_END;
            end
            `PKT_RECV_END: begin
                new_pkt <= 1'b0;
                if(rx_vld == 1'b0) begin
                    pkt_recv_status <= `PKT_RECV_IDLE;
                end
            end
            default : begin
            end
        endcase
    end
end

always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0) begin
        pkt_sent_status <= `PKT_SENT_IDLE;
        pkt_sent_status <= 'h0;
        mem_rd_ptr <= 'h0;
        sending_pkt_size <= 'h0;
        txd <= 'h0;
        tx_vld <= 1'b0;
        one_pkt_sent <= 1'b0;
    end
    else begin
        case(pkt_sent_status)
            `PKT_SENT_IDLE: begin
                if(pkt_no > 0) begin
                    pkt_sent_status <= `PKT_SENT_VALID;
                    sending_pkt_size <= need_to_sent_pkt_size;
                    mem_rd_ptr <= 'h0;
                end
            end
            `PKT_SENT_VALID: begin
                if(mem_rd_ptr < sending_pkt_size) begin
                    mem_rd_ptr <= mem_rd_ptr + 1;
                    txd <= mem[mem_rd_ptr];
                    tx_vld <= 1'b1;
                end
                else begin
                    pkt_sent_status <= `PKT_SENT_END;
                    txd <= 'h0;
                    tx_vld <= 1'b0;
                end
            end
            `PKT_SENT_END: begin
                pkt_sent_status <= `PKT_SENT_FIN;
                one_pkt_sent <= 1'b1;
            end
            `PKT_SENT_FIN: begin
                pkt_sent_status <= `PKT_SENT_IDLE;
                one_pkt_sent <= 1'b0;
            end
            default: begin
            end
        endcase
    end
end

always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0) begin
        pkt_no <= 'h0;
    end
    else begin
        if(new_pkt == 1'b1) begin
            pkt_no <= pkt_no + 1;
        end
        else if(one_pkt_sent == 1'b1) begin
            pkt_no <= pkt_no - 1;
        end
    end
end

endmodule

//env/cpu_interface_env/cpu_interface.svi
`ifndef __CPU_INTERFACE_SVI__
`define __CPU_INTERFACE_SVI__
interface cpu_interface (input logic clk);

logic        rst_n ;
logic [7:0 ] addr  ;
logic [31:0] din   ;
logic        rw    ;
logic [31:0] dout  ;

clocking cb @(posedge clk);
    default input #1 output #1;
    output addr ;
    output rw   ;
    output dout ;
    input  din  ;
endclocking

modport master(clocking cb, output rst_n);

endinterface
`endif

//env/cpu_interface_env/cpu_transaction.sv
`ifndef __CPU_TRANSACTION_SV__
`define __CPU_TRANSACTION_SV__

class cpu_transaction;

string name;
rand logic [7:0] addr;
rand logic rw;
rand logic [31:0] dout;
logic [31:0] din = 32'h0;

extern function new(string name = "CPU Transaction");
extern function cpu_transaction copy();
extern function void display(string prefix = "Note");

endclass

function cpu_transaction::new(string name);
    this.name = name;
endfunction

function cpu_transaction cpu_transaction::copy();
    cpu_transaction cpu_tran_cpy = new();

    cpu_tran_cpy.addr = addr;
    cpu_tran_cpy.rw   = rw;
    cpu_tran_cpy.dout = dout;
    cpu_tran_cpy.din  = din;

    return cpu_tran_cpy;
endfunction

function void cpu_transaction::display(string prefix);
  $display("[%s]%t %s addr = %0h, dout = %0h, rw = %d, din = %0h", prefix, $realtime, name, addr, dout, rw, din);
endfunction

`endif

//env/cpu_interface_env/cpu_define.sv
`ifndef __CPU_DEFINE_SV__
`define __CPU_DEFINE_SV__

typedef class cpu_transaction;
typedef mailbox #(cpu_transaction) cpu_tran_mbox;

`endif

//env/cpu_interface_env/cpu_generator.sv
`ifndef __CPU_GENERATOR_SV__
`define __CPU_GENERATOR_SV__

`include "cpu_transaction.sv"
`include "cpu_define.sv"

class cpu_generator;

string name;
cpu_tran_mbox out_box;
int run_for_n_trans = 0;
cpu_transaction cpu_tran;
event done;

extern function new(string name = "CPU Generator", cpu_tran_mbox out_box);
extern task start();

endclass

function cpu_generator::new(string name, cpu_tran_mbox out_box);
    this.name = name;
    this.out_box = out_box;
endfunction

task cpu_generator::start();
    fork
        begin
            for(int i=0; i<run_for_n_trans; i++) begin
                cpu_transaction cpu_tran_cpy = new cpu_tran;
                if(!cpu_tran_cpy.randomize()) begin
                    $display("Error to randomize cpu_tran_cpy");
                end
                out_box.put(cpu_tran_cpy);
            end
            ->done;
        end
    join_none
endtask

`endif

//env/cpu_interface_env/cpu_driver.sv
`ifndef __CPU_DRIVER_SV__
`define __CPU_DRIVER_SV__

`include "cpu_transaction.sv"
`include "cpu_define.sv"
`include "cpu_interface.svi"

class cpu_driver;

string name;
cpu_tran_mbox in_box;
cpu_tran_mbox out_box;
virtual cpu_interface.master cpu_intf;

extern function new(string name = "CPU Driver", cpu_tran_mbox in_box, cpu_tran_mbox out_box, virtual cpu_interface.master cpu_intf);
extern task start();

endclass

function cpu_driver::new(string name, cpu_tran_mbox in_box, cpu_tran_mbox out_box, virtual cpu_interface.master cpu_intf);
    this.name = name;
    this.in_box = in_box;
    this.out_box = out_box;
    this.cpu_intf = cpu_intf;
endfunction

task cpu_driver::start();
    fork
        forever begin
            cpu_transaction cpu_tran;
            in_box.get(cpu_tran);
            cpu_tran.display();

            @(cpu_intf.cb);
            cpu_intf.cb.addr <= cpu_tran.addr;
            cpu_intf.cb.dout <= cpu_tran.dout;
            cpu_intf.cb.rw   <= cpu_tran.rw;
            if(cpu_tran.rw == 1'b1) begin
                @(cpu_intf.cb);
                cpu_tran.din  = cpu_intf.cb.din;
            end

            out_box.put(cpu_tran);
        end
    join_none
endtask

`endif

//env/cpu_interface_env/cpu_scoreboard.sv
`ifndef __CPU_SCOREBOARD_SV__
`define __CPU_SCOREBOARD_SV__

`include "cpu_transaction.sv"
`include "cpu_define.sv"

class cpu_scoreboard;

string name;
cpu_tran_mbox in_box;
cpu_transaction cpu_tran;

logic [31:0] cpu_reg [7:0];
integer error_no = 0;
integer total_no = 0;

extern function new(string name = "CPU Scoreboard", cpu_tran_mbox in_box);
extern task start();
extern task check();
extern function display(string prefix = "CPU Scoreboard Result");

endclass

function cpu_scoreboard::new(string name, cpu_tran_mbox in_box);
    this.name = name;
    this.in_box = in_box;

    for(int i=0; i<128; i++) begin
        cpu_reg[i] = 32'h0;
    end
endfunction

task cpu_scoreboard::start();
    fork
        forever begin
            in_box.get(cpu_tran);
            total_no++;
            check();
        end
    join_none
endtask

task cpu_scoreboard::check();
    string message;
    if(cpu_tran.rw == 1'b0) begin
        cpu_reg[cpu_tran.addr] = cpu_tran.din;
    end
    else if(cpu_tran.rw == 1'b1) begin
        if(cpu_reg[cpu_tran.addr] != cpu_tran.din) begin
            message = $psprintf("[Error] %t Comparision result is not correct\n", $realtime);
            message = { message, $psprintf("cpu_reg[%d] = %0h, cpu_tran.din = %0h\n", cpu_tran.addr, cpu_reg[cpu_tran.addr], cpu_tran.din) };
            $display(message);
            error_no++;
        end
        else begin
            $display("[Note] %t comparison correct", $realtime);
        end
    end
    else begin
        $display("[Error] cpu_tran.rw can only be 0 or 1");
        error_no++;
    end
endtask

function cpu_scoreboard::display(string prefix);
    $display("Total: %d, Error: %d", total_no, error_no);
endfunction

`endif

//env/cpu_interface_env/cpu_env.sv
`ifndef __CPU_ENV_SV__
`define __CPU_ENV_SV__

`include "cpu_interface.svi"
`include "cpu_transaction.sv"
`include "cpu_generator.sv"
`include "cpu_define.sv"
`include "cpu_driver.sv"
`include "cpu_scoreboard.sv"

class cpu_env;

cpu_tran_mbox  drv_mbox = new(1);
cpu_tran_mbox  mon_mbox = new();
cpu_generator  cpu_gen;
cpu_driver     cpu_drv;
cpu_scoreboard cpu_sb;

virtual cpu_interface.master cpu_intf;

extern function new(virtual cpu_interface.master cpu_intf);
extern function configure();
extern task reset();
extern task start();

endclass

function cpu_env::new(virtual cpu_interface.master cpu_intf);
    this.cpu_intf = cpu_intf;
    cpu_gen = new("CPU Generator", drv_mbox);
    cpu_drv = new("CPU Driver", drv_mbox, mon_mbox, cpu_intf);
    cpu_sb  = new("CPU Scoreboard", mon_mbox);
endfunction

function cpu_env::configure();
endfunction

task cpu_env::reset();
    cpu_intf.cb.addr <= 8'h0;
    cpu_intf.cb.rw   <= 1'b1;
    cpu_intf.cb.dout <= 31'h0;
    @(cpu_intf.cb);
    cpu_intf.rst_n = 1'b0;
    @(cpu_intf.cb);
    cpu_intf.rst_n = 1'b1;
    @(cpu_intf.cb);
endtask

task cpu_env::start();
    cpu_gen.start();
    cpu_drv.start();
    cpu_sb.start();
endtask

`endif

//env/packet_env/packet_interface.svi
`ifndef __PACKET_INTERFACE_SVI__
`define __PACKET_INTERFACE_SVI__
interface packet_interface (input logic clk);

logic       rst_n  ;
logic [7:0] rxd    ;
logic       rx_vld ;
logic [7:0] txd    ;
logic       tx_vld ;

clocking cb @(posedge clk);
    default input #1 output #1;
    output rst_n  ;
    output txd    ;
    output tx_vld ;
    input  rxd    ;
    input  rx_vld ;
endclocking

modport master(clocking cb, output rst_n);

endinterface
`endif

//env/packet_env/packet_transaction.sv
`ifndef __PACKET_TRANSACTION_SV__
`define __PACKET_TRANSACTION_SV__

class packet_transaction;

string name;
typedef enum {GOOD, BAD} head_type;
typedef enum {LONG, SHORT, NORMAL} packet_length;
rand head_type     pkt_head_type     ;
rand packet_length pkt_packet_length ;
rand logic [15:0] header     ;
rand logic [7:0 ] payload[$] ;
rand logic [6:0 ] frame_interval;

extern function new(string name = "Packet Transaction");
extern function void display(string prefix = "Note");
extern function bit compare(packet_transaction pkt2cmp, ref string message);
extern function void byte2pkt(ref logic[7:0] data[$]);
extern function packet_transaction copy();

constraint con_head_type {
    solve pkt_head_type before header;
    pkt_head_type dist {GOOD := 5, BAD := 1};
    (pkt_head_type == GOOD) -> (header == 16'h55d5);
    (pkt_head_type == BAD) -> (header inside {[0:16'h55d4], [16'h55d6:16'hffff]});
}

constraint con_pkt_len {
    solve pkt_packet_length before payload;
    pkt_packet_length dist {LONG := 1, SHORT := 1, NORMAL := 5};
    (pkt_packet_length == LONG) -> (payload.size() inside {[0:49]});
    (pkt_packet_length == SHORT) -> (payload.size() inside {[50:500]});
    (pkt_packet_length == NORMAL) -> (payload.size() inside {[501:600]});
}

constraint con_frame_interval {
    frame_interval inside {[96:200]};
}

endclass

function packet_transaction::new(string name);
    this.name = name;
endfunction

function bit packet_transaction::compare(packet_transaction pkt2cmp, ref string message);
    if(header != pkt2cmp.header) begin
        message = "Header Mismatch:\n";
        message = { message, $psprintf("Header Sent:  %p\nHeader Received: %p", header, pkt2cmp.header) };
        return(0);
    end

    if (payload.size() != pkt2cmp.payload.size()) begin
        message = "Payload Size Mismatch:\n";
        message = { message, $psprintf("payload.size() = %0d, pkt2cmp.payload.size() = %0d\n", payload.size(), pkt2cmp.payload.size()) };
        return(0);
    end

    if (payload == pkt2cmp.payload) ;
    else begin
        message = "Payload Content Mismatch:\n";
        message = { message, $psprintf("Packet Sent:  %p\nPkt Received: %p", payload, pkt2cmp.payload) };
        return(0);
    end

    message = "Successfully Compared";
    return(1);
endfunction

function void packet_transaction::display(string prefix);
    $display("[%s]%t %s", prefix, $realtime, name);
    $display("    [%s]%t %s frame_interval = %0d", prefix, $realtime, name, frame_interval);
    $display("    [%s]%t %s header = %0h", prefix, $realtime, name, header);
    foreach(payload[i])
        $display("    [%s]%t %s payload[%0d] = %0d", prefix, $realtime, name, i, payload[i]);
endfunction

function void packet_transaction::byte2pkt(ref logic[7:0] data[$]);
    if(data.size() >= 2) begin
        header[15:8] = data.pop_front();
        header[7:0]  = data.pop_front();
    end

    foreach(data[i]) begin
        payload.push_back(data[i]);
    end

endfunction

function packet_transaction packet_transaction::copy();
    packet_transaction pkt_tran = new();
    pkt_tran.frame_interval = this.frame_interval;
    pkt_tran.header = this.header;
    foreach(this.payload[i]) begin
        pkt_tran.payload.push_back(this.payload[i]);
    end

    return pkt_tran;
endfunction

`endif

//env/packet_env/pcaket_define.sv
`ifndef __PACKET_DEFINE_SV__
`define __PACKET_DEFINE_SV__

typedef class packet_transaction;
typedef mailbox #(packet_transaction) packet_tran_mbox;

`endif

//env/packet_env/packet_generator.sv
`ifndef __PACKET_GENERATOR_SV__
`define __PACKET_GENERATOR_SV__

`include "packet_transaction.sv"
`include "packet_define.sv"

class packet_generator;

string name;
packet_tran_mbox drv_mbox;
int run_for_n_trans = 0;
packet_transaction pkt_tran = new();
event done;

extern function new(string name = "CPU Generator", packet_tran_mbox drv_mbox);
extern task start();

endclass

function packet_generator::new(string name, packet_tran_mbox drv_mbox);
    this.name = name;
    this.drv_mbox = drv_mbox;
endfunction

task packet_generator::start();
    fork
        begin
            for(int i=0; i<run_for_n_trans; i++) begin
                packet_transaction pkt_tran_cpy = new pkt_tran;
                if(!pkt_tran_cpy.randomize()) begin
                    $display("Error to randomize pkt_tran_cpy");
                end
                drv_mbox.put(pkt_tran_cpy);
            end
            ->done;
        end
    join_none
endtask

`endif

//env/packet_env/packet_driver.sv
`ifndef __PACKET_DRIVER_SV__
`define __PACKET_DRIVER_SV__

`include "packet_transaction.sv"
`include "packet_define.sv"
`include "packet_interface.svi"

class packet_driver;

string name;
packet_tran_mbox gen2drv_mbox;
packet_tran_mbox drv2sb_mbox;
virtual packet_interface.master pkt_intf;

extern function new(string name = "CPU Driver", packet_tran_mbox gen2drv_mbox, packet_tran_mbox drv2sb_mbox, virtual packet_interface.master pkt_intf);
extern task start();

endclass

function packet_driver::new(string name, packet_tran_mbox gen2drv_mbox, packet_tran_mbox drv2sb_mbox, virtual packet_interface.master pkt_intf);
    this.name = name;
    this.gen2drv_mbox = gen2drv_mbox;
    this.drv2sb_mbox = drv2sb_mbox;
    this.pkt_intf = pkt_intf;
endfunction

task packet_driver::start();
    integer delay = 0;
    fork
        forever begin
            packet_transaction pkt_tran;
            gen2drv_mbox.get(pkt_tran);
            //pkt_tran.display();
            drv2sb_mbox.put(pkt_tran.copy());

            @(pkt_intf.cb);
            pkt_intf.cb.tx_vld <= 1'b1;
            pkt_intf.cb.txd    <= pkt_tran.header[15:8];
            @(pkt_intf.cb);
            pkt_intf.cb.txd    <= pkt_tran.header[7:0];

            foreach(pkt_tran.payload[i]) begin
                @(pkt_intf.cb);
                pkt_intf.cb.txd    <= pkt_tran.payload[i];
            end

            if(pkt_tran.frame_interval > pkt_tran.payload.size())
                delay = pkt_tran.frame_interval;
            else
                delay = pkt_tran.payload.size();
            repeat(delay) begin
                @(pkt_intf.cb);
                pkt_intf.cb.tx_vld <= 1'b0;
            end

        end
    join_none
endtask

`endif

//env/packet_env/packet_monitor.sv
`ifndef __PACKET_MONITOR_SV__
`define __PACKET_MONITOR_SV__

`include "packet_transaction.sv"
`include "packet_define.sv"
`include "packet_interface.svi"

class packet_monitor;

string name;
packet_tran_mbox out_box;
virtual packet_interface.master pkt_intf;

extern function new(string name = "CPU monitor", packet_tran_mbox out_box, virtual packet_interface.master pkt_intf);
extern task start();

endclass

function packet_monitor::new(string name, packet_tran_mbox out_box, virtual packet_interface.master pkt_intf);
    this.name = name;
    this.out_box = out_box;
    this.pkt_intf = pkt_intf;
endfunction

task packet_monitor::start();
    fork
        forever begin
            logic [7:0] rxd[$];
            packet_transaction pkt_tran = new();

            @(pkt_intf.cb);
            while(pkt_intf.cb.rx_vld == 1'b0) begin
                @(pkt_intf.cb);
            end

            while(pkt_intf.cb.rx_vld == 1'b1) begin
                rxd.push_back(pkt_intf.cb.rxd);
                @(pkt_intf.cb);
            end

            pkt_tran.byte2pkt(rxd);
            //pkt_tran.display("Monitor");

            out_box.put(pkt_tran);
        end
    join_none
endtask

`endif

//env/packet_env/packet_scoreboard.sv
`ifndef __PACKET_SCOREBOARD_SV__
`define __PACKET_SCOREBOARD_SV__

`include "packet_transaction.sv"
`include "packet_define.sv"

class packet_scoreboard;

string name;
packet_tran_mbox drv2sb_mbox;
packet_tran_mbox mon2sb_mbox;
packet_transaction ref_pkt_tran[$];

integer error_no = 0;
integer send_no = 0;
integer receive_no = 0;

logic [9:0] min_pkt_size;
logic [9:0] max_pkt_size;

extern function new(string name = "CPU Scoreboard", packet_tran_mbox drv2sb_mbox, packet_tran_mbox mon2sb_mbox);
extern task start();
extern virtual task get_pkt_from_mon_and_check();
extern function display(string prefix = "Packet Scoreboard Result");

endclass

function packet_scoreboard::new(string name, packet_tran_mbox drv2sb_mbox, packet_tran_mbox mon2sb_mbox);
    this.name = name;
    this.drv2sb_mbox = drv2sb_mbox;
    this.mon2sb_mbox = mon2sb_mbox;
endfunction

task packet_scoreboard::start();
    fork
        get_pkt_from_mon_and_check();
    join_none
endtask

task packet_scoreboard::get_pkt_from_mon_and_check();
    string message;
    packet_transaction pkt_tran_mon;
    packet_transaction pkt_tran_drv;
    forever begin
        message = "Scoreboare Comparison:\n";

        mon2sb_mbox.get(pkt_tran_mon);
        receive_no++;

        do begin
            drv2sb_mbox.get(pkt_tran_drv);
            send_no++;
        end while(pkt_tran_drv.payload.size() < min_pkt_size || pkt_tran_drv.payload.size() > max_pkt_size);

        if(!pkt_tran_drv.compare(pkt_tran_mon, message)) begin
            error_no++;
        end
        message = {message, $psprintf(" Send: %3d, Receive: %3d, Error: %3d\n", send_no, receive_no, error_no)};
        $display(message);
    end
endtask

function packet_scoreboard::display(string prefix);
    $display("Send: %d, Receive: %d, Error: %d", send_no + drv2sb_mbox.num(), receive_no, error_no);
endfunction

`endif

//env/packet_env/packet_env.sv
`ifndef __PACKET_ENV_SV__
`define __PACKET_ENV_SV__

`include "packet_interface.svi"
`include "packet_transaction.sv"
`include "packet_generator.sv"
`include "packet_define.sv"
`include "packet_driver.sv"
`include "packet_scoreboard.sv"
`include "packet_monitor.sv"

class packet_env;

packet_tran_mbox  gen2drv_mbox = new(1);
packet_tran_mbox  drv2sb_mbox = new();
packet_tran_mbox  mon2sb_mbox = new();
packet_generator  pkt_gen;
packet_driver     pkt_drv;
packet_scoreboard pkt_sb;
packet_monitor    pkt_mon;

virtual packet_interface.master pkt_intf;

extern function new(virtual packet_interface.master pkt_intf);
extern function configure();
extern task reset();
extern task start();

endclass

function packet_env::new(virtual packet_interface.master pkt_intf);
    this.pkt_intf = pkt_intf;
    pkt_gen = new("Packet Generator", gen2drv_mbox);
    pkt_drv = new("Packet Driver", gen2drv_mbox, drv2sb_mbox, pkt_intf);
    pkt_mon = new("Packet Monitor", mon2sb_mbox, pkt_intf);
    pkt_sb  = new("Packet Scoreboard", drv2sb_mbox, mon2sb_mbox);
endfunction

function packet_env::configure();
endfunction

task packet_env::reset();
    @(pkt_intf.cb);
    pkt_intf.cb.txd <= 8'h0;
    pkt_intf.cb.tx_vld <= 1'b0;

    @(pkt_intf.cb);
    pkt_intf.rst_n = 1'b0;
    @(pkt_intf.cb);
    pkt_intf.rst_n = 1'b1;
    @(pkt_intf.cb);
endtask

task packet_env::start();
    pkt_gen.start();
    pkt_drv.start();
    pkt_mon.start();
    pkt_sb.start();
endtask

`endif

//env/env.sv
`ifndef __ENV_SV__
`define __ENV_SV__

`include "cpu_interface.svi"
`include "packet_interface.svi"
`include "cpu_env.sv"
`include "packet_env.sv"

class env;

cpu_env cpu_e;
packet_env pkt_e;

extern function new(virtual cpu_interface.master cpu_intf, virtual packet_interface.master pkt_intf);
extern function configure();
extern task reset();
extern task start();

endclass

function env::new(virtual cpu_interface.master cpu_intf, virtual packet_interface.master pkt_intf);
    cpu_e = new(cpu_intf);
    pkt_e = new(pkt_intf);
endfunction

function env::configure();
    cpu_e.configure();
    pkt_e.configure();
endfunction

task env::reset();
    cpu_e.reset();
    pkt_e.reset();
endtask

task env::start();
    cpu_e.start();
    pkt_e.start();
endtask

`endif

//test/test.sv
`ifndef __TEST_SV__
`define __TEST_SV__

`include "env.sv"

program test(cpu_interface.master cpu_intf, packet_interface.master pkt_intf);

class packet_transaction_ext extends packet_transaction;

//constraint con_pkt_len_test {
//    payload.size() == 100;
//    pkt_head_type == GOOD;
//}

endclass

cpu_transaction cpu_tran;
cpu_transaction cpu_tran_cpy;
packet_transaction_ext pkt_tran;
env e;

initial begin
    cpu_tran = new();
    pkt_tran = new();
    e = new(cpu_intf, pkt_intf);
    e.configure();

    e.pkt_e.pkt_gen.pkt_tran = pkt_tran;

    e.reset();
    e.start();

    cpu_tran.randomize() with {addr == 'h4; rw == 1'b0; dout == 'd65;};
    cpu_tran_cpy = cpu_tran.copy();
    e.cpu_e.drv_mbox.put(cpu_tran_cpy);

    cpu_tran.randomize() with {addr == 'h8; rw == 1'b0; dout == 'd500;};
    cpu_tran_cpy = cpu_tran.copy();
    e.cpu_e.drv_mbox.put(cpu_tran_cpy);

    cpu_tran.randomize() with {addr == 'h0; rw == 1'b0; dout == 'h1;};
    cpu_tran_cpy = cpu_tran.copy();
    e.cpu_e.drv_mbox.put(cpu_tran_cpy);

    repeat(100) begin
        @(cpu_intf.cb);
    end

    e.pkt_e.pkt_sb.min_pkt_size = 65;
    e.pkt_e.pkt_sb.max_pkt_size = 500;
    e.pkt_e.pkt_gen.run_for_n_trans = 100;
    e.pkt_e.pkt_gen.start();

    @e.pkt_e.pkt_gen.done;
    repeat(10000) begin
        @(pkt_intf.cb);
        if(e.pkt_e.pkt_sb.send_no == e.pkt_e.pkt_gen.run_for_n_trans) begin
            break;
        end
    end
    e.pkt_e.pkt_sb.display();
    $finish();
end

endprogram
`endif

//env/top.sv
`timescale 1ns/1ps

`include "cpu_interface.svi"
`include "packet_interface.svi"
`include "test.sv"

module top;
parameter clock_cycle = 10;

logic clk;

cpu_interface cpu_intf(clk);
packet_interface pkt_intf(clk);

test u_test(cpu_intf, pkt_intf);

dut u_dut (
    .clk    (pkt_intf.clk    ) ,
    .rst_n  (pkt_intf.rst_n  ) ,

    .addr   (cpu_intf.addr   ) ,
    .rw     (cpu_intf.rw     ) ,
    .din    (cpu_intf.dout   ) ,
    .dout   (cpu_intf.din    ) ,

    .txd    (pkt_intf.rxd    ) ,
    .tx_vld (pkt_intf.rx_vld ) ,
    .rxd    (pkt_intf.txd    ) ,
    .rx_vld (pkt_intf.tx_vld )
);

initial begin
    $timeformat(-9, 1, "ns", 10);
    clk = 0;
    forever begin
        #(clock_cycle/2) clk = ~clk;
    end
end

`ifdef WAVE_ON
initial begin
    $vcdpluson();
end
`endif

endmodule

#script/Makefile
ifeq ($(GUI), 1)
    GUI_ARG = -gui
endif

PROJECT_DIR = /mnt/svtb
RTL_DIR = $(PROJECT_DIR)/rtl
RTL = $(RTL_DIR)/dut.v

TB_DIR = $(PROJECT_DIR)/env
INCDIR = +incdir+$(TB_DIR)

CPU_INTF_TB_DIR = $(TB_DIR)/cpu_interface_env
INCDIR += +incdir+$(CPU_INTF_TB_DIR)

PKT_TB_DIR = $(TB_DIR)/packet_env
INCDIR += +incdir+$(PKT_TB_DIR)

SVTB = $(TB_DIR)/env.sv $(TB_DIR)/top.sv

TEST_DIR = $(PROJECT_DIR)/test
TEST_FILE = $(TEST_DIR)/test.sv
INCDIR += +incdir+$(TEST_DIR)

COMPILE_LOG_ARG = -l vcs.log

WAVE_ARG = +define+WAVE_ON=1

COMPILE_ARG = -sverilog -debug_all
COMPILE_ARG += $(INCDIR) $(COMPILE_LOG_ARG) $(WAVE_ARG)

RUN_LOG_ARG = -l simv.log

RUN_ARG  =
RUN_ARG += $(RUN_LOG_ARG) $(GUI_ARG)

SEED = 1

default: test

test: compile run

run:
./simv $(RUN_ARG) +ntb_random_seed=$(SEED)

compile:
vcs $(COMPILE_ARG) $(RTL) $(SVTB) $(TEST_FILE)

clean:
rm -rf simv simv.* *log

an example of systemVerilog TB

/////////////////////////////////////////////
//dut.v
/////////////////////////////////////////////
`timescale 1ns/1ps

module dut(
    clk    ,
    rst_n  ,
    txd    ,
    tx_vld ,
    rxd    ,
    rx_vld  
);

input        clk    ;
input        rst_n  ;
input  [7:0] rxd    ;
input        rx_vld ;

output [7:0] txd    ;
output       tx_vld ;

wire       clk    ;
wire       rst_n  ;
wire [7:0] rxd    ;
wire       rx_vld ;

reg  [7:0] txd    ;
reg        tx_vld ;

always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0) begin
        txd <= 8'h0;
        tx_vld <= 1'b0;
    end
    else begin
        txd <= rxd;
        tx_vld <= rx_vld;
    end
end

endmodule

/////////////////////////////////////////////
//packet_interface.svi
/////////////////////////////////////////////
`ifndef __PACKET_INTERFACE_SVI__
`define __PACKET_INTERFACE_SVI__
interface packet_interface (input logic clk);

logic       rst_n  ;
logic [7:0] rxd    ;
logic       rx_vld ;
logic [7:0] txd    ;
logic       tx_vld ;

clocking cb @(posedge clk);
    default input #1 output #1;
    output rst_n  ;
    output txd    ;
    output tx_vld ;
    input  rxd    ;
    input  rx_vld ;
endclocking

modport master(clocking cb, output rst_n);

endinterface
`endif

/////////////////////////////////////////////
//packet_transaction.sv
/////////////////////////////////////////////
`ifndef __PACKET_TRANSACTION_SV__
`define __PACKET_TRANSACTION_SV__

class packet_transaction;

string name;
typedef enum {GOOD, BAD} head_type;
typedef enum {LONG, SHORT, NORMAL} packet_length;
rand head_type     pkt_head_type     ;
rand packet_length pkt_packet_length ;
rand logic [15:0] header     ;
rand logic [7:0 ] payload[$] ;

extern function new(string name = "Packet Transaction");
extern function void display(string prefix = "Note");
extern function bit compare(packet_transaction pkt2cmp, ref string message);
extern function void byte2pkt(ref logic[7:0] data[$]);
extern function packet_transaction copy();

constraint con_head_type {
    solve pkt_head_type before header;
    pkt_head_type dist {GOOD := 5, BAD := 1};
    (pkt_head_type == GOOD) -> (header == 16'h55d5);
    (pkt_head_type == BAD) -> (header inside {[0:16'h55d4], [16'h55d6:16'hffff]});
}

constraint con_pkt_len {
    solve pkt_packet_length before payload;
    pkt_packet_length dist {LONG := 1, SHORT := 1, NORMAL := 5};
    (pkt_packet_length == LONG) -> (payload.size() inside {[0:49]});
    (pkt_packet_length == SHORT) -> (payload.size() inside {[50:500]});
    (pkt_packet_length == NORMAL) -> (payload.size() inside {[501:600]});
}

endclass

function packet_transaction::new(string name);
    this.name = name;
endfunction

function bit packet_transaction::compare(packet_transaction pkt2cmp, ref string message);
    if(header != pkt2cmp.header) begin
        message = "Header Mismatch:\n";
        message = { message, $psprintf("Header Sent:  %p\nHeader Received: %p", header, pkt2cmp.header) };
        return(0);
    end

    if (payload.size() != pkt2cmp.payload.size()) begin
        message = "Payload Size Mismatch:\n";
        message = { message, $psprintf("payload.size() = %0d, pkt2cmp.payload.size() = %0d\n", payload.size(), pkt2cmp.payload.size()) };
        return(0);
    end

    if (payload == pkt2cmp.payload) ;
    else begin
        message = "Payload Content Mismatch:\n";
        message = { message, $psprintf("Packet Sent:  %p\nPkt Received: %p", payload, pkt2cmp.payload) };
        return(0);
    end

    message = "Successfully Compared";
    return(1);
endfunction

function void packet_transaction::display(string prefix);
    $display("[%s]%t %s", prefix, $realtime, name);
    $display("    [%s]%t %s header = %0h", prefix, $realtime, name, header);
    foreach(payload[i])
        $display("    [%s]%t %s payload[%0d] = %0d", prefix, $realtime, name, i, payload[i]);
endfunction

function void packet_transaction::byte2pkt(ref logic[7:0] data[$]);
    if(data.size() >= 2) begin
        header[15:8] = data.pop_front();
        header[7:0]  = data.pop_front();
    end

    foreach(data[i]) begin
        payload.push_back(data[i]);
    end

endfunction

function packet_transaction packet_transaction::copy();
    packet_transaction pkt_tran = new();
    pkt_tran.header = this.header;
    foreach(this.payload[i]) begin
        pkt_tran.payload.push_back(this.payload[i]);
    end

    return pkt_tran;
endfunction

`endif

/////////////////////////////////////////////
//packet_define.sv
/////////////////////////////////////////////
`ifndef __PACKET_DEFINE_SV__
`define __PACKET_DEFINE_SV__

typedef class packet_transaction;
typedef mailbox #(packet_transaction) packet_tran_mbox;

`endif

/////////////////////////////////////////////
//packet_generator.sv
/////////////////////////////////////////////
`ifndef __PACKET_GENERATOR_SV__
`define __PACKET_GENERATOR_SV__

`include "packet_transaction.sv"
`include "packet_define.sv"

class packet_generator;

string name;
packet_tran_mbox drv_mbox;
int run_for_n_trans = 0;
packet_transaction pkt_tran;
event done;

extern function new(string name = "CPU Generator", packet_tran_mbox drv_mbox);
extern task start();

endclass

function packet_generator::new(string name, packet_tran_mbox drv_mbox);
    this.name = name;
    this.drv_mbox = drv_mbox;
endfunction

task packet_generator::start();
    fork
        begin
            for(int i=0; i<run_for_n_trans; i++) begin
                packet_transaction pkt_tran_cpy = new pkt_tran;
                if(!pkt_tran_cpy.randomize()) begin
                    $display("Error to randomize pkt_tran_cpy");
                end
                drv_mbox.put(pkt_tran_cpy);
            end
            ->done;
        end
    join_none
endtask

`endif

/////////////////////////////////////////////
//packet_driver.sv
/////////////////////////////////////////////
`ifndef __PACKET_DRIVER_SV__
`define __PACKET_DRIVER_SV__

`include "packet_transaction.sv"
`include "packet_define.sv"
`include "packet_interface.svi"

class packet_driver;

string name;
packet_tran_mbox gen2drv_mbox;
packet_tran_mbox drv2sb_mbox;
virtual packet_interface.master pkt_intf;

extern function new(string name = "CPU Driver", packet_tran_mbox gen2drv_mbox, packet_tran_mbox drv2sb_mbox, virtual packet_interface.master pkt_intf);
extern task start();

endclass

function packet_driver::new(string name, packet_tran_mbox gen2drv_mbox, packet_tran_mbox drv2sb_mbox, virtual packet_interface.master pkt_intf);
    this.name = name;
    this.gen2drv_mbox = gen2drv_mbox;
    this.drv2sb_mbox = drv2sb_mbox;
    this.pkt_intf = pkt_intf;
endfunction

task packet_driver::start();
    fork
        forever begin
            packet_transaction pkt_tran;
            gen2drv_mbox.get(pkt_tran);
            //pkt_tran.display();
            drv2sb_mbox.put(pkt_tran.copy());

            @(pkt_intf.cb);
            pkt_intf.cb.tx_vld <= 1'b1;
            pkt_intf.cb.txd    <= pkt_tran.header[15:8];
            @(pkt_intf.cb);
            pkt_intf.cb.txd    <= pkt_tran.header[7:0];

            foreach(pkt_tran.payload[i]) begin
                @(pkt_intf.cb);
                pkt_intf.cb.txd    <= pkt_tran.payload[i];
            end

            @(pkt_intf.cb);
            pkt_intf.cb.tx_vld <= 1'b0;

        end
    join_none
endtask

`endif

/////////////////////////////////////////////
//packet_monitor.sv
/////////////////////////////////////////////
`ifndef __PACKET_MONITOR_SV__
`define __PACKET_MONITOR_SV__

`include "packet_transaction.sv"
`include "packet_define.sv"
`include "packet_interface.svi"

class packet_monitor;

string name;
packet_tran_mbox out_box;
virtual packet_interface.master pkt_intf;

extern function new(string name = "CPU monitor", packet_tran_mbox out_box, virtual packet_interface.master pkt_intf);
extern task start();

endclass

function packet_monitor::new(string name, packet_tran_mbox out_box, virtual packet_interface.master pkt_intf);
    this.name = name;
    this.out_box = out_box;
    this.pkt_intf = pkt_intf;
endfunction

task packet_monitor::start();
    fork
        forever begin
            logic [7:0] rxd[$];
            packet_transaction pkt_tran = new();

            @(pkt_intf.cb);
            while(pkt_intf.cb.rx_vld == 1'b0) begin
                @(pkt_intf.cb);
            end

            while(pkt_intf.cb.rx_vld == 1'b1) begin
                rxd.push_back(pkt_intf.cb.rxd);
                @(pkt_intf.cb);
            end

            pkt_tran.byte2pkt(rxd);
            //pkt_tran.display("Monitor");

            out_box.put(pkt_tran);
        end
    join_none
endtask

`endif

/////////////////////////////////////////////
//packet_scoreboard.sv
/////////////////////////////////////////////
`ifndef __PACKET_SCOREBOARD_SV__
`define __PACKET_SCOREBOARD_SV__

`include "packet_transaction.sv"
`include "packet_define.sv"

class packet_scoreboard;

string name;
packet_tran_mbox drv2sb_mbox;
packet_tran_mbox mon2sb_mbox;
packet_transaction ref_pkt_tran[$];

integer error_no = 0;
integer send_no = 0;
integer receive_no = 0;

extern function new(string name = "CPU Scoreboard", packet_tran_mbox drv2sb_mbox, packet_tran_mbox mon2sb_mbox);
extern task start();
extern task get_pkt_from_mon_and_check();
extern function display(string prefix = "Packet Scoreboard Result");

endclass

function packet_scoreboard::new(string name, packet_tran_mbox drv2sb_mbox, packet_tran_mbox mon2sb_mbox);
    this.name = name;
    this.drv2sb_mbox = drv2sb_mbox;
    this.mon2sb_mbox = mon2sb_mbox;
endfunction

task packet_scoreboard::start();
    fork
        get_pkt_from_mon_and_check();
    join_none
endtask

task packet_scoreboard::get_pkt_from_mon_and_check();
    string message;
    packet_transaction pkt_tran_mon;
    packet_transaction pkt_tran_drv;
    forever begin
        message = "Scoreboare Comparison";

        mon2sb_mbox.get(pkt_tran_mon);
        receive_no++;

        drv2sb_mbox.get(pkt_tran_drv);
        send_no++;

        if(!pkt_tran_drv.compare(pkt_tran_mon, message)) begin
            error_no++;
        end
        $display(message);
    end
endtask

function packet_scoreboard::display(string prefix);
    $display("Send: %d, Receive: %d, Error: %d", send_no + drv2sb_mbox.num(), receive_no, error_no);
endfunction

`endif

/////////////////////////////////////////////
//packet_env.sv
/////////////////////////////////////////////
`ifndef __PACKET_ENV_SV__
`define __PACKET_ENV_SV__

`include "packet_interface.svi"
`include "packet_transaction.sv"
`include "packet_generator.sv"
`include "packet_define.sv"
`include "packet_driver.sv"
`include "packet_scoreboard.sv"
`include "packet_monitor.sv"

class packet_env;

packet_tran_mbox  gen2drv_mbox = new(1);
packet_tran_mbox  drv2sb_mbox = new();
packet_tran_mbox  mon2sb_mbox = new();
packet_generator  pkt_gen;
packet_driver     pkt_drv;
packet_scoreboard pkt_sb;
packet_monitor    pkt_mon;

virtual packet_interface.master pkt_intf;

extern function new(virtual packet_interface.master pkt_intf);
extern function configure();
extern task reset();
extern task start();

endclass

function packet_env::new(virtual packet_interface.master pkt_intf);
    this.pkt_intf = pkt_intf;
    pkt_gen = new("Packet Generator", gen2drv_mbox);
    pkt_drv = new("Packet Driver", gen2drv_mbox, drv2sb_mbox, pkt_intf);
    pkt_mon = new("Packet Monitor", mon2sb_mbox, pkt_intf);
    pkt_sb  = new("Packet Scoreboard", drv2sb_mbox, mon2sb_mbox);
endfunction

function packet_env::configure();
endfunction

task packet_env::reset();
    @(pkt_intf.cb);
    pkt_intf.cb.txd <= 8'h0;
    pkt_intf.cb.tx_vld <= 1'b0;

    @(pkt_intf.cb);
    pkt_intf.rst_n = 1'b0;
    @(pkt_intf.cb);
    pkt_intf.rst_n = 1'b1;
    @(pkt_intf.cb);
endtask

task packet_env::start();
    pkt_gen.start();
    pkt_drv.start();
    pkt_mon.start();
    pkt_sb.start();
endtask

`endif

/////////////////////////////////////////////
//test.sv
/////////////////////////////////////////////
`ifndef __TEST_SV__
`define __TEST_SV__

`include "packet_interface.svi"
`include "packet_transaction.sv"
`include "packet_env.sv"

program test(packet_interface.master pkt_intf);

class packet_transaction_ext extends packet_transaction;
    //constraint addr_range {addr[7:2] inside {[8'h0:8'h1]}; addr[1:0] == 2'h0;}
    //constraint write {rw == 1'b0;}
endclass

packet_transaction_ext packet_tran_ext = new();
packet_env pkt_e;

initial begin
    pkt_e = new(pkt_intf);
    pkt_e.configure();

    pkt_e.pkt_gen.pkt_tran = packet_tran_ext;
    pkt_e.pkt_gen.run_for_n_trans = 100;

    pkt_e.reset();
    pkt_e.start();

    @pkt_e.pkt_gen.done;
    repeat(10000) begin
        @(pkt_intf.cb);
        if(pkt_e.pkt_sb.send_no == pkt_e.pkt_gen.run_for_n_trans) begin
            break;
        end
    end
    pkt_e.pkt_sb.display();
    $finish();
end

endprogram
`endif

/////////////////////////////////////////////
//top.sv
/////////////////////////////////////////////
`timescale 1ns/1ps

`include "packet_interface.svi"
`include "test.sv"

module top;
parameter pkt_clock_cycle = 10;

logic clk;

packet_interface pkt_intf(clk);

test u_test(pkt_intf);

dut u_dut (
    .clk    (pkt_intf.clk    ) ,
    .rst_n  (pkt_intf.rst_n  ) ,
    .txd    (pkt_intf.rxd    ) ,
    .tx_vld (pkt_intf.rx_vld ) ,
    .rxd    (pkt_intf.txd    ) ,
    .rx_vld (pkt_intf.tx_vld )  
);

initial begin
    $timeformat(-9, 1, "ns", 10);
    clk = 0;
    forever begin
        #(pkt_clock_cycle/2) clk = ~clk;
    end
end

`ifdef WAVE_ON
initial begin
    $vcdpluson();
end
`endif

endmodule

#############################################
#Makefile
#############################################
ifeq ($(GUI), 1)
    GUI_ARG = -gui
endif
RTL_DIR = /mnt/packet
RTL = $(RTL_DIR)/dut.v

TB_DIR = /mnt/packet
SVTB  = $(TB_DIR)/top.sv

INCDIR = +incdir+$(TB_DIR)

COMPILE_LOG_ARG = -l vcs.log

WAVE_ARG = +define+WAVE_ON=1

COMPILE_ARG = -sverilog -debug_all
COMPILE_ARG += $(INCDIR) $(COMPILE_LOG_ARG) $(WAVE_ARG)

RUN_LOG_ARG = -l simv.log

RUN_ARG  =
RUN_ARG += $(RUN_LOG_ARG) $(GUI_ARG)

SEED = 1

default: test

test: compile run

run:
./simv $(RUN_ARG) +ntb_random_seed=$(SEED)

compile:
vcs $(COMPILE_ARG) $(SVTB) $(RTL)

clean:
rm -rf simv simv.* *log