| //--------------------------------------------------- | |
| // 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 |