Wednesday, August 6, 2014

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

No comments:

Post a Comment