Skip to content
Snippets Groups Projects
main.sv 14.7 KiB
Newer Older
//------------------------------------------------------------------------------
// 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 #23ns clk_62m5 <= ~clk_62m5;
   logic rst_62m5_n;
   logic rst_gn4124_n;
   logic clk_gn4124;

   logic wb_dma_clk;
   logic wb_dma_rst_n;
      rst_62m5_n = 0;
      #80ns;
      rst_125m_n = 1;
      rst_62m5_n = 1;
   xwb_gn4124_core
          .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;
      .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)
        );
   task val_check(string name, uint32_t addr, 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
   task reg_check(uint32_t addr, expected);
      uint64_t val;
      acc.read(addr, val);
      val_check("Register read-back", addr, val, expected);
   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

      automatic int ntest = 1;

      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);
      acc.write('h0c, 'hffacce55);
      acc.write('h10, 'h1badcafe);
      reg_check('h0c, 'hffacce55);
      reg_check('h10, 'h1badcafe);
      for (addr = 'h00; addr <= 'h20; addr += 4)
      repeat(2) @(posedge clk_125m);

      // ---------------------------------
      $write("Test %0d/%0d: 128B read over DMA, abort after first read: ",
      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);

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

      // ---------------------------------
      $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): ",
         acc.write('h14, 'h2000); // count
         acc.write('h0c, 'h20000000); // hstartL
         acc.write('h10, 'h00000000); // hstartH
         acc.write('h00, (i << 2) | 'h01); // start

         for (addr = 'h00; addr < 'h800; addr += 1)
              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);
      $write("Test %0d/%0d: 256B DMA read with 32bit host address overflow: ",
      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");

   end // initial begin