//------------------------------------------------------------------------------ // 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; reg clk_62m5 = 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; always #23ns clk_62m5 <= ~clk_62m5; logic rst_125m_n; logic rst_62m5_n; logic rst_gn4124_n; logic clk_gn4124; logic wb_dma_clk; logic wb_dma_rst_n; initial begin rst_125m_n = 0; rst_62m5_n = 0; #80ns; rst_125m_n = 1; rst_62m5_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 (wb_dma_clk), .wb_dma_dat_rst_n_i (wb_dma_rst_n), .wb_dma_dat_i (wb_dma_in), .wb_dma_dat_o (wb_dma_out) ); assign wb_dma_clk = clk_125m; assign wb_dma_rst_n = rst_125m_n; xwb_dpram # ( .g_size (16384), .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 (wb_dma_rst_n), .clk_sys_i (wb_dma_clk), .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 int stall_cycle[14]; automatic int ntest = 1; const int tests = 12; 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 repeat(2) @(posedge clk_125m); $write("PASS\n"); // --------------------------------- $write("Test %0d/%0d: 128B read 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 // wait for transfer to start @(posedge i_gn4124.l2p_valid); 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: 256B DMA write: ", ntest++, tests); // Setup data in BFM memory for (addr = 'h00; addr < 'h40; addr += 1) i_gn4124.host_mem_write(4 * addr, 32'h80000020 - addr); // Setup DMA acc.write('h14, 'h100); // count acc.write('h20, 'h01); // attrib acc.write('h0c, 'h20000000); // hstartL acc.write('h10, 'h00000000); // hstartH acc.write('h00, 'h01); // start @(posedge dma_irq); check_irq_status; clear_irq; repeat(4) @(posedge clk_125m); $write("PASS\n"); // wait for WB transfer to finish #5us; // --------------------------------- $write("Test %0d/%0d: 2x128B chained DMA reads: ", ntest++, tests); // Setup DMA chain info in BFM memory i_gn4124.host_mem_write('h20000, 'h00000080); // remote address i_gn4124.host_mem_write('h20004, 'h20000080); // 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; clear_irq; for (addr = 'h00; addr < 'h40; addr += 1) begin expected = 32'h80000020 - addr; mem_check(4 * addr, expected); end repeat(4) @(posedge clk_125m); $write("PASS\n"); // --------------------------------- $write("Test %0d/%0d: 256B DMA read: ", 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; clear_irq; for (addr = 'h00; addr < 'h40; addr += 1) begin expected = 32'h80000020 - addr; mem_check(4 * addr, expected); end repeat(4) @(posedge clk_125m); $write("PASS\n"); // --------------------------------- $write("Test %0d/%0d: 256B DMA reads with stalling: ", ntest++, tests); // Setup DMA acc.write('h14, 'h100); // count acc.write('h20, 'h00); // attrib acc.write('h0c, 'h20000000); // hstartL acc.write('h10, 'h00000000); // hstartH stall_cycle = '{0,1,2,14,15,16,17,30,31,32,33,61,62,63}; foreach(stall_cycle[i]) begin acc.write('h00, 'h01); // start @(posedge wb_dma_out.stb); repeat(stall_cycle[i]) @(posedge clk_125m); force DUT.cmp_wrapped_gn4124.dma_stall_i = 1'b1; @(posedge clk_125m); force DUT.cmp_wrapped_gn4124.dma_ack_i = 1'b0; @(posedge clk_125m); release DUT.cmp_wrapped_gn4124.dma_stall_i; @(posedge clk_125m); release DUT.cmp_wrapped_gn4124.dma_ack_i; @(posedge dma_irq); check_irq_status; clear_irq; for (addr = 'h00; addr < 'h40; addr += 1) begin expected = 32'h80000020 - addr; mem_check(4 * addr, expected); end repeat(4) @(posedge clk_125m); end $write("PASS\n"); // --------------------------------- $write("Test %0d/%0d: 8KiB chained DMA write: ", ntest++, tests); // Setup data in BFM memory for (addr = 'h00; addr < 'h800; addr += 1) i_gn4124.host_mem_write(4 * addr, 32'h80000020 - addr); // Setup DMA chain info in BFM memory i_gn4124.host_mem_write('h20000, 'h00001800); // remote address i_gn4124.host_mem_write('h20004, 'h20001800); // hstartL i_gn4124.host_mem_write('h20008, 'h00000000); // hstartH i_gn4124.host_mem_write('h2000C, 'h800); // count i_gn4124.host_mem_write('h20010, 'h00); // nextL i_gn4124.host_mem_write('h20014, 'h00); // nextH i_gn4124.host_mem_write('h20018, 'h01); // attrib // Setup DMA acc.write('h14, 'h1800); // count acc.write('h20, 'h03); // 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; clear_irq; repeat(4) @(posedge clk_125m); $write("PASS\n"); // wait for WB transfer to finish #5us; // Check all four byte swap settings // --------------------------------- for (int i = 0; i < 4; i++) begin $write("Test %0d/%0d: 8KiB DMA read (byte swap = %0d): ", ntest++, tests, i); // Restart acc.write('h14, 'h2000); // count acc.write('h20, 'h00); // attrib acc.write('h0c, 'h20000000); // hstartL acc.write('h10, 'h00000000); // hstartH acc.write('h00, (i << 2) | 'h01); // start @(posedge dma_irq); check_irq_status; for (addr = 'h00; addr < 'h800; addr += 1) begin expected = 32'h80000020 - addr; if (i == 1) expected = {<<8{expected}}; else if (i == 2) expected = {<<16{expected}}; else if (i == 3) expected = {<<16{{<<8{expected}}}}; mem_check(4 * addr, expected); end clear_irq; repeat(4) @(posedge clk_125m); $write("PASS\n"); #1us; end $write("Test %0d/%0d: 256B DMA read with 32bit host address overflow: ", ntest++, tests); acc.write('h14, 'h100); // count acc.write('h20, 'h00); // attrib acc.write('h0c, 'hffffff80); // hstartL acc.write('h10, 'h00000000); // hstartH acc.write('h00, 'h01); // start // Transfer will be split internally by L2P DMA master in two requests, the first // one with a 32-bit adress starting at ffff_ff80 and the next one with a 64-bit // address starting at 1_0000_0000 @(posedge DUT.cmp_wrapped_gn4124.ldm_arb_dframe); @(posedge DUT.cmp_wrapped_gn4124.sys_clk); val_check("Host address overflow header", 1, DUT.cmp_wrapped_gn4124.ldm_arb_data, 'h02ff0020); @(posedge DUT.cmp_wrapped_gn4124.sys_clk); val_check("Host address overflow address", 1, DUT.cmp_wrapped_gn4124.ldm_arb_data, 'hffffff80); @(posedge DUT.cmp_wrapped_gn4124.ldm_arb_dframe); @(posedge DUT.cmp_wrapped_gn4124.sys_clk); val_check("Host address overflow header", 2, DUT.cmp_wrapped_gn4124.ldm_arb_data, 'h03ff0020); @(posedge DUT.cmp_wrapped_gn4124.sys_clk); val_check("Host address overflow address high", 2, DUT.cmp_wrapped_gn4124.ldm_arb_data, 1); @(posedge DUT.cmp_wrapped_gn4124.sys_clk); val_check("Host address overflow address low", 2, DUT.cmp_wrapped_gn4124.ldm_arb_data, 0); @(posedge dma_irq); // Check irq status reg_check('h04, 'h04); if (dma_irq != 1'b1) $fatal(1, "dma irq should be 1"); // clear irq acc.write('h04, 'h04); reg_check('h04, 'h00); if (dma_irq != 1'b0) $fatal(1, "dma irq should be 0"); repeat(4) @(posedge clk_125m); $write("PASS\n"); $display(); $display("Simulation PASSED"); $finish; end // initial begin endmodule // main