// ---------------------------------------------------------------- // qsys_system_tb.vhd // // 6/3/2015 D. W. Hawkins (dwh@caltech.edu) // // Altera Avalon-MM Master BFM testbench. // // The Qsys system contents are; // * Avalon-MM Master BFM // * PIO control register (output) // * PIO status register (input) // * On-Chip RAM (4kB total) // // The testbench uses the master BFM to access each of these // slave devices. // // ---------------------------------------------------------------- // Modelsim-ASE requires a timescale directive `timescale 1 ps / 1 ps // Console messaging level `define VERBOSITY VERBOSITY_INFO // Path to the BFM `define BFM u1.bfm_master // BFM related parameters // (look at the parameters in bfm_master.v) `define AV_ADDRESS_W 32 `define AV_SYMBOL_W 8 `define AV_NUMSYMBOLS 4 // Derived parameters `define AV_DATA_W (`AV_SYMBOL_W * `AV_NUMSYMBOLS) //----------------------------------------------------------------- // The testbench //----------------------------------------------------------------- // module qsys_system_tb #( // Clock frequency parameter real CLK_FREQUENCY = 50.0e6 ); // ------------------------------------------------------------ // Local parameters // ------------------------------------------------------------ // // Clock period localparam time CLK_PERIOD = (1.0e9/CLK_FREQUENCY)*1ns; // Address map (per Qsys settings) localparam integer PIO_CONTROL = 'h00000000; localparam integer PIO_STATUS = 'h00000010; localparam integer ONCHIP_RAM = 'h00001000; // ------------------------------------------------------------ // Packages // ------------------------------------------------------------ // import verbosity_pkg::*; import avalon_mm_pkg::*; // ------------------------------------------------------------ // Local variables and signals // ------------------------------------------------------------ // // Top-level signals logic clk; logic rstN; // Control/status signals logic [31:0] status; logic [31:0] control; // Test number // * global so that it can be displayed in // the Modelsim wave window integer test_number = 0; // ------------------------------------------------------------ // Clock generator // ------------------------------------------------------------ // initial clk = 1'b0; always #(CLK_PERIOD/2) clk <= ~clk; // ------------------------------------------------------------ // Device under test // ------------------------------------------------------------ // qsys_system u1 ( .reset_reset_n (rstN), .clk_clk (clk), .control_export(control), .status_export (status) ); // ------------------------------------------------------------ // Test stimulus // ------------------------------------------------------------ initial begin // Master variables logic [`AV_ADDRESS_W-1:0] addr; logic [`AV_DATA_W-1:0] rdsingle, wrsingle; logic [`AV_DATA_W-1:0] rdburst [0:7]; logic [`AV_DATA_W-1:0] wrburst [0:7]; int len, offset; // -------------------------------------------------------- // Start message // -------------------------------------------------------- // $display(""); $display("==============================================================="); $display("Qsys System Testbench"); $display("==============================================================="); $display(""); // -------------------------------------------------------- // Signal defaults // -------------------------------------------------------- // rstN = 0; status = 0; // -------------------------------------------------------- // Initialize the master BFM // -------------------------------------------------------- // set_verbosity(`VERBOSITY); `BFM.init(); // -------------------------------------------------------- // Deassert reset // -------------------------------------------------------- // $display("Deassert reset"); #100ns rstN = 1; #(10*CLK_PERIOD); // -------------------------------------------------------- $display(""); test_number = test_number + 1; $display("-----------------------------------------------"); $display(" #%1d: Control register tests", test_number); $display("-----------------------------------------------"); // -------------------------------------------------------- // $display("Walking 1's test"); addr = PIO_CONTROL; for (int i = 0; i < 32; i++) begin wrsingle = 1 << i; // Control register write $display("Write (%.8Xh, %.8Xh)", addr, wrsingle); avalon_write_single(addr, wrsingle); // Read and check the data avalon_read_single(addr, rdsingle); assert (rdsingle == wrsingle) else $error("Error: control register data mismatch!"); // Read and check the control signal assert (rdsingle == control) else $error("Error: control data mismatch!"); end; $display("Control register tests passed!"); // Delay between tests #(10*CLK_PERIOD); // -------------------------------------------------------- $display(""); test_number = test_number + 1; $display("-----------------------------------------------"); $display(" #%1d: Status register tests", test_number); $display("-----------------------------------------------"); // -------------------------------------------------------- // // Read from the status register $display("Walking 1's test"); addr = PIO_STATUS; for (int i = 0; i < 32; i++) begin wrsingle = 1 << i; status = wrsingle; // Status register read avalon_read_single(addr, rdsingle); $display("Read (%.8Xh, %.8Xh)", addr, rdsingle); // Check the data assert (rdsingle == wrsingle) else $error("Error: status register data mismatch!"); end; $display("Status register tests passed!"); // Delay between tests #(10*CLK_PERIOD); // -------------------------------------------------------- $display(""); test_number = test_number + 1; $display("-----------------------------------------------"); $display(" #%1d: On-chip RAM single transactions tests", test_number); $display("-----------------------------------------------"); // -------------------------------------------------------- // // Write to multiple locations in RAM // * the RAM is 1024 x 32-bit (4096-bytes total) // * test the first 64 locations $display("Write an incrementing data pattern"); for (int i = 0; i < 64; i++) begin // Address and write data addr = ONCHIP_RAM + 4*i; wrsingle = 32'h12340000 + i; // RAM write $display("Write (%.8Xh, %.8Xh)", addr, wrsingle); avalon_write_single(addr, wrsingle); end; $display("Read and check for an incrementing data pattern"); for (int i = 0; i < 64; i++) begin // Address and write data addr = ONCHIP_RAM + 4*i; wrsingle = 32'h12340000 + i; // RAM read avalon_read_single(addr, rdsingle); $display("Read (%.8Xh, %.8Xh)", addr, rdsingle); // Check the data assert (rdsingle == wrsingle) else $error("Error: RAM data mismatch!"); end; $display("RAM single write/read checks passed!"); // Delay between tests #(10*CLK_PERIOD); // -------------------------------------------------------- $display(""); test_number = test_number + 1; $display("-----------------------------------------------"); $display(" #%1d: On-chip RAM burst transactions tests", test_number); $display("-----------------------------------------------"); // -------------------------------------------------------- // // Write to multiple locations in RAM // * the RAM is 1024 x 32-bit (4096-bytes total) // * test the first 64 locations $display("Write an incrementing data pattern"); for (int i = 0; i < 8; i++) begin // Create the burst write data addr = ONCHIP_RAM + 8*4*i; for (int j = 0; j < 8; j++) begin wrburst[j] = 32'h56780000 + 8*i + j; // Print the write data $display("Write (%.8Xh, %.8Xh)", addr + 4*j, wrburst[j]); end; // RAM burst write avalon_write_burst(addr, wrburst); end; $display("Read and check for an incrementing data pattern"); for (int i = 0; i < 8; i++) begin // Address and burst write (expected read) data addr = ONCHIP_RAM + 8*4*i; for (int j = 0; j < 8; j++) begin wrburst[j] = 32'h56780000 + 8*i + j; end; // RAM burst read avalon_read_burst(addr, rdburst); // Check the data for (int j = 0; j < 8; j++) begin // Print the read data $display("Read (%.8Xh, %.8Xh)", addr + 4*j, rdburst[j]); // Check the read data assert (rdburst[j] == wrburst[j]) else $error("Error: RAM data mismatch!"); end; end; $display("RAM burst write/read checks passed!"); // Delay between tests #(10*CLK_PERIOD); // -------------------------------------------------------- $display(""); $display("==============================================="); $display("Simulation complete."); $display("==============================================="); $display(""); // -------------------------------------------------------- $stop; end // ============================================================ // Avalon-MM read/write single/burst tasks // ============================================================ // // SystemVerilog does not support overloaded tasks // (same task name, different task argument types), so // the function names are unique. // // Avalon-MM single-transaction read and write procedures. // // ------------------------------------------------------------ task avalon_write_single ( // ------------------------------------------------------------ input [`AV_ADDRESS_W-1:0] addr, input [`AV_DATA_W-1:0] data ); begin // Construct the BFM request `BFM.set_command_request(REQ_WRITE); `BFM.set_command_idle(0, 0); `BFM.set_command_init_latency(0); `BFM.set_command_address(addr); `BFM.set_command_burst_size(1); `BFM.set_command_burst_count(1); `BFM.set_command_data(data, 0); `BFM.set_command_byte_enable('1,0); // Queue the command `BFM.push_command(); // Wait until the transaction has completed @(`BFM.signal_write_response_complete); // Dequeue the response and discard `BFM.pop_response(); end endtask // ------------------------------------------------------------ task avalon_read_single ( // ------------------------------------------------------------ input [`AV_ADDRESS_W-1:0] addr, output [`AV_DATA_W-1:0] data ); begin // Construct the BFM request `BFM.set_command_request(REQ_READ); `BFM.set_command_idle(0, 0); `BFM.set_command_init_latency(0); `BFM.set_command_address(addr); `BFM.set_command_burst_size(1); `BFM.set_command_burst_count(1); `BFM.set_command_data(0, 0); `BFM.set_command_byte_enable('1,0); // Queue the command `BFM.push_command(); // Wait until the transaction has completed @(`BFM.signal_read_response_complete); // Dequeue the response and return the data `BFM.pop_response(); data = `BFM.get_response_data(0); end endtask // // ------------------------------------------------------------ // // Avalon-MM burst-transaction read and write procedures. // // ------------------------------------------------------------ task avalon_write_burst ( // ------------------------------------------------------------ input [`AV_ADDRESS_W-1:0] addr, input [`AV_DATA_W-1:0] data [0:7] ); begin // Construct the BFM request `BFM.set_command_request(REQ_WRITE); `BFM.set_command_idle(0, 0); `BFM.set_command_init_latency(0); `BFM.set_command_address(addr); `BFM.set_command_burst_size(8); `BFM.set_command_burst_count(8); for (int i = 0; i < 8; i++) begin `BFM.set_command_data(data[i], i); `BFM.set_command_byte_enable('1,i); end // Queue the command `BFM.push_command(); // Wait until the transaction has completed @(`BFM.signal_write_response_complete); // Dequeue the response and discard `BFM.pop_response(); end endtask // ------------------------------------------------------------ task avalon_read_burst ( // ------------------------------------------------------------ input [`AV_ADDRESS_W-1:0] addr, output [`AV_DATA_W-1:0] data [0:7] ); begin // Construct the BFM request `BFM.set_command_request(REQ_READ); `BFM.set_command_idle(0, 0); `BFM.set_command_init_latency(0); `BFM.set_command_address(addr); `BFM.set_command_burst_size(8); `BFM.set_command_burst_count(8); `BFM.set_command_data(0, 0); `BFM.set_command_byte_enable('1,0); // Queue the command `BFM.push_command(); // Wait until the transaction has completed @(`BFM.signal_read_response_complete); // Dequeue the response and return the data `BFM.pop_response(); for (int i = 0; i < 8; i++) data[i] = `BFM.get_response_data(i); end endtask endmodule