Newer
Older

Dimitris Lampridis
committed
//------------------------------------------------------------------------------
// CERN BE-CO-HT
// GN4124 core for PCIe FMC carrier
// http://www.ohwr.org/projects/gn4124-core
//------------------------------------------------------------------------------
//
// unit name: main
//

Dimitris Lampridis
committed
// description: This is a simple example testbench, to demonstrate how to use

Dimitris Lampridis
committed
// 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

Dimitris Lampridis
committed
// 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.

Dimitris Lampridis
committed
//
//------------------------------------------------------------------------------
// 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;

Dimitris Lampridis
committed

Dimitris Lampridis
committed
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;

Dimitris Lampridis
committed
always #4ns clk_125m <= ~clk_125m;
always #23ns clk_62m5 <= ~clk_62m5;

Dimitris Lampridis
committed
logic rst_125m_n;
logic rst_gn4124_n;
logic clk_gn4124;
logic wb_dma_clk;
logic wb_dma_rst_n;
initial begin
rst_125m_n = 0;
end

Dimitris Lampridis
committed
IGN4124PCIMaster i_gn4124 ();

Dimitris Lampridis
committed
DUT (
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
.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)
);

Dimitris Lampridis
committed
assign wb_dma_clk = clk_125m;
assign wb_dma_rst_n = rst_125m_n;

Dimitris Lampridis
committed
xwb_dpram #
(

Dimitris Lampridis
committed
.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)
);

Dimitris Lampridis
committed

Dimitris Lampridis
committed
CBusAccessor acc;
task val_check(string name, uint32_t addr, val, expected);

Dimitris Lampridis
committed
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

Dimitris Lampridis
committed
endtask // val_check
task reg_check(uint32_t addr, expected);

Dimitris Lampridis
committed
uint64_t val;
acc.read(addr, val);
val_check("Register read-back", addr, val, expected);

Dimitris Lampridis
committed
endtask // reg_check

Dimitris Lampridis
committed
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

Dimitris Lampridis
committed
initial begin
int stall_cycle[14];
automatic int ntest = 1;
const int tests = 12;
uint32_t addr, val, expected;

Dimitris Lampridis
committed
@(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);

Dimitris Lampridis
committed
// Verify simple read/writes over wishbone

Dimitris Lampridis
committed
reg_check('h0, 'h0);
acc.write('h0c, 'hffacce55);
acc.write('h10, 'h1badcafe);

Dimitris Lampridis
committed
reg_check('h0c, 'hffacce55);
reg_check('h10, 'h1badcafe);

Dimitris Lampridis
committed

Dimitris Lampridis
committed
// Reset all DMA config registers

Dimitris Lampridis
committed
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: ",

Dimitris Lampridis
committed
if (dma_irq != 1'b0)
$fatal(1, "dma irq should be 0");
acc.write('h14, 'h80); // count
acc.write('h00, 'h01); // start

Dimitris Lampridis
committed
// 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");
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
// ---------------------------------
$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: ",

Dimitris Lampridis
committed
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

Dimitris Lampridis
committed
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;

Dimitris Lampridis
committed
for (addr = 'h00; addr < 'h40; addr += 1)

Dimitris Lampridis
committed
begin

Dimitris Lampridis
committed
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;
for (addr = 'h00; addr < 'h40; addr += 1)
begin
mem_check(4 * addr, expected);
end

Dimitris Lampridis
committed
repeat(4) @(posedge clk_125m);
$write("PASS\n");
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
// ---------------------------------
$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

Dimitris Lampridis
committed
acc.write('h20, 'h00); // attrib
acc.write('h0c, 'h20000000); // hstartL

Dimitris Lampridis
committed
acc.write('h10, 'h00000000); // hstartH
acc.write('h00, (i << 2) | 'h01); // start

Dimitris Lampridis
committed
@(posedge dma_irq);
check_irq_status;
for (addr = 'h00; addr < 'h800; addr += 1)
if (i == 1)
expected = {<<8{expected}};
else if (i == 2)
expected = {<<16{expected}};
else if (i == 3)
expected = {<<16{{<<8{expected}}}};

Dimitris Lampridis
committed
repeat(4) @(posedge clk_125m);
$write("PASS\n");
#1us;
end

Dimitris Lampridis
committed
$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");

Dimitris Lampridis
committed
$display();
$display("Simulation PASSED");
$finish;

Dimitris Lampridis
committed
endmodule // main