Wednesday, October 29, 2014

an example of systemVerilog TB with callback

//////////////////////////////////////////////////
// ./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;
mem[mem_wr_ptr] <= rxd;

if(rxd != 8'h55) begin
pkt_recv_status <= `PKT_RECV_END;
end
else begin
pkt_recv_status <= `PKT_RECV_START;
end
end
end
`PKT_RECV_START: begin
if(rx_vld == 1'b1) begin
mem_wr_ptr <= mem_wr_ptr + 1;
mem[mem_wr_ptr] <= rxd;

if(rxd != 8'hd5) begin
pkt_recv_status <= `PKT_RECV_END;
end
else begin
pkt_recv_status <= `PKT_RECV_RECV;
end
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/transactor.sv
//////////////////////////////////////////////////
`ifndef __TRANSACTOR_SV__
`define __TRANSACTOR_SV__

`include "transactor_callback.sv"

class transactor;
transactor_callback callbacks[$];

function new();
endfunction

virtual function bit prepend_callback(transactor_callback cb);
foreach (this.callbacks[i]) begin
if (this.callbacks[i] == cb) begin
return 1;
end
end

this.callbacks.push_front(cb);

return 0;
endfunction

virtual function bit append_callback(transactor_callback cb);
foreach (this.callbacks[i]) begin
if (this.callbacks[i] == cb) begin
return 1;
end
end

this.callbacks.push_back(cb);

return 0;
endfunction

virtual function bit unregister_callback(transactor_callback cb);
foreach (this.callbacks[i]) begin
if(cb == callbacks[i]) begin
callbacks.delete(i);
return 0;
end
end

return 1;
endfunction

endclass

`endif


//////////////////////////////////////////////////
// ./env/transactor_callback.sv
//////////////////////////////////////////////////
`ifndef __TRANSACTOR_CALLBACK_SV__
`define __TRANSACTOR_CALLBACK_SV__

`define callback_macro(facade, call) foreach(this.callbacks[i]) begin facade cb; if(!$cast(cb, this.callbacks[i])) continue; cb.call; end

virtual class transactor_callback;
endclass

`endif


//////////////////////////////////////////////////
// ./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 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"
`include "transactor_callback.sv"
`include "transactor.sv"

class cpu_driver extends transactor;

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

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

endclass

class cpu_driver_callback extends transactor_callback;
virtual task driver_pre_transactor (cpu_driver xactor, ref cpu_transaction cpu_tr, ref bit drop);
endtask
virtual task driver_post_transactor (cpu_driver xactor, ref cpu_transaction cpu_tr);
endtask
endclass

function cpu_driver::new(string name, cpu_tran_mbox in_box, virtual cpu_interface.master cpu_intf);
super.new();

this.name = name;
this.in_box = in_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

`callback_macro(cpu_driver_callback, driver_post_transactor(this, 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"
`include "cpu_driver.sv"

class cpu_scoreboard;

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

extern function new(string name = "CPU Scoreboard");
extern function bit compare(cpu_transaction data);
extern function display(string prefix = "CPU Scoreboard Result");

endclass

class cpu_driver_sb_callback extends cpu_driver_callback;
cpu_scoreboard cpu_sb;

function new(cpu_scoreboard cpu_sb);
this.cpu_sb = cpu_sb;
endfunction

virtual task driver_post_transactor(cpu_driver xactor, ref cpu_transaction cpu_tr);
cpu_sb.compare(cpu_tr);
endtask
endclass

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

for(int i=0; i<128 data-blogger-escaped-begin="" data-blogger-escaped-br="" data-blogger-escaped-i=""> cpu_reg[i] = 32'h0;
end
endfunction

function bit cpu_scoreboard::compare(cpu_transaction data);
cpu_transaction tr;
string message;

$cast(tr, data);

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

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_generator cpu_gen;
cpu_driver cpu_drv;
cpu_scoreboard cpu_sb;
cpu_driver_sb_callback cpu_drv_cbs_scb;

virtual cpu_interface.master cpu_intf;

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

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, cpu_intf);
cpu_sb = new("CPU Scoreboard");

cpu_drv_cbs_scb = new(cpu_sb);
cpu_drv.append_callback(cpu_drv_cbs_scb);

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

function cpu_env::report();
cpu_sb.display();
endfunction

`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] = %0h", 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/packet_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 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"
`include "transactor_callback.sv"
`include "transactor.sv"

class packet_driver extends transactor;

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

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

endclass

class packet_driver_callback extends transactor_callback;
virtual task driver_pre_transactor (packet_driver xactor, ref packet_transaction pkt_tr, ref bit drop);
endtask
virtual task driver_post_transactor (packet_driver xactor, ref packet_transaction pkt_tr);
endtask
endclass

function packet_driver::new(string name, packet_tran_mbox gen2drv_mbox, virtual packet_interface.master pkt_intf);
super.new();

this.name = name;
this.gen2drv_mbox = gen2drv_mbox;
this.pkt_intf = pkt_intf;
endfunction

task packet_driver::start();
integer delay = 0;
bit drop = 0;
fork
forever begin
packet_transaction pkt_tran;
packet_transaction cb_pkt_tr;

gen2drv_mbox.get(pkt_tran);
//pkt_tran.display();

cb_pkt_tr = pkt_tran.copy();
`callback_macro(packet_driver_callback, driver_pre_transactor(this, cb_pkt_tr, drop))

@(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"
`include "transactor_callback.sv"
`include "transactor.sv"

class packet_monitor extends transactor;

string name;
virtual packet_interface.master pkt_intf;

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

endclass

class packet_monitor_callback extends transactor_callback;
virtual task monitor_pre_transactor (packet_monitor xactor, ref packet_transaction pkt_tr, ref bit drop);
endtask
virtual task monitor_post_transactor (packet_monitor xactor, ref packet_transaction pkt_tr);
endtask
endclass

function packet_monitor::new(string name, virtual packet_interface.master pkt_intf);
super.new();

this.name = name;
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");

`callback_macro(packet_monitor_callback, monitor_post_transactor(this, pkt_tran))
end
join_none
endtask

`endif


//////////////////////////////////////////////////
// ./env/packet_env/packet_rm.sv
//////////////////////////////////////////////////
`ifndef __PACKET_RM_SV__
`define __PACKET_RM_SV__

`include "packet_transaction.sv"

class packet_rm;

string name;

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

extern function new(string name="Packet RM", int unsigned min_pkt_size = 0, int unsigned max_pkt_size = 0);
extern function bit packet_process(ref packet_transaction pkt_tr);

endclass

function packet_rm::new(string name="Packet RM", int unsigned min_pkt_size = 0, int unsigned max_pkt_size = 0);
if(min_pkt_size == 0) begin
this.min_pkt_size = 32'd64;
end
else begin
this.min_pkt_size = min_pkt_size[9:0];
end

if(max_pkt_size == 0) begin
this.max_pkt_size = 32'd512;
end
else begin
this.max_pkt_size = max_pkt_size[9:0];
end
endfunction

function bit packet_rm::packet_process(ref packet_transaction pkt_tr);
if(pkt_tr == null) begin
return 0;
end

if(pkt_tr.header != 16'h55d5) begin
return 0;
end

if(pkt_tr.payload.size() < min_pkt_size - 2 || pkt_tr.payload.size() > max_pkt_size - 2) begin
return 0;
end

return 1;
endfunction

`endif


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

`include "packet_transaction.sv"
`include "packet_define.sv"
`include "packet_driver.sv"
`include "packet_monitor.sv"
`include "packet_rm.sv"

class packet_scoreboard;

string name;
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");
extern function display(string prefix = "Packet Scoreboard Result");

endclass

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

class packet_driver_sb_callback extends packet_driver_callback;
packet_scoreboard pkt_sb;
packet_rm pkt_rm;

function new(packet_scoreboard pkt_sb, packet_rm pkt_rm);
this.pkt_sb = pkt_sb;
this.pkt_rm = pkt_rm;
endfunction

virtual task driver_pre_transactor (packet_driver xactor, ref packet_transaction pkt_tr, ref bit drop);
if(!pkt_rm.packet_process(pkt_tr)) begin
drop = 1;
end
else begin
pkt_sb.ref_pkt_tran.push_back(pkt_tr);
pkt_sb.send_no++;
drop = 0;

pkt_sb.display("[PKT][SEND]");
end
endtask
endclass

class packet_monitor_sb_callback extends packet_monitor_callback;
packet_scoreboard pkt_sb;

function new(packet_scoreboard pkt_sb);
this.pkt_sb = pkt_sb;
endfunction

virtual task monitor_post_transactor (packet_monitor xactor, ref packet_transaction pkt_tr);
pkt_sb.receive_no++;

if(pkt_sb.ref_pkt_tran.size() > 0) begin
packet_transaction ref_pkt_tr = pkt_sb.ref_pkt_tran.pop_front();

string message = "Scoreboare Comparison:\n";

if(!pkt_tr.compare(ref_pkt_tr, message)) begin
pkt_sb.error_no++;
end

message = { message, $psprintf("\n[PKT][RECV]") };
pkt_sb.display(message);
end
else begin
pkt_sb.error_no++;
pkt_sb.display("[PKT][RECV][RECV PKT > SEND PKT]");
pkt_tr.display("[PKT][RECV][Error]");
end

endtask
endclass

function packet_scoreboard::display(string prefix);
$display("%s Send: %d, Receive: %d, Error: %d", prefix, send_no, 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"
`include "packet_rm.sv"

class packet_env;

packet_tran_mbox gen2drv_mbox = new(1);
packet_generator pkt_gen;
packet_driver pkt_drv;
packet_scoreboard pkt_sb;
packet_monitor pkt_mon;
packet_driver_sb_callback pkt_drv_sb_cb;
packet_monitor_sb_callback pkt_mon_sb_cb;
packet_rm pkt_rm;

virtual packet_interface.master pkt_intf;

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

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, pkt_intf);
pkt_mon = new("Packet Monitor", pkt_intf);
pkt_sb = new("Packet Scoreboard");
pkt_rm = new("Packet RM");

pkt_drv_sb_cb = new(pkt_sb, pkt_rm);
pkt_mon_sb_cb = new(pkt_sb);

pkt_drv.append_callback(pkt_drv_sb_cb);
pkt_mon.append_callback(pkt_mon_sb_cb);

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

function packet_env::report();
pkt_sb.display();
endfunction

`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();
extern function report();

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

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

`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_rm.min_pkt_size = 65;
e.pkt_e.pkt_rm.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.report();
$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


Tuesday, October 14, 2014

realization of callback in cpp

main.cpp
#include <iostream>
#include <list>
#include <string>

using std::list;
using std::string;
using std::cout;
using std::endl;

#define callback_macro(facade, call) \
 \
for(list<transactor_callbacks*>::iterator it=this->callbacks.begin(); it!=this->callbacks.end(); ++it) { \
   facade* cb = dynamic_cast<facade*>(*it); \
   cb->call; \
}

class transactor_callbacks {
public:
    virtual ~transactor_callbacks(){}
};

class driver;
class driver_callbacks: virtual public transactor_callbacks {
public:
   virtual void driver_pre_transactor (driver* xactor, bool* drop) {}
   virtual void driver_post_transactor (driver* xactor) {}

};

class driver {
public:
    virtual bool prepend_callback(transactor_callbacks* cb) {
        for(list<transactor_callbacks*>::iterator it=callbacks.begin(); it!=callbacks.end(); ++it) {
            if(cb == *it) {
                return 1;
            }
        }

        callbacks.push_front(cb);

        return 0;
    }

    virtual bool append_callback(transactor_callbacks* cb) {
        for(list<transactor_callbacks*>::iterator it=callbacks.begin(); it!=callbacks.end(); ++it) {
            if(cb == *it) {
                return 1;
            }
        }

        callbacks.push_back(cb);

        return 0;
    }

    virtual bool unregister_callback(transactor_callbacks* cb) {
        for(list<transactor_callbacks*>::iterator it=callbacks.begin(); it!=callbacks.end(); ++it) {
            if(cb == *it) {
                callbacks.erase(it);
                return 0;
            }
        }

        return 1;
    }

    void run() {
        callback_macro(driver_callbacks, driver_pre_transactor(this, &drop));
        callback_macro(driver_callbacks, driver_post_transactor(this));
    }

    list<transactor_callbacks*> callbacks;
    bool drop;
};

class driver_pre_transactor1: public driver_callbacks {
public:
    driver_pre_transactor1(string name="cb1"):driver_callbacks() {
        this->name = name;
    }

   virtual void driver_pre_transactor (driver* xactor, bool* drop) {
       cout << name << ": before transactor" << endl;
   }

private:
    string name;

};

class driver_post_transactor2: public driver_callbacks {
public:
    driver_post_transactor2(string name="cb2"):driver_callbacks() {
        this->name = name;
    }

    virtual void driver_post_transactor (driver* xactor) {
        cout << name << ": after transactor" << endl;
    }

private:
    string name;

};

int main(int argc, char** argv) {
    driver* drv = new driver();
    driver_pre_transactor1* drv_pre_tx1 = new driver_pre_transactor1("pre_tx1");
    driver_pre_transactor1* drv_pre_tx2 = new driver_pre_transactor1("pre_tx2");
    driver_post_transactor2* drv_post_tx1 = new driver_post_transactor2("post_tx1");

    drv->append_callback(drv_pre_tx1);
    drv->append_callback(drv_pre_tx2);
    drv->append_callback(drv_post_tx1);
    drv->run();

    drv->unregister_callback(drv_pre_tx2);
    drv->run();

    return 0;
}

Makefile
PROJECT = main
EXE = $(PROJECT).exe

OBJ = $(patsubst %.exe,%.o,$(EXE))

.PHONY: clean all

all: clean $(EXE)

clean:
rm -rf *.o *.exe

$(EXE): $(OBJ)
g++ $^ -o $@

Sunday, October 12, 2014

Implement a channel in cpp

#ifndef __CHANNEL_H__
#define __CHANNEL_H__

#include <list>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <iostream>
#include <system_error>
#include "packet.h"

using std::cout;
using std::endl;

//#define DEBUG 1

template<class item>
class channel {
public:
    channel(int queue_max = -1) : closed(false), queue_max(queue_max) { }
    ~channel() {
        cout << "delete channel" << endl;
    }

    int size() {
        return queue.size();
    }

    void close() {
        std::unique_lock<std::mutex> lock_q(m_q);
        closed = true;
        cv_qne.notify_all();
        cv_qnf.notify_all();
    }

    bool is_closed() {
        std::unique_lock<std::mutex> lock_q(m_q);
        return closed;
    }

    bool put(item* &in, bool wait = true) {
#ifdef DEBUG
        cout << "before checking queue size in put" << endl;
#endif
        if(queue_max > 0 && queue.size() >= queue_max) {
            if(wait) {
                //std::unique_lock<std::mutex> lock_wait_for_queue_not_full(m_qnf, std::defer_lock);
                std::unique_lock<std::mutex> lock_wait_for_queue_not_full(m_qnf);
                cv_qnf.wait(lock_wait_for_queue_not_full, [this]() -> bool {return closed || queue.size() < queue_max;});
            }
            else {
                return false;
            }
        }
#ifdef DEBUG
        cout << "after checking queue size in put" << endl;
#endif

        if(closed) {
            throw std::logic_error("put to closed channel");
        }

#ifdef DEBUG
        cout << "before locking queue in put" << endl;
#endif
        std::unique_lock<std::mutex> lock_q(m_q);
        queue.push_back(in);
        lock_q.unlock();
#ifdef DEBUG
        cout << "after locking queue in put" << endl;
#endif

#ifdef DEBUG
        cout << "before notifying get in put" << endl;
#endif
        cv_qne.notify_one();
        //cv_qne.notify_all();
#ifdef DEBUG
        cout << "after notifying get in put" << endl;
#endif

        return true;
    }

    bool get(item* &out, bool wait = true) {
#ifdef DEBUG
        cout << "before checking queue size in get" << endl;
#endif
        if(wait) {
            //std::unique_lock<std::mutex> lock_wait_for_queue_not_empty(m_qne, std::defer_lock);
            std::unique_lock<std::mutex> lock_wait_for_queue_not_empty(m_qne);
            cv_qne.wait(lock_wait_for_queue_not_empty, [this]() -> bool {return closed || !queue.empty();});
        }
        else {
            if(queue.empty()) {
                return false;
            }
        }
#ifdef DEBUG
        cout << "after checking queue size in get" << endl;
#endif

        if(closed) {
            throw std::logic_error("channel is closed");
        }

#ifdef DEBUG
        cout << "before locking queue in put" << endl;
#endif
        std::unique_lock<std::mutex> lock_q(m_q);
        out = queue.front();
        queue.pop_front();
        lock_q.unlock();
#ifdef DEBUG
        cout << "after locking queue in put" << endl;
#endif

        cv_qnf.notify_one();

        return true;
    }
   
private:
    std::list<item* > queue;
    std::mutex m_qne;
    std::condition_variable cv_qne;
    std::mutex m_qnf;
    std::condition_variable cv_qnf;
    std::mutex m_q;
    bool closed;
    int queue_max;

};

typedef channel<packet > packet_channel;

#endif