1、interface
interface router_io(input bit clock);
logic reset_n;
logic [15:0] din;
logic [15:0] frame_n;
logic [15:0] valid_n;
logic [15:0] dout;
logic [15:0] valido_n;
logic [15:0] busy_n;
logic [15:0] frameo_n;
clocking cb @(posedge clock);
default input #1ns output #1ns;
output reset_n;
output frame_n;
output valid_n;
input dout;
input valido_n;
input frameo_n;
input busy_n;
endclocking:cb
modport TB(clocking cb,output reset_n );
endinterface
2、transaction(packet)
class packet;
rand reg [3:0] sa,da; // random port selection
rand reg[7:0] payload[$]; // random data array
string name; // unique identifier
constraint Limit {
sa inside {[0:15]};
da inside {[0:15]};
payload.size() inside {[2:4]};
}
extern function new(string name = "Packet"); //将所有的function和task都放到class外面完成
extern function void pre_randomize();
extern function bit compare(Packet pkt2cmp, ref string message);
extern function void display(string prefix = "NOTE");
endclass
function Packet::new(string name); //构造函数new
this.name = name;
endfunction
function void Packet::pre_randomize();
this.payload.delete();
endfunction
function bit Packet::compare(Packet pkt2cmp, ref string message);
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
foreach(payload[i]) begin
if (payload[i] == pkt2cmp.payload[i]) ;
else begin
message = "Payload Content Mismatch:\n";
foreach(payload[i])
message = { message, $psprintf("payload[%0d] = %0d,\tpkt2cmp.payload[%0d] = %0d\n", i, payload[i], i, pkt2cmp.payload[i]) };
return(0);
end
end
message = "Successfully Compared";
return(1);
endfunction
function void Packet::display(string prefix = "NOTE");
$display("[%s]%0d %s sa = %0d, da = %0d", prefix, $time, name, sa, da);
foreach(payload[i])
$display("[%s]%0d %s payload[%0d] = %0d", prefix, $time, name, i, payload[i]);
endfunction
3、generator
class generator;
string name; // unique identifier
Packet pkt2send; // stimulus Packet object
mailbox out_box[]; // mailbox to Drivers
extern function new(string name = "Generator");
extern virtual task gen();
extern virtual task start();
endclass
function Generator::new(string name);
if (TRACE_ON) $display("[TRACE]%0d %s:%m", $time, name);
this.name = name;
this.pkt2send = new();
this.out_box = new[16];
foreach(this.out_box[i])
this.out_box[i] = new();
endfunction
task Generator::gen();
static int pkts_generated = 0;
if (TRACE_ON) $display("[TRACE]%0d %s:%m", $time, name);
pkt2send.name = $psprintf("Packet[%0d]", pkts_generated++);
if (!pkt2send.randomize()) begin
$display("\n%m\n[ERROR]%0d Randomization Failed!\n", $time);
$finish;
end
endtask
task Generator::start();
if (TRACE_ON) $display("[TRACE]%0d %s:%m", $time, name);
fork
for (int i=0; i<run_for_n_packets || run_for_n_packets <= 0; i++) begin
gen();
begin
Packet pkt = new pkt2send;
out_box[pkt.sa].put(pkt);
end
end
join_none
endtask
4、driver(此处包含driverbase和driver)
class driverbase;
virtual router_io.TB router;
string name;
reg [3:0] sa,da;
reg [7:0] payload[$];
packet pkt2send;
extern function new(string name="driverbase",virtual router_io.TB router);
extern virtual task send();
extern virtual task send_addrs();
extern virtual task send_pad();
extern virtual task send_payload();
endclass
function DriverBase::new(string name, virtual router_io.TB router);
if (TRACE_ON) $display("[TRACE]%0d %s:%m", $time, name);
this.name = name;
this.router = router;
endfunction
task DriverBase::send();
if (TRACE_ON) $display("[TRACE]%0d %s:%m", $time, name);
send_addrs();
send_pad();
send_payload();
endtask
task DriverBase::send_addrs();
if (TRACE_ON) $display("[TRACE]%0d %s:%m", $time, name);
router.cb.frame_n[sa] <= 1'b0;
for(int i=0; i<4; i++) begin
router.cb.din[sa] <= da[i];
@(router.cb);
end
endtask
task DriverBase::send_pad();
if (TRACE_ON) $display("[TRACE]%0d %s:%m", $time, name);
router.cb.din[sa] <= 1'b1;
router.cb.valid_n[sa] <= 1'b1;
repeat(5) @(router.cb);
endtask
task DriverBase::send_payload();
if (TRACE_ON) $display("[TRACE]%0d %s:%m", $time, name);
foreach(payload[index]) begin
for(int i=0; i<8; i++) begin
router.cb.din[sa] <= payload[index][i];
router.cb.valid_n[sa] <= 1'b0;
router.cb.frame_n[sa] <= ((index == (payload.size() - 1)) && (i == 7));
@(router.cb);
end
end
router.cb.valid_n[sa] <= 1'b1;
endtask
`include "DriverBase.sv"
class Driver extends DriverBase;
mailbox in_box; // Generator mailbox
mailbox out_box; // Scoreboard mailbox
semaphore sem[]; // output port arbitration
extern function new(string name = "Driver", int port_id, semaphore sem[], mailbox in_box, out_box, virtual router_io.TB router);
extern virtual task start();
endclass
function Driver::new(string name, int port_id, semaphore sem[], mailbox in_box, out_box, virtual router_io.TB router);
super.new(name, router);
if (TRACE_ON) $display("[TRACE]%0d %s:%m", $time, name);
this.sa = port_id;
this.sem = sem;
this.in_box = in_box;
this.out_box = out_box;
endfunction
task Driver::start();
if (TRACE_ON) $display("[TRACE]%0d %s:%m", $time, name);
fork
forever begin
in_box.get(pkt2send);
if (pkt2send.sa != this.sa) continue;
this.da = pkt2send.da;
this.payload = pkt2send.payload;
sem[this.da].get(1);
send();
out_box.put(pkt2send);
sem[this.da].put(1);
end
join_none
endtask
// Do not uncomment!
// The following is the prototype of the DriverBase class:
//
// class DriverBase;
// virtual router_io.TB router; // interface signal
// string name; // unique identifier
// reg[3:0] sa, da; // source and destination addresses
// reg[7:0] payload[$]; // Packet payload
// Packet pkt2send; // stimulus Packet object
//
// extern function new(string name = "DriverBase", virtual router_io.TB router);
// extern virtual task send();
// extern virtual task send_addrs();
// extern virtual task send_pad();
// extern virtual task send_payload();
// endclass
5、receiver(monitor)
class ReceiverBase;
virtual router_io.TB router; // interface signals
string name; // unique identifier
reg[3:0] da; // output port to monitor
reg[7:0] pkt2cmp_payload[$]; // actual payload array
Packet pkt2cmp; // actual Packet object
extern function new(string name = "ReceiverBase", virtual router_io.TB router);
extern virtual task recv();
extern virtual task get_payload();
endclass
function ReceiverBase::new(string name, virtual router_io.TB router);
if (TRACE_ON) $display("[TRACE]%0d %s:%m", $time, name);
this.name = name;
this.router = router;
pkt2cmp = new();
endfunction
task ReceiverBase::recv();
static int pkt_cnt = 0;
if (TRACE_ON) $display("[TRACE]%0d %s:%m", $time, name);
get_payload();
pkt2cmp.da = da;
pkt2cmp.payload = pkt2cmp_payload;
pkt2cmp.name = $psprintf("rcvdPkt[%0d]", pkt_cnt++);
endtask
task ReceiverBase::get_payload();
if (TRACE_ON) $display("[TRACE]%0d %s:%m", $time, name);
pkt2cmp_payload.delete();
fork: frameo_wd_timer
@(negedge router.cb.frameo_n[da]);
begin
repeat(1000) @(router.cb);
$display("\n%m\n[ERROR]%0d Frame signal timed out!\n", $time);
$finish;
end
join_any: frameo_wd_timer
disable fork;
while (!router.cb.frameo_n[da]) begin
reg[7:0] datum;
for (int i=0; i<8; i=i ) begin
if (!router.cb.valido_n[da])
datum[i++] = router.cb.dout[da];
if (router.cb.frameo_n[da] && (i != 8)) begin
$display("\n%m\n[ERROR]%0d Packet payload not byte aligned!\n", $time);
$finish;
end
@(router.cb);
end
pkt2cmp_payload.push_back(datum);
end
endtask
`include "ReceiverBase.sv"
class Receiver extends ReceiverBase;
mailbox out_box; // Scoreboard mailbox
extern function new(string name = "Receiver", int port_id, mailbox out_box, virtual router_io.TB router);
extern virtual task start();
endclass
function Receiver::new(string name, int port_id, mailbox out_box, virtual router_io.TB router);
super.new(name, router);
if (TRACE_ON) $display("[TRACE]%0d %s:%m", $time, name);
this.da = port_id;
this.out_box = out_box;
endfunction
task Receiver::start();
if (TRACE_ON) $display("[TRACE]%0d %s:%m", $time, name);
fork
forever begin
recv();
begin
Packet pkt = new pkt2cmp;
out_box.put(pkt);
end
end
join_none
endtask
// Do not uncomment!
//
// class ReceiverBase;
// virtual router_io.TB router; // interface signals
// string name; // unique identifier
// reg[3:0] da; // output port to monitor
// reg[7:0] pkt2cmp_payload[$]; // actual payload array
// Packet pkt2cmp; // actual Packet object
//
// extern function new(string name = "ReceiverBase", virtual router_io.TB router);
// extern virtual task recv();
// extern virtual task get_payload();
// endclass
6、scoreboard
class Scoreboard;
string name; // unique identifier
event DONE; // flag to indicate goal reached
Packet refPkt[$]; // reference Packet array
Packet pkt2send; // Packet object from Drivers
Packet pkt2cmp; // Packet object from Receivers
mailbox driver_mbox; // mailbox for Packet objects from Drivers
mailbox receiver_mbox; // mailbox fro Packet objects from Receivers
bit[3:0] sa, da; // functional coverage properties
covergroup router_cov; //覆盖点
coverpoint sa;
coverpoint da;
cross sa, da;
endgroup
extern function new(string name = "Scoreboard", mailbox driver_mbox = null, receiver_mbox = null);
extern virtual task start();
extern virtual task check();
endclass
function Scoreboard::new(string name, mailbox driver_mbox, receiver_mbox); //构造函数
if (TRACE_ON) $display("[TRACE]%0d %s:%m", $time, name);
this.name = name;
if (driver_mbox == null) driver_mbox = new();
this.driver_mbox = driver_mbox;
if (receiver_mbox == null) receiver_mbox = new();
this.receiver_mbox = receiver_mbox;
router_cov = new();
endfunction
task Scoreboard::start(); //task
if (TRACE_ON) $display("[TRACE]%0d %s:%m", $time, name);
fork
while (1) begin
receiver_mbox.get(pkt2cmp);
while (driver_mbox.num()) begin
Packet pkt;
driver_mbox.get(pkt);
refPkt.push_back(pkt);
end
check();
end
join_none
endtask
task Scoreboard::check();
int index[$];
string message;
static int pkts_checked = 0;
real coverage_result;
if (TRACE_ON) $display("[TRACE]%0d %s:%m", $time, name);
index = refPkt.find_first_index() with (item.da == pkt2cmp.da);
if (index.size() <= 0) begin
$display("\n%m\n[ERROR]%0d %s not found in Reference Queue\n", $time, pkt2cmp.name);
pkt2cmp.display("ERROR");
$finish;
end
pkt2send = refPkt[index[0]];
refPkt.delete(index[0]);
if (!pkt2send.compare(pkt2cmp, message)) begin
$display("\n%m\n[ERROR]%0d Packet #%0d %s\n", $time, pkts_checked, message);
pkt2send.display("ERROR");
pkt2cmp.display("ERROR");
$finish;
end
this.sa = pkt2send.sa;
this.da = pkt2send.da;
router_cov.sample();
coverage_result = $get_coverage();
$display("[NOTE]%0d Packet #%0d %s coverage = %0d", $time, pkts_checked++, message, coverage_result);
if ((pkts_checked >= run_for_n_packets) || (coverage_result == 100))
->DONE;
endtask
7、top
module router_test_top;
parameter simulation_cycle = 100;
reg SystemClock;
router_io top_io(SystemClock);
router_test test(top_io);
router dut(
.reset_n (top_io.reset_n),
.clock (top_io.clock),
.din (top_io.din),
.frame_n (top_io.frame_n),
.valid_n (top_io.valid_n),
.dout (top_io.dout),
.valido_n (top_io.valido_n),
.busy_n (top_io.busy_n),
.frameo_n (top_io.frameo_n)
);
initial begin
SystemClock = 0;
forever begin
#(simulation_cycle/2)
SystemClock = ~SystemClock;
end
end
8、test
program automatic router_test(router_io.TB router);
int run_for_n_packets; // number of packets to test
int TRACE_ON; // subroutine tracing control
`include "Packet.sv"
`include "Driver.sv"
`include "Receiver.sv"
`include "Generator.sv"
`include "Scoreboard.sv"
semaphore sem[]; // prevent output port collision
Driver drvr[]; // driver objects
Receiver rcvr[]; // receiver objects
Generator gen; // generator object
Scoreboard sb; // scoreboard object
initial begin
$vcdpluson;
run_for_n_packets = 3000;
TRACE_ON = 0;
sem = new[16];
drvr = new[16];
rcvr = new[16];
gen = new();
sb = new();
foreach (sem[i])
sem[i] = new(1);
for (int i=0; i<drvr.size(); i++)
drvr[i] = new($psprintf("drvr[%0d]", i), i, sem, gen.out_box[i], sb.driver_mbox, router);
for (int i=0; i<rcvr.size(); i++)
rcvr[i] = new($psprintf("rcvr[%0d]", i), i, sb.receiver_mbox, router);
reset();
gen.start();
sb.start();
foreach(drvr[i])
drvr[i].start();
foreach(rcvr[i])
rcvr[i].start();
wait(sb.DONE.triggered);
end
task reset();
if (TRACE_ON) $display("[TRACE]%0d %m", $time);
router.reset_n <= 1'b0;
router.cb.frame_n <= '1;
router.cb.valid_n <= '1;
##2 router.cb.reset_n <= 1'b1;
repeat(15) @(router.cb);
endtask
endprogram
program automatic test();
int a;
string message;
initial begin
$vcdpluson;
message = "SUCCESS!";
$display(message);
a = 0;
$display(a);
end
endprogram
module dut;
int a, b=0;
initial begin
a = b;
end
endmodule
module dut_dut;
dut d1;
test t1;
endmodule