blob: e5201bf999b9b5fa7b8f60bfd8fa39892fbdf3a5 [file] [log] [blame]
//---------------------------------------------------
// Macro definition
//---------------------------------------------------
`define NUM_MASTERS 2
`define NUM_SLAVES 2
`define TB $root.tb.dut
`define MASTER0 $root.tb.dut.master_0
`define MASTER1 $root.tb.dut.master_1
`define SLAVE0 $root.tb.dut.slave_0
`define SLAVE1 $root.tb.dut.slave_1
module test_program ();
import verbosity_pkg::*;
import avalon_mm_pkg::*;
//---------------------------------------------------
// Constants
//---------------------------------------------------
localparam ADDR_W = 13;
localparam SYMBOL_W = 8;
localparam NUM_SYMBOLS = 4;
localparam DATA_W = NUM_SYMBOLS * SYMBOL_W;
localparam BURST_W = 4;
localparam MAX_BURST = 8;
localparam SLAVE_SPAN = 32'h1000;
localparam MAX_COMMAND_IDLE = 5;
localparam MAX_COMMAND_BACKPRESSURE = 2;
localparam MAX_DATA_IDLE = 3;
//---------------------------------------------------
// Data structures
//---------------------------------------------------
typedef logic [BURST_W-1:0] Burstcount;
typedef enum bit
{
WRITE = 0,
READ = 1
} Transaction;
typedef enum bit
{
NOBURST = 0,
BURST = 1
} Burstmode;
typedef struct
{
Transaction trans;
Burstcount burstcount;
logic [ADDR_W-1: 0] addr;
logic [DATA_W-1:0] data [MAX_BURST-1:0];
logic [NUM_SYMBOLS-1:0] byteenable [MAX_BURST-1:0];
bit [31:0] cmd_delay;
bit [31:0] data_idles [MAX_BURST-1:0];
} Command;
typedef struct
{
Burstcount burstcount;
logic [DATA_W-1:0] data [MAX_BURST-1:0];
bit [31:0] latency [MAX_BURST-1:0];
} Response;
//---------------------------------------------------
// Command and Response Queues
//---------------------------------------------------
// master command queue
Command write_command_queue_master[`NUM_MASTERS][$];
Command read_command_queue_master[`NUM_MASTERS][$];
// slave command queue
Command write_command_queue_slave[`NUM_SLAVES][$];
Command read_command_queue_slave[`NUM_SLAVES][$];
// slave response queue
Response read_response_queue_slave[`NUM_SLAVES][$];
//---------------------------------------------------
// Macro Definitions
//---------------------------------------------------
`define MACRO_CONFIGURE_AND_PUSH_COMMAND_TO_MASTER(MASTER_ID) \
task automatic configure_and_push_command_to_master_``MASTER_ID ( \
Command cmd \
); \
`MASTER``MASTER_ID.set_command_address(cmd.addr); \
`MASTER``MASTER_ID.set_command_burst_count(cmd.burstcount); \
`MASTER``MASTER_ID.set_command_burst_size(cmd.burstcount); \
`MASTER``MASTER_ID.set_command_init_latency(cmd.cmd_delay); \
\
if (cmd.trans == WRITE) begin \
`MASTER``MASTER_ID.set_command_request(REQ_WRITE); \
for (int i = 0; i < cmd.burstcount; i++) begin \
`MASTER``MASTER_ID.set_command_data(cmd.data[i], i); \
`MASTER``MASTER_ID.set_command_byte_enable(cmd.byteenable[i], i); \
`MASTER``MASTER_ID.set_command_idle(cmd.data_idles[i], i); \
end \
end else begin \
`MASTER``MASTER_ID.set_command_request(REQ_READ); \
`MASTER``MASTER_ID.set_command_idle(cmd.data_idles[0], 0); \
end \
\
`MASTER``MASTER_ID.push_command(); \
endtask
// Get command received by slave, verify command.
// If command is read command, send out response
`define MACRO_SLAVE_THREAD(SLAVE_ID) \
always @(`SLAVE``SLAVE_ID.signal_command_received) begin \
\
Command actual_cmd, exp_cmd; \
Response rsp; \
\
automatic int backpressure_cycles; \
\
// set random backpressure cycles for next command \
for (int i = 0; i < MAX_BURST; i++) begin \
backpressure_cycles = $urandom_range(0, MAX_COMMAND_BACKPRESSURE); \
`SLAVE``SLAVE_ID.set_interface_wait_time(backpressure_cycles, i); \
end \
\
actual_cmd = get_command_from_slave_``SLAVE_ID(); \
exp_cmd = get_expected_command_for_slave(actual_cmd, ``SLAVE_ID); \
verify_command(actual_cmd, exp_cmd); \
\
// set read response \
if (actual_cmd.trans == READ) begin \
rsp = create_response(actual_cmd.burstcount); \
configure_and_push_response_to_slave_``SLAVE_ID(rsp); \
read_response_queue_slave[``SLAVE_ID].push_back(rsp); \
end \
end
`define MACRO_GET_COMMAND_FROM_SLAVE(SLAVE_ID) \
function automatic Command get_command_from_slave_``SLAVE_ID (); \
\
Command cmd; \
\
`SLAVE``SLAVE_ID.pop_command(); \
cmd.burstcount = `SLAVE``SLAVE_ID.get_command_burst_count(); \
cmd.addr = `SLAVE``SLAVE_ID.get_command_address(); \
\
if (`SLAVE``SLAVE_ID.get_command_request() == REQ_WRITE) begin \
cmd.trans = WRITE; \
for(int i = 0; i < cmd.burstcount; i++) begin \
cmd.data[i] =`SLAVE``SLAVE_ID.get_command_data(i); \
cmd.byteenable[i] =`SLAVE``SLAVE_ID.get_command_byte_enable(i); \
end \
end else begin \
cmd.trans = READ; \
end \
\
return cmd; \
endfunction
`define MACRO_PENDING_READ_CYCLES(SLAVE_ID) \
int pending_read_cycles_slave_``SLAVE_ID = 0; \
always @(posedge `TB.clk_clk) begin \
if (pending_read_cycles_slave_``SLAVE_ID > 0) begin \
pending_read_cycles_slave_``SLAVE_ID--; \
end \
end
`define MACRO_CONFIGURE_AND_PUSH_RESPONSE_TO_SLAVE(SLAVE_ID) \
task automatic configure_and_push_response_to_slave_``SLAVE_ID ( \
Response rsp \
); \
\
int read_response_latency; \
\
`SLAVE``SLAVE_ID.set_response_request(REQ_READ); \
`SLAVE``SLAVE_ID.set_response_burst_size(rsp.burstcount); \
for (int i = 0; i < rsp.burstcount; i++) begin \
`SLAVE``SLAVE_ID.set_response_data(rsp.data[i], i); \
\
if (i == 0) begin \
`SLAVE``SLAVE_ID.set_response_latency(rsp.latency[i] + pending_read_cycles_slave_``SLAVE_ID, i); \
read_response_latency = rsp.latency[i]; \
end else begin \
`SLAVE``SLAVE_ID.set_response_latency(rsp.latency[i], i); \
read_response_latency = rsp.latency[i] + read_response_latency; \
end \
\
end \
`SLAVE``SLAVE_ID.push_response(); \
pending_read_cycles_slave_``SLAVE_ID = pending_read_cycles_slave_``SLAVE_ID + read_response_latency + rsp.burstcount + 1; \
endtask
`define MACRO_MASTER_RESPONSE_THREAD(MASTER_ID) \
// Get read response received by master and verify read response \
always @(`MASTER``MASTER_ID.signal_read_response_complete) begin \
\
Command cmd; \
Response actual_rsp, exp_rsp; \
\
cmd = read_command_queue_master[``MASTER_ID].pop_front(); \
actual_rsp = get_read_response_from_master_``MASTER_ID(); \
exp_rsp = get_expected_read_response(cmd); \
verify_response(actual_rsp, exp_rsp); \
end \
\
// Flush out response for write command created by master bfm \
always @(`MASTER``MASTER_ID.signal_write_response_complete) begin \
`MASTER``MASTER_ID.pop_response(); \
end
`define MACRO_GET_READ_RESPONSE_FROM_MASTER(MASTER_ID) \
function automatic Response get_read_response_from_master_``MASTER_ID (); \
\
Response rsp; \
\
`MASTER``MASTER_ID.pop_response(); \
rsp.burstcount = `MASTER``MASTER_ID.get_response_burst_size(); \
for (int i = 0; i < rsp.burstcount; i++) begin \
rsp.data[i] = `MASTER``MASTER_ID.get_response_data(i); \
end \
\
return rsp; \
endfunction
//---------------------------------------------------
// Macro Instantiations
//---------------------------------------------------
// master 0
`MACRO_CONFIGURE_AND_PUSH_COMMAND_TO_MASTER(0)
`MACRO_MASTER_RESPONSE_THREAD(0)
`MACRO_GET_READ_RESPONSE_FROM_MASTER(0)
// slave 0
`MACRO_SLAVE_THREAD(0)
`MACRO_GET_COMMAND_FROM_SLAVE(0)
`MACRO_PENDING_READ_CYCLES(0)
`MACRO_CONFIGURE_AND_PUSH_RESPONSE_TO_SLAVE(0)
// master 1
`MACRO_CONFIGURE_AND_PUSH_COMMAND_TO_MASTER(1)
`MACRO_MASTER_RESPONSE_THREAD(1)
`MACRO_GET_READ_RESPONSE_FROM_MASTER(1)
// slave 1
`MACRO_SLAVE_THREAD(1)
`MACRO_GET_COMMAND_FROM_SLAVE(1)
`MACRO_PENDING_READ_CYCLES(1)
`MACRO_CONFIGURE_AND_PUSH_RESPONSE_TO_SLAVE(1)
//---------------------------------------------------
// Test status checking
//---------------------------------------------------
bit test_success = 1;
//---------------------------------------------------
// Events
//---------------------------------------------------
event assert_fail;
//---------------------------------------------------
// Test program
//---------------------------------------------------
// master test program
initial begin
set_verbosity(VERBOSITY_INFO);
wait (`TB.reset_reset_n == 1);
$display("Starting master test program");
$display("Master sending out non bursting write commands");
master_send_commands(10, WRITE, NOBURST);
$display("Master sending out non bursting read commands");
master_send_commands(10, READ, NOBURST);
$display("Master sending out burst write commands");
master_send_commands(10, WRITE, BURST);
$display("Master sending out burst read commands");
master_send_commands(10, READ, BURST);
$display("Master has sent out all commands");
end
task automatic master_send_commands (
int num_command,
Transaction trans,
Burstmode burstmode
);
Command cmd;
Response rsp, exp_rsp;
int master_id, slave_id;
for (int i = 0; i < num_command; i++) begin
// specify which master and slave
master_id = $urandom_range(0,`NUM_MASTERS - 1);
slave_id = $urandom_range(0,`NUM_SLAVES - 1);
cmd = create_command (
.trans(trans),
.burstmode(burstmode),
.slave_id(slave_id)
);
queue_command(cmd, master_id, slave_id);
end
endtask
function automatic Command create_command (
Transaction trans,
Burstmode burstmode,
int slave_id
);
Command cmd;
if (burstmode == BURST) begin
cmd.burstcount = randomize_burstcount();
end else begin
cmd.burstcount = 1;
end
cmd.trans = trans;
cmd.addr = generate_random_aligned_address(slave_id);
cmd.cmd_delay = $urandom_range(0, MAX_COMMAND_IDLE);
if (trans == WRITE) begin
for (int i = 0; i < cmd.burstcount; i++) begin
cmd.data[i] = $random;
cmd.byteenable[i] = {NUM_SYMBOLS{1'b1}};
cmd.data_idles[i] = $urandom_range(0, MAX_DATA_IDLE);
end
end else begin
cmd.data_idles[0] = $urandom_range(0, MAX_DATA_IDLE);
end
return cmd;
endfunction
function automatic Burstcount randomize_burstcount ();
Burstcount burstcount;
burstcount = $urandom_range(1, MAX_BURST);
return burstcount;
endfunction
function automatic logic [ADDR_W-1: 0] generate_random_aligned_address (
int slave_id
);
logic [ADDR_W-1:0] base_addr, addr;
base_addr = slave_id * SLAVE_SPAN;
addr = base_addr + ($random % SLAVE_SPAN);
return (addr / NUM_SYMBOLS) * NUM_SYMBOLS;
endfunction
task automatic queue_command (
Command cmd,
int master_id,
int slave_id
);
save_command_master(cmd, master_id);
save_command_slave(cmd, slave_id);
configure_and_push_command_to_master(cmd, master_id);
endtask
task automatic save_command_master(
Command cmd,
int master_id
);
if (cmd.trans == WRITE) begin
write_command_queue_master[master_id].push_back(cmd);
end else begin
read_command_queue_master[master_id].push_back(cmd);
end
endtask
task automatic save_command_slave(
Command cmd,
int slave_id
);
cmd.addr = translate_master_to_slave_address(cmd.addr);
if (cmd.trans == WRITE) begin
write_command_queue_slave[slave_id].push_back(cmd);
end else begin
read_command_queue_slave[slave_id].push_back(cmd);
end
endtask
function automatic logic [ADDR_W-1: 0] translate_master_to_slave_address (
logic [ADDR_W-1: 0] addr
);
int slave_id = addr / SLAVE_SPAN;
logic [ADDR_W-1:0] base_addr, offset;
base_addr = slave_id * SLAVE_SPAN;
offset = addr - base_addr;
return (offset / NUM_SYMBOLS);
endfunction
task automatic configure_and_push_command_to_master (
Command cmd,
int master_id
);
if (master_id == 0) begin
configure_and_push_command_to_master_0(cmd);
end else if (master_id == 1) begin
configure_and_push_command_to_master_1(cmd);
end
endtask
function automatic Command get_expected_command_for_slave (
Command cmd,
int slave_id
);
Command exp_cmd;
int found = 0;
if (cmd.trans == WRITE) begin
foreach (write_command_queue_slave[slave_id,i]) begin
exp_cmd = write_command_queue_slave[slave_id][i];
if (exp_cmd.addr == cmd.addr) begin
write_command_queue_slave[slave_id].delete(i);
found = 1;
break;
end
end
if (found == 0) begin
exp_cmd = write_command_queue_slave[slave_id].pop_front();
end
end else begin
foreach (read_command_queue_slave[slave_id,i]) begin
exp_cmd = read_command_queue_slave[slave_id][i];
if (exp_cmd.addr == cmd.addr) begin
read_command_queue_slave[slave_id].delete(i);
found = 1;
break;
end
end
if (found == 0) begin
exp_cmd = read_command_queue_slave[slave_id].pop_front();
end
end
return exp_cmd;
endfunction
task automatic verify_command (
Command actual_cmd, exp_cmd
);
assert_equals("wrong address", exp_cmd.addr, actual_cmd.addr);
assert_equals("wrong burstcount", exp_cmd.burstcount, actual_cmd.burstcount);
if (actual_cmd.trans == WRITE) begin
for (int i = 0; i < actual_cmd.burstcount; i++) begin
assert_equals("wrong write data", exp_cmd.data[i], actual_cmd.data[i]);
assert_equals("wrong byteenable", exp_cmd.byteenable[i], actual_cmd.byteenable[i]);
end
end
endtask
task automatic assert_equals(
string message,
logic [1023:0] expected_obj,
logic [1023:0] actual_obj
);
string data_comparison_msg;
begin
if (actual_obj == expected_obj) begin
// Success case. Code it this way because in Verilog,
// 1) "!=" and "==" give 'x' if either operand contains 'x' or 'z'
// 2) 'x' evaluated as a boolean is false
end else begin
$sformat(data_comparison_msg, "%s, expected %0x got %0x",
message,
expected_obj,
actual_obj);
print(VERBOSITY_ERROR, data_comparison_msg);
test_success = 0;
-> assert_fail;
end
end
endtask
function automatic Response create_response (
Burstcount burstcount
);
Response rsp;
rsp.burstcount = burstcount;
for (int i = 0;i < burstcount; i++) begin
rsp.data[i] = $random;
rsp.latency[i] = $urandom_range(0, MAX_DATA_IDLE);
end
return rsp;
endfunction
function automatic Response get_expected_read_response (
Command cmd
);
Response rsp;
int slave_id = cmd.addr / SLAVE_SPAN;
rsp = read_response_queue_slave[slave_id].pop_front();
return rsp;
endfunction
task automatic verify_response (
Response actual_rsp, exp_rsp
);
assert_equals("wrong burstcount", exp_rsp.burstcount, actual_rsp.burstcount);
for (int i = 0; i < actual_rsp.burstcount; i++) begin
assert_equals("wrong read data", exp_rsp.data[i], actual_rsp.data[i]);
end
endtask
endmodule