//------------------------------------------------------------------------------ // CERN BE-CO-HT // GN4124 core for PCIe FMC carrier // http://www.ohwr.org/projects/gn4124-core //------------------------------------------------------------------------------ // // unit name: main // // description: This is a simple example testbench, to demonstrate how to use // the SystemVerilog BFM of the GN4124 to perform simple accesses over wishbone. // // The testbench simply connects the wishbone master of the GN4124 to its own // DMA configuration wishbone slave and attaches a pre-initialised dummy RAM // with a wishbone interface to the pipelined DMA interface in order to perform // a DMA read. // //------------------------------------------------------------------------------ // Copyright CERN 2019 //------------------------------------------------------------------------------ // Copyright and related rights are licensed under the Solderpad Hardware // License, Version 2.0 (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // http://solderpad.org/licenses/SHL-2.0. // Unless required by applicable law or agreed to in writing, software, // hardware and materials distributed under this License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express // or implied. See the License for the specific language governing permissions // and limitations under the License. //------------------------------------------------------------------------------ `timescale 1ns/1ps `include "gn4124_bfm.svh" import wishbone_pkg::*; module main; reg clk_125m = 0; logic gn4124_irq; t_wishbone_master_in wb_in, wb_dma_in, wb_mem_in; t_wishbone_master_out wb_out, wb_dma_out, wb_mem_out; always #4ns clk_125m <= ~clk_125m; logic rst_125m_n; initial begin rst_125m_n = 0; #80ns rst_125m_n = 1; end IGN4124PCIMaster i_gn4124 (); xwb_gn4124_core DUT ( .rst_n_a_i (i_gn4124.rst_n), .p2l_clk_p_i (i_gn4124.p2l_clk_p), .p2l_clk_n_i (i_gn4124.p2l_clk_n), .p2l_data_i (i_gn4124.p2l_data), .p2l_dframe_i (i_gn4124.p2l_dframe), .p2l_valid_i (i_gn4124.p2l_valid), .p2l_rdy_o (i_gn4124.p2l_rdy), .p_wr_req_i (i_gn4124.p_wr_req), .p_wr_rdy_o (i_gn4124.p_wr_rdy), .rx_error_o (i_gn4124.rx_error), .vc_rdy_i (i_gn4124.vc_rdy), .l2p_clk_p_o (i_gn4124.l2p_clk_p), .l2p_clk_n_o (i_gn4124.l2p_clk_n), .l2p_data_o (i_gn4124.l2p_data), .l2p_dframe_o (i_gn4124.l2p_dframe), .l2p_valid_o (i_gn4124.l2p_valid), .l2p_edb_o (i_gn4124.l2p_edb), .l2p_rdy_i (i_gn4124.l2p_rdy), .l_wr_rdy_i (i_gn4124.l_wr_rdy), .p_rd_d_rdy_i (i_gn4124.p_rd_d_rdy), .tx_error_i (i_gn4124.tx_error), .dma_irq_o (dma_irq), .irq_p_i (1'b0), .irq_p_o (gn4124_irq), .status_o (), .wb_master_clk_i (clk_125m), .wb_master_rst_n_i (rst_125m_n), .wb_master_i (wb_in), .wb_master_o (wb_out), .wb_dma_cfg_clk_i (clk_125m), .wb_dma_cfg_rst_n_i (rst_125m_n), .wb_dma_cfg_i (wb_out), .wb_dma_cfg_o (wb_in), .wb_dma_dat_clk_i (clk_125m), .wb_dma_dat_rst_n_i (rst_125m_n), .wb_dma_dat_i (wb_dma_in), .wb_dma_dat_o (wb_dma_out) ); xwb_dpram # ( .g_size (32), .g_init_file ("mem_init.bram"), .g_slave1_interface_mode (1), // 1 = PIPELINED .g_slave2_interface_mode (1), .g_slave1_granularity (1), // 1 = WORD .g_slave2_granularity (1) ) MEM ( .rst_n_i (1'b1), .clk_sys_i (clk_125m), .slave1_i (wb_dma_out), .slave1_o (wb_dma_in), .slave2_i (wb_mem_out), .slave2_o (wb_mem_in) ); CBusAccessor acc; task val_check(string name, uint32_t addr, val, expected); if (val != expected) begin $display(); $display("Simulation FAILED"); $fatal(1, "%s error at address 0x%.2x. Expected 0x%.8x, got 0x%.8x", name, addr, expected, val); end endtask // val_check task reg_check(uint32_t addr, expected); uint64_t val; acc.read(addr, val); val_check("Register read-back", addr, val, expected); endtask // reg_check task mem_check(uint32_t addr, expected); uint64_t val; i_gn4124.host_mem_read(addr, val); val_check("Memory read-back", addr, val, expected); endtask // reg_check task check_irq_status; // Check irq status reg_check('h04, 'h04); if (dma_irq != 1'b1) $fatal(1, "dma irq should be 1"); endtask task clear_irq; acc.write('h04, 'h04); reg_check('h04, 'h00); if (dma_irq != 1'b0) $fatal(1, "dma irq should be 0"); endtask initial begin automatic int ntest = 1; const int tests = 7; uint32_t addr, val, expected; @(posedge i_gn4124.ready); acc = i_gn4124.get_accessor(); acc.set_default_xfer_size(4); @(posedge clk_125m); $write("Test %0d/%0d: simple read/write accesses over Wishbone: ", ntest++, tests); // Verify simple read/writes over wishbone reg_check('h0, 'h0); acc.write('h0c, 'hffacce55); acc.write('h10, 'h1badcafe); reg_check('h0c, 'hffacce55); reg_check('h10, 'h1badcafe); // Reset all DMA config registers for (addr = 'h00; addr <= 'h20; addr += 4) begin acc.write(addr, 'h0); end $write("PASS\n"); $write("Test %0d/%0d: 32 reads over DMA, abort after first read: ", ntest++, tests); if (dma_irq != 1'b0) $fatal(1, "dma irq should be 0"); acc.write('h14, 'h80); // count acc.write('h00, 'h01); // start // Check values read from memory @(posedge i_gn4124.l2p_valid); // skip header @(posedge i_gn4124.l2p_valid); expected = 32'h8000001f; val = i_gn4124.l2p_data; @(posedge i_gn4124.l2p_clk_n); val |= i_gn4124.l2p_data << 16; val_check("DMA read-back", 'h20, val, expected); repeat(2) @(posedge clk_125m); // Test abort feature acc.write('h00, 'h02); reg_check('h04, 'h03); acc.write('h00, 'h00); repeat(2) @(posedge clk_125m); $write("PASS\n"); $write("Test %0d/%0d: 2x32 chained reads over DMA: ", ntest++, tests); // Setup DMA chain info in BFM memory i_gn4124.host_mem_write('h20000, 'h00000000); // remote address i_gn4124.host_mem_write('h20004, 'h20000100); // hstartL i_gn4124.host_mem_write('h20008, 'h00000000); // hstartH i_gn4124.host_mem_write('h2000C, 'h80); // count i_gn4124.host_mem_write('h20010, 'h00); // nextL i_gn4124.host_mem_write('h20014, 'h00); // nextH i_gn4124.host_mem_write('h20018, 'h00); // attrib acc.write('h14, 'h80); // count acc.write('h20, 'h02); // attrib acc.write('h0c, 'h20000000); // hstartL acc.write('h10, 'h00000000); // hstartH // Point to chain info in BFM memory acc.write('h18, 'h20020000); // nextL acc.write('h1C, 'h00000000); // nextH acc.write('h00, 'h01); // start @(posedge dma_irq); check_irq_status; for (addr = 'h00; addr < 'h20; addr += 1) begin expected = 32'h80000000 + 'h20 - addr - 1; mem_check(4 * addr, expected); mem_check('h100 + 4 * addr, expected); end clear_irq; repeat(4) @(posedge clk_125m); $write("PASS\n"); // --------------------------------- $write("Test %0d/%0d: 128 reads over DMA: ", ntest++, tests); // Setup DMA acc.write('h14, 'h100); // count acc.write('h20, 'h00); // attrib acc.write('h0c, 'h20000000); // hstartL acc.write('h10, 'h00000000); // hstartH acc.write('h00, 'h01); // start @(posedge dma_irq); check_irq_status; for (addr = 'h00; addr < 'h40; addr += 1) begin expected = 32'h80000000 + 'h20 - addr - 1; mem_check(4 * addr, expected); end clear_irq; repeat(4) @(posedge clk_125m); $write("PASS\n"); // Check all four byte swap settings // --------------------------------- for (int i = 0; i < 4; i++) begin $write("Test %0d/%0d: 16KB reads over DMA (byte swap = %0d): ", ntest++, tests, i); // Restart acc.write('h14, 'h4000); // count acc.write('h20, 'h00); // attrib acc.write('h0c, 'h20000000 + i * 'h1000); // hstartL acc.write('h10, 'h00000000); // hstartH acc.write('h00, (i << 2) | 'h01); // start @(posedge dma_irq); check_irq_status; for (addr = 'h00; addr < 'h1000; addr += 1) begin expected = 32'h80000000 + 'h20 - (addr % 'h20) - 1; $write("ex: %x", expected); if (i == 1) expected = {<<8{expected}}; else if (i == 2) expected = {<<16{expected}}; else if (i == 3) expected = {<<16{{<<8{expected}}}}; $display("ex: %x", expected); mem_check((i * 'h1000) + 4 * addr, expected); end clear_irq; repeat(4) @(posedge clk_125m); $write("PASS\n"); #1us; end $display(); $display("Simulation PASSED"); $finish; end endmodule // main