diff --git a/fpga/app/dma_bench/rtl/dram_test_ch.v b/fpga/app/dma_bench/rtl/dram_test_ch.v new file mode 100644 index 000000000..f13a3db48 --- /dev/null +++ b/fpga/app/dma_bench/rtl/dram_test_ch.v @@ -0,0 +1,656 @@ +/* + +Copyright 2023, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`resetall +`timescale 1ns / 1ps +`default_nettype none + +/* + * DRAM test channel + */ +module dram_test_ch # +( + // AXI configuration + parameter AXI_DATA_WIDTH = 32, + parameter AXI_ADDR_WIDTH = 16, + parameter AXI_STRB_WIDTH = (AXI_DATA_WIDTH/8), + parameter AXI_ID_WIDTH = 8, + parameter AXI_MAX_BURST_LEN = 16, + + // FIFO config + parameter FIFO_BASE_ADDR = 0, + parameter FIFO_SIZE_MASK = {AXI_ADDR_WIDTH{1'b1}}, + + // Register interface + parameter REG_ADDR_WIDTH = 7, + parameter REG_DATA_WIDTH = 32, + parameter REG_STRB_WIDTH = (REG_DATA_WIDTH/8), + parameter RB_BASE_ADDR = 0, + parameter RB_NEXT_PTR = 0 +) +( + input wire clk, + input wire rst, + + /* + * Register interface + */ + input wire [REG_ADDR_WIDTH-1:0] reg_wr_addr, + input wire [REG_DATA_WIDTH-1:0] reg_wr_data, + input wire [REG_STRB_WIDTH-1:0] reg_wr_strb, + input wire reg_wr_en, + output wire reg_wr_wait, + output wire reg_wr_ack, + input wire [REG_ADDR_WIDTH-1:0] reg_rd_addr, + input wire reg_rd_en, + output wire [REG_DATA_WIDTH-1:0] reg_rd_data, + output wire reg_rd_wait, + output wire reg_rd_ack, + + /* + * AXI master interface + */ + input wire m_axi_clk, + input wire m_axi_rst, + output wire [AXI_ID_WIDTH-1:0] m_axi_awid, + output wire [AXI_ADDR_WIDTH-1:0] m_axi_awaddr, + output wire [7:0] m_axi_awlen, + output wire [2:0] m_axi_awsize, + output wire [1:0] m_axi_awburst, + output wire m_axi_awlock, + output wire [3:0] m_axi_awcache, + output wire [2:0] m_axi_awprot, + output wire m_axi_awvalid, + input wire m_axi_awready, + output wire [AXI_DATA_WIDTH-1:0] m_axi_wdata, + output wire [AXI_STRB_WIDTH-1:0] m_axi_wstrb, + output wire m_axi_wlast, + output wire m_axi_wvalid, + input wire m_axi_wready, + input wire [AXI_ID_WIDTH-1:0] m_axi_bid, + input wire [1:0] m_axi_bresp, + input wire m_axi_bvalid, + output wire m_axi_bready, + output wire [AXI_ID_WIDTH-1:0] m_axi_arid, + output wire [AXI_ADDR_WIDTH-1:0] m_axi_araddr, + output wire [7:0] m_axi_arlen, + output wire [2:0] m_axi_arsize, + output wire [1:0] m_axi_arburst, + output wire m_axi_arlock, + output wire [3:0] m_axi_arcache, + output wire [2:0] m_axi_arprot, + output wire m_axi_arvalid, + input wire m_axi_arready, + input wire [AXI_ID_WIDTH-1:0] m_axi_rid, + input wire [AXI_DATA_WIDTH-1:0] m_axi_rdata, + input wire [1:0] m_axi_rresp, + input wire m_axi_rlast, + input wire m_axi_rvalid, + output wire m_axi_rready +); + +localparam RBB = RB_BASE_ADDR & {REG_ADDR_WIDTH{1'b1}}; + +localparam LANE_WIDTH = 32; +localparam LANE_COUNT = (AXI_DATA_WIDTH + LANE_WIDTH-1) / LANE_WIDTH; +localparam LANE_ERR_CNT_WIDTH = $clog2(LANE_WIDTH)+1; + +// check configuration +initial begin + if (REG_DATA_WIDTH != 32) begin + $error("Error: Register interface width must be 32 (instance %m)"); + $finish; + end + + if (REG_STRB_WIDTH * 8 != REG_DATA_WIDTH) begin + $error("Error: Register interface requires byte (8-bit) granularity (instance %m)"); + $finish; + end + + if (REG_ADDR_WIDTH < 7) begin + $error("Error: Register address width too narrow (instance %m)"); + $finish; + end + + if (RB_NEXT_PTR && RB_NEXT_PTR >= RB_BASE_ADDR && RB_NEXT_PTR < RB_BASE_ADDR + 7'h60) begin + $error("Error: RB_NEXT_PTR overlaps block (instance %m)"); + $finish; + end +end + +// control registers +reg reg_wr_ack_reg = 1'b0, reg_wr_ack_next; +reg [REG_DATA_WIDTH-1:0] reg_rd_data_reg = 0, reg_rd_data_next; +reg reg_rd_ack_reg = 1'b0, reg_rd_ack_next; + +reg [AXI_ADDR_WIDTH-1:0] fifo_base_addr_reg = FIFO_BASE_ADDR, fifo_base_addr_next; +reg [AXI_ADDR_WIDTH-1:0] fifo_size_mask_reg = FIFO_SIZE_MASK, fifo_size_mask_next; +reg fifo_enable_reg = 1'b0, fifo_enable_next; +reg fifo_reset_reg = 1'b0, fifo_reset_next; + +(* shreg_extract = "no" *) +reg [AXI_ADDR_WIDTH-1:0] fifo_base_addr_sync_1_reg = FIFO_BASE_ADDR; +(* shreg_extract = "no" *) +reg [AXI_ADDR_WIDTH-1:0] fifo_base_addr_sync_2_reg = FIFO_BASE_ADDR; +(* shreg_extract = "no" *) +reg [AXI_ADDR_WIDTH-1:0] fifo_size_mask_sync_1_reg = FIFO_SIZE_MASK; +(* shreg_extract = "no" *) +reg [AXI_ADDR_WIDTH-1:0] fifo_size_mask_sync_2_reg = FIFO_SIZE_MASK; +(* shreg_extract = "no" *) +reg fifo_enable_sync_1_reg = 1'b0; +(* shreg_extract = "no" *) +reg fifo_enable_sync_2_reg = 1'b0; +(* shreg_extract = "no" *) +reg fifo_reset_sync_1_reg = 1'b0; +(* shreg_extract = "no" *) +reg fifo_reset_sync_2_reg = 1'b0; + +always @(posedge m_axi_clk) begin + fifo_base_addr_sync_1_reg <= fifo_base_addr_reg; + fifo_base_addr_sync_2_reg <= fifo_base_addr_sync_1_reg; + fifo_size_mask_sync_1_reg <= fifo_size_mask_reg; + fifo_size_mask_sync_2_reg <= fifo_size_mask_sync_1_reg; + fifo_enable_sync_1_reg <= fifo_enable_reg; + fifo_enable_sync_2_reg <= fifo_enable_sync_1_reg; + fifo_reset_sync_1_reg <= fifo_reset_reg; + fifo_reset_sync_2_reg <= fifo_reset_sync_1_reg; + + if (m_axi_rst) begin + fifo_base_addr_sync_1_reg <= FIFO_BASE_ADDR; + fifo_base_addr_sync_2_reg <= FIFO_BASE_ADDR; + fifo_size_mask_sync_1_reg <= FIFO_SIZE_MASK; + fifo_size_mask_sync_2_reg <= FIFO_SIZE_MASK; + fifo_enable_sync_1_reg <= 1'b0; + fifo_enable_sync_2_reg <= 1'b0; + fifo_reset_sync_1_reg <= 1'b0; + fifo_reset_sync_2_reg <= 1'b0; + end +end + +wire [AXI_ADDR_WIDTH+1-1:0] fifo_occupancy; +wire fifo_reset_status; +wire fifo_active; + +reg [AXI_ADDR_WIDTH+1-1:0] fifo_occupancy_reg = 0; +reg fifo_reset_status_reg = 1'b0; +reg fifo_active_reg = 1'b0; + +(* shreg_extract = "no" *) +reg [AXI_ADDR_WIDTH+1-1:0] fifo_occupancy_sync_1_reg = 0; +(* shreg_extract = "no" *) +reg [AXI_ADDR_WIDTH+1-1:0] fifo_occupancy_sync_2_reg = 0; +(* shreg_extract = "no" *) +reg fifo_reset_status_sync_1_reg = 1'b0; +(* shreg_extract = "no" *) +reg fifo_reset_status_sync_2_reg = 1'b0; +(* shreg_extract = "no" *) +reg fifo_active_sync_1_reg = 1'b0; +(* shreg_extract = "no" *) +reg fifo_active_sync_2_reg = 1'b0; + +always @(posedge m_axi_clk) begin + fifo_occupancy_reg <= fifo_occupancy; + fifo_reset_status_reg <= fifo_reset_status; + fifo_active_reg <= fifo_active; +end + +always @(posedge clk) begin + fifo_occupancy_sync_1_reg <= fifo_occupancy_reg; + fifo_occupancy_sync_2_reg <= fifo_occupancy_sync_1_reg; + fifo_reset_status_sync_1_reg <= fifo_reset_status_reg; + fifo_reset_status_sync_2_reg <= fifo_reset_status_sync_1_reg; + fifo_active_sync_1_reg <= fifo_active_reg; + fifo_active_sync_2_reg <= fifo_active_sync_1_reg; +end + +reg data_gen_enable_reg = 1'b0, data_gen_enable_next; +reg data_gen_reset_reg = 1'b0, data_gen_reset_next; +reg data_check_enable_reg = 1'b0, data_check_enable_next; +reg data_check_reset_reg = 1'b0, data_check_reset_next; + +reg [31:0] active_cycle_count_reg = 0, active_cycle_count_next; + +reg [31:0] write_count_reg = 0, write_count_next; +reg [31:0] read_count_reg = 0, read_count_next; + +assign reg_wr_wait = 1'b0; +assign reg_wr_ack = reg_wr_ack_reg; +assign reg_rd_data = reg_rd_data_reg; +assign reg_rd_wait = 1'b0; +assign reg_rd_ack = reg_rd_ack_reg; + +// test data generator and checker +wire [AXI_DATA_WIDTH-1:0] prbs_gen_data; +reg prbs_gen_en; +reg prbs_gen_rst; + +reg [AXI_DATA_WIDTH-1:0] prbs_check_data; +wire [31:0] prbs_check_lane_err_cnt[LANE_COUNT-1:0]; +reg prbs_check_en; +reg prbs_check_rst; + +reg [AXI_DATA_WIDTH-1:0] in_axis_tdata_reg = 0, in_axis_tdata_next; +reg in_axis_tvalid_reg = 1'b0, in_axis_tvalid_next; +wire in_axis_tready; + +wire [AXI_DATA_WIDTH-1:0] out_axis_tdata; +wire out_axis_tvalid; +reg out_axis_tready_reg = 1'b0, out_axis_tready_next; + +generate + +genvar n; + +for (n = 0; n < LANE_COUNT; n = n + 1) begin : lane + + // test data generator (PRBS31) + reg [30:0] lane_gen_state_reg = {31{1'b1}}; + wire [LANE_WIDTH-1:0] lane_gen_data; + wire [30:0] lane_gen_state; + + assign prbs_gen_data[n*LANE_WIDTH +: LANE_WIDTH] = lane_gen_data; + + lfsr #( + .LFSR_WIDTH(31), + .LFSR_POLY(31'h10000001), + .LFSR_CONFIG("FIBONACCI"), + .LFSR_FEED_FORWARD(0), + .REVERSE(0), + .DATA_WIDTH(LANE_WIDTH) + ) + prbs31_gen_inst ( + .data_in(0), + .state_in(lane_gen_state_reg), + .data_out(lane_gen_data), + .state_out(lane_gen_state) + ); + + always @(posedge clk) begin + if (prbs_gen_en) begin + lane_gen_state_reg <= lane_gen_state; + end + + if (prbs_gen_rst) begin + lane_gen_state_reg <= {31{1'b1}}; + end + end + + // test data checker (PRBS31) + reg [30:0] lane_check_state_reg = {31{1'b1}}; + wire [LANE_WIDTH-1:0] lane_check_data = prbs_check_data[n*LANE_WIDTH +: LANE_WIDTH]; + wire [LANE_WIDTH-1:0] lane_check_errors; + reg [LANE_ERR_CNT_WIDTH-1:0] lane_check_err_cnt; + reg [LANE_ERR_CNT_WIDTH-1:0] lane_check_err_cnt_reg = 0; + reg [31:0] lane_check_err_cnt_acc_reg = 0; + wire [30:0] lane_check_state; + + assign prbs_check_lane_err_cnt[n] = lane_check_err_cnt_acc_reg; + + lfsr #( + .LFSR_WIDTH(31), + .LFSR_POLY(31'h10000001), + .LFSR_CONFIG("FIBONACCI"), + .LFSR_FEED_FORWARD(1), + .REVERSE(0), + .DATA_WIDTH(LANE_WIDTH) + ) + prbs31_check_inst ( + .data_in(lane_check_data), + .state_in(lane_check_state_reg), + .data_out(lane_check_errors), + .state_out(lane_check_state) + ); + + integer i; + + always @* begin + lane_check_err_cnt = 0; + for (i = 0; i < LANE_WIDTH; i = i + 1) begin + lane_check_err_cnt = lane_check_err_cnt + lane_check_errors[i]; + end + end + + always @(posedge clk) begin + lane_check_err_cnt_reg <= 0; + lane_check_err_cnt_acc_reg <= lane_check_err_cnt_acc_reg + lane_check_err_cnt_reg; + + if (prbs_check_en) begin + lane_check_state_reg <= lane_check_state; + lane_check_err_cnt_reg <= lane_check_err_cnt; + end + + if (prbs_check_rst) begin + lane_check_state_reg <= {31{1'b1}}; + lane_check_err_cnt_acc_reg <= 0; + end + end + +end + +endgenerate + +integer k; + +always @* begin + reg_wr_ack_next = 1'b0; + reg_rd_data_next = 0; + reg_rd_ack_next = 1'b0; + + fifo_base_addr_next = fifo_base_addr_reg; + fifo_size_mask_next = fifo_size_mask_reg; + fifo_enable_next = fifo_enable_reg; + fifo_reset_next = fifo_reset_reg; + + data_gen_enable_next = data_gen_enable_reg; + data_gen_reset_next = data_gen_reset_reg; + data_check_enable_next = data_check_enable_reg; + data_check_reset_next = data_check_reset_reg; + + active_cycle_count_next = active_cycle_count_reg; + + write_count_next = write_count_reg; + read_count_next = read_count_reg; + + in_axis_tdata_next = in_axis_tdata_reg; + in_axis_tvalid_next = in_axis_tvalid_reg && !in_axis_tready; + + out_axis_tready_next = data_check_enable_reg; + + prbs_gen_en = 1'b0; + prbs_gen_rst = data_gen_reset_reg; + + prbs_check_data = out_axis_tdata; + prbs_check_en = 1'b0; + prbs_check_rst = data_check_reset_reg; + + if (data_gen_enable_reg && (!in_axis_tvalid_reg || in_axis_tready)) begin + prbs_gen_en = 1'b1; + in_axis_tdata_next = prbs_gen_data; + in_axis_tvalid_next = 1'b1; + + if (write_count_reg) begin + write_count_next = write_count_reg - 1; + end + + if (write_count_reg == 1) begin + data_gen_enable_next = 1'b0; + end + end + + if (out_axis_tready_reg && out_axis_tvalid) begin + prbs_check_en = 1'b1; + + if (read_count_reg) begin + read_count_next = read_count_reg - 1; + end + + if (read_count_reg == 1) begin + data_check_enable_next = 1'b0; + out_axis_tready_next = 1'b0; + end + end + + if (data_gen_enable_reg || data_check_enable_reg) begin + active_cycle_count_next = active_cycle_count_reg + 1; + end + + if (reg_wr_en && !reg_wr_ack_reg) begin + // write operation + reg_wr_ack_next = 1'b1; + case ({reg_wr_addr >> 2, 2'b00}) + RBB+8'h20: begin + fifo_enable_next = reg_wr_data[0]; + fifo_reset_next = reg_wr_data[1]; + end + RBB+8'h24: begin + data_gen_enable_next = reg_wr_data[0]; + data_gen_reset_next = reg_wr_data[1]; + data_check_enable_next = reg_wr_data[8]; + data_check_reset_next = reg_wr_data[9]; + end + RBB+8'h40: fifo_base_addr_next = (fifo_base_addr_reg & 64'hffffffff00000000) | {32'd0, reg_wr_data}; + RBB+8'h44: fifo_base_addr_next = (fifo_base_addr_reg & 64'h00000000ffffffff) | {reg_wr_data, 32'd0}; + RBB+8'h48: fifo_size_mask_next = (fifo_size_mask_reg & 64'hffffffff00000000) | {32'd0, reg_wr_data}; + RBB+8'h4C: fifo_size_mask_next = (fifo_size_mask_reg & 64'h00000000ffffffff) | {reg_wr_data, 32'd0}; + RBB+8'h60: active_cycle_count_next = 0; + RBB+8'h68: write_count_next = reg_wr_data; + RBB+8'h6C: read_count_next = reg_wr_data; + default: reg_wr_ack_next = 1'b0; + endcase + end + + if (reg_rd_en && !reg_rd_ack_reg) begin + // read operation + reg_rd_ack_next = 1'b1; + case ({reg_rd_addr >> 2, 2'b00}) + RBB+8'h00: reg_rd_data_next = 32'h12348102; // Type + RBB+8'h04: reg_rd_data_next = 32'h00000100; // Version + RBB+8'h08: reg_rd_data_next = RB_NEXT_PTR; // Next header + RBB+8'h10: reg_rd_data_next = AXI_ADDR_WIDTH; + RBB+8'h14: reg_rd_data_next = AXI_DATA_WIDTH; + RBB+8'h18: reg_rd_data_next = LANE_COUNT; + RBB+8'h1C: reg_rd_data_next = LANE_WIDTH; + RBB+8'h20: begin + reg_rd_data_next[0] = fifo_enable_reg; + reg_rd_data_next[1] = fifo_reset_reg; + reg_rd_data_next[16] = fifo_active_sync_2_reg; + reg_rd_data_next[17] = fifo_reset_status_sync_2_reg; + end + RBB+8'h24: begin + reg_rd_data_next[0] = data_gen_enable_reg; + reg_rd_data_next[1] = data_gen_reset_reg; + reg_rd_data_next[8] = data_check_enable_reg; + reg_rd_data_next[9] = data_check_reset_reg; + end + RBB+8'h30: reg_rd_data_next = FIFO_BASE_ADDR; + RBB+8'h34: reg_rd_data_next = FIFO_BASE_ADDR >> 32; + RBB+8'h38: reg_rd_data_next = FIFO_SIZE_MASK; + RBB+8'h3C: reg_rd_data_next = FIFO_SIZE_MASK >> 32; + RBB+8'h40: reg_rd_data_next = fifo_base_addr_reg; + RBB+8'h44: reg_rd_data_next = fifo_base_addr_reg >> 32; + RBB+8'h48: reg_rd_data_next = fifo_size_mask_reg; + RBB+8'h4C: reg_rd_data_next = fifo_size_mask_reg >> 32; + RBB+8'h50: reg_rd_data_next = fifo_occupancy_sync_2_reg; + RBB+8'h54: reg_rd_data_next = fifo_occupancy_sync_2_reg >> 32; + RBB+8'h60: reg_rd_data_next = active_cycle_count_reg; + RBB+8'h68: reg_rd_data_next = write_count_reg; + RBB+8'h6C: reg_rd_data_next = read_count_reg; + default: reg_rd_ack_next = 1'b0; + endcase + for (k = 0; k < LANE_COUNT; k = k + 1) begin + if ({reg_rd_addr >> 2, 2'b00} == RBB+8'h80 + k*4) begin + reg_rd_data_next = prbs_check_lane_err_cnt[k]; + reg_rd_ack_next = 1'b1; + end + end + end +end + +always @(posedge clk) begin + reg_wr_ack_reg <= reg_wr_ack_next; + reg_rd_data_reg <= reg_rd_data_next; + reg_rd_ack_reg <= reg_rd_ack_next; + + fifo_base_addr_reg <= fifo_base_addr_next; + fifo_size_mask_reg <= fifo_size_mask_next; + fifo_enable_reg <= fifo_enable_next; + fifo_reset_reg <= fifo_reset_next; + + data_gen_enable_reg <= data_gen_enable_next; + data_gen_reset_reg <= data_gen_reset_next; + data_check_enable_reg <= data_check_enable_next; + data_check_reset_reg <= data_check_reset_next; + + active_cycle_count_reg <= active_cycle_count_next; + + write_count_reg <= write_count_next; + read_count_reg <= read_count_next; + + in_axis_tdata_reg <= in_axis_tdata_next; + in_axis_tvalid_reg <= in_axis_tvalid_next; + + out_axis_tready_reg <= out_axis_tready_next; + + if (rst) begin + reg_wr_ack_reg <= 1'b0; + reg_rd_ack_reg <= 1'b0; + + fifo_base_addr_reg <= FIFO_BASE_ADDR; + fifo_size_mask_reg <= FIFO_SIZE_MASK; + fifo_enable_reg <= 1'b0; + fifo_reset_reg <= 1'b0; + + data_gen_enable_reg <= 1'b0; + data_gen_reset_reg <= 1'b0; + data_check_enable_reg <= 1'b0; + data_check_reset_reg <= 1'b0; + + active_cycle_count_reg <= 0; + + write_count_reg <= 0; + read_count_reg <= 0; + + in_axis_tvalid_reg <= in_axis_tvalid_next; + out_axis_tready_reg <= out_axis_tready_next; + end +end + +axi_vfifo_raw #( + .SEG_WIDTH(AXI_DATA_WIDTH), + .SEG_CNT(1), + .AXI_DATA_WIDTH(AXI_DATA_WIDTH), + .AXI_ADDR_WIDTH(AXI_ADDR_WIDTH), + .AXI_STRB_WIDTH(AXI_STRB_WIDTH), + .AXI_ID_WIDTH(AXI_ID_WIDTH), + .AXI_MAX_BURST_LEN(AXI_MAX_BURST_LEN), + .LEN_WIDTH(AXI_ADDR_WIDTH), + .CTRL_OUT_EN(0) +) +axi_vfifo_raw_inst ( + .clk(m_axi_clk), + .rst(m_axi_rst), + + /* + * Segmented data input (from encode logic) + */ + .input_clk(clk), + .input_rst(rst), + .input_rst_out(), + .input_watermark(), + .input_data(in_axis_tdata_reg), + .input_valid(in_axis_tvalid_reg), + .input_ready(in_axis_tready), + + /* + * Segmented data output (to decode logic) + */ + .output_clk(clk), + .output_rst(rst), + .output_rst_out(), + .output_data(out_axis_tdata), + .output_valid(out_axis_tvalid), + .output_ready(out_axis_tready_reg), + .output_ctrl_data(), + .output_ctrl_valid(), + .output_ctrl_ready(1'b1), + + /* + * AXI master interface + */ + .m_axi_awid(m_axi_awid), + .m_axi_awaddr(m_axi_awaddr), + .m_axi_awlen(m_axi_awlen), + .m_axi_awsize(m_axi_awsize), + .m_axi_awburst(m_axi_awburst), + .m_axi_awlock(m_axi_awlock), + .m_axi_awcache(m_axi_awcache), + .m_axi_awprot(m_axi_awprot), + .m_axi_awvalid(m_axi_awvalid), + .m_axi_awready(m_axi_awready), + .m_axi_wdata(m_axi_wdata), + .m_axi_wstrb(m_axi_wstrb), + .m_axi_wlast(m_axi_wlast), + .m_axi_wvalid(m_axi_wvalid), + .m_axi_wready(m_axi_wready), + .m_axi_bid(m_axi_bid), + .m_axi_bresp(m_axi_bresp), + .m_axi_bvalid(m_axi_bvalid), + .m_axi_bready(m_axi_bready), + .m_axi_arid(m_axi_arid), + .m_axi_araddr(m_axi_araddr), + .m_axi_arlen(m_axi_arlen), + .m_axi_arsize(m_axi_arsize), + .m_axi_arburst(m_axi_arburst), + .m_axi_arlock(m_axi_arlock), + .m_axi_arcache(m_axi_arcache), + .m_axi_arprot(m_axi_arprot), + .m_axi_arvalid(m_axi_arvalid), + .m_axi_arready(m_axi_arready), + .m_axi_rid(m_axi_rid), + .m_axi_rdata(m_axi_rdata), + .m_axi_rresp(m_axi_rresp), + .m_axi_rlast(m_axi_rlast), + .m_axi_rvalid(m_axi_rvalid), + .m_axi_rready(m_axi_rready), + + /* + * Reset sync + */ + .rst_req_out(), + .rst_req_in(1'b0), + + /* + * Configuration + */ + .cfg_fifo_base_addr(fifo_base_addr_sync_2_reg), + .cfg_fifo_size_mask(fifo_size_mask_sync_2_reg), + .cfg_enable(fifo_enable_sync_2_reg), + .cfg_reset(fifo_reset_sync_2_reg), + + /* + * Status + */ + .sts_fifo_occupancy(fifo_occupancy), + .sts_fifo_empty(), + .sts_fifo_full(), + .sts_reset(fifo_reset_status), + .sts_active(fifo_active), + .sts_write_active(), + .sts_read_active() +); + +endmodule + +`resetall diff --git a/fpga/app/dma_bench/syn/vivado/dram_test_ch.tcl b/fpga/app/dma_bench/syn/vivado/dram_test_ch.tcl new file mode 100644 index 000000000..bbff97d75 --- /dev/null +++ b/fpga/app/dma_bench/syn/vivado/dram_test_ch.tcl @@ -0,0 +1,57 @@ +# Copyright 2023, The Regents of the University of California. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +# OF SUCH DAMAGE. +# +# The views and conclusions contained in the software and documentation are those +# of the authors and should not be interpreted as representing official policies, +# either expressed or implied, of The Regents of the University of California. + +# DRAM test channel timing constraints + +foreach inst [get_cells -hier -filter {(ORIG_REF_NAME == dram_test_ch || REF_NAME == dram_test_ch)}] { + puts "Inserting timing constraints for dram_test_ch instance $inst" + + proc constrain_sync_chain {inst driver args} { + set sync_ffs [get_cells -hier [concat $driver $args] -filter "PARENT == $inst"] + + if {[llength $sync_ffs]} { + set_property ASYNC_REG TRUE $sync_ffs + + set src_clk [get_clocks -of_objects [get_pins "$inst/$driver/C"]] + + set src_clk_period [if {[llength $src_clk]} {get_property -min PERIOD $src_clk} {expr 1.0}] + + set_max_delay -from [get_cells "$inst/$driver"] -to [get_cells "$inst/[lindex $args 0]"] -datapath_only $src_clk_period + } + } + + constrain_sync_chain $inst "fifo_base_addr_reg_reg[*]" "fifo_base_addr_sync_1_reg_reg[*]" "fifo_base_addr_sync_2_reg_reg[*]" + constrain_sync_chain $inst "fifo_size_mask_reg_reg[*]" "fifo_size_mask_sync_1_reg_reg[*]" "fifo_size_mask_sync_2_reg_reg[*]" + constrain_sync_chain $inst "fifo_enable_reg_reg" "fifo_enable_sync_1_reg_reg" "fifo_enable_sync_2_reg_reg" + constrain_sync_chain $inst "fifo_reset_reg_reg" "fifo_reset_sync_1_reg_reg" "fifo_reset_sync_2_reg_reg" + + constrain_sync_chain $inst "fifo_occupancy_reg_reg[*]" "fifo_occupancy_sync_1_reg_reg[*]" "fifo_occupancy_sync_2_reg_reg[*]" + constrain_sync_chain $inst "fifo_reset_status_reg_reg" "fifo_reset_status_sync_1_reg_reg" "fifo_reset_status_sync_2_reg_reg" + constrain_sync_chain $inst "fifo_active_reg_reg" "fifo_active_sync_1_reg_reg" "fifo_active_sync_2_reg_reg" +} diff --git a/fpga/app/dma_bench/tb/dram_test_ch/Makefile b/fpga/app/dma_bench/tb/dram_test_ch/Makefile new file mode 100644 index 000000000..1e7b32e30 --- /dev/null +++ b/fpga/app/dma_bench/tb/dram_test_ch/Makefile @@ -0,0 +1,126 @@ +# Copyright 2023, The Regents of the University of California. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +# OF SUCH DAMAGE. +# +# The views and conclusions contained in the software and documentation are those +# of the authors and should not be interpreted as representing official policies, +# either expressed or implied, of The Regents of the University of California. + +TOPLEVEL_LANG = verilog + +SIM ?= icarus +WAVES ?= 0 + +COCOTB_HDL_TIMEUNIT = 1ns +COCOTB_HDL_TIMEPRECISION = 1ps + +DUT = dram_test_ch +TOPLEVEL = $(DUT) +MODULE = test_$(DUT) +VERILOG_SOURCES += ../../rtl/$(DUT).v +VERILOG_SOURCES += ../../lib/eth/rtl/lfsr.v +VERILOG_SOURCES += ../../lib/axi/rtl/axi_vfifo_raw.v +VERILOG_SOURCES += ../../lib/axi/rtl/axi_vfifo_raw_rd.v +VERILOG_SOURCES += ../../lib/axi/rtl/axi_vfifo_raw_wr.v +VERILOG_SOURCES += ../../lib/axi/rtl/axil_crossbar.v +VERILOG_SOURCES += ../../lib/axi/rtl/axil_crossbar_addr.v +VERILOG_SOURCES += ../../lib/axi/rtl/axil_crossbar_rd.v +VERILOG_SOURCES += ../../lib/axi/rtl/axil_crossbar_wr.v +VERILOG_SOURCES += ../../lib/axi/rtl/axil_reg_if.v +VERILOG_SOURCES += ../../lib/axi/rtl/axil_reg_if_rd.v +VERILOG_SOURCES += ../../lib/axi/rtl/axil_reg_if_wr.v +VERILOG_SOURCES += ../../lib/axi/rtl/axil_register_rd.v +VERILOG_SOURCES += ../../lib/axi/rtl/axil_register_wr.v +VERILOG_SOURCES += ../../lib/axi/rtl/arbiter.v +VERILOG_SOURCES += ../../lib/axi/rtl/priority_encoder.v +VERILOG_SOURCES += ../../lib/axis/rtl/axis_adapter.v +VERILOG_SOURCES += ../../lib/axis/rtl/axis_arb_mux.v +VERILOG_SOURCES += ../../lib/axis/rtl/axis_async_fifo.v +VERILOG_SOURCES += ../../lib/axis/rtl/axis_async_fifo_adapter.v +VERILOG_SOURCES += ../../lib/axis/rtl/axis_demux.v +VERILOG_SOURCES += ../../lib/axis/rtl/axis_fifo.v +VERILOG_SOURCES += ../../lib/axis/rtl/axis_fifo_adapter.v +VERILOG_SOURCES += ../../lib/axis/rtl/axis_pipeline_fifo.v +VERILOG_SOURCES += ../../lib/axis/rtl/axis_register.v + +# module parameters + +# AXI configuration +export PARAM_AXI_DATA_WIDTH ?= 256 +export PARAM_AXI_ADDR_WIDTH ?= 32 +export PARAM_AXI_ID_WIDTH ?= 8 +export PARAM_AXI_MAX_BURST_LEN ?= 256 + +# Register interface +export PARAM_REG_ADDR_WIDTH ?= 7 +export PARAM_REG_DATA_WIDTH ?= 32 +export PARAM_RB_BASE_ADDR ?= 0 +export PARAM_RB_NEXT_PTR ?= 0 + +ifeq ($(SIM), icarus) + PLUSARGS += -fst + + COMPILE_ARGS += -P $(TOPLEVEL).AXI_DATA_WIDTH=$(PARAM_AXI_DATA_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).AXI_ADDR_WIDTH=$(PARAM_AXI_ADDR_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).AXI_ID_WIDTH=$(PARAM_AXI_ID_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).AXI_MAX_BURST_LEN=$(PARAM_AXI_MAX_BURST_LEN) + COMPILE_ARGS += -P $(TOPLEVEL).REG_ADDR_WIDTH=$(PARAM_REG_ADDR_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).REG_DATA_WIDTH=$(PARAM_REG_DATA_WIDTH) + COMPILE_ARGS += -P $(TOPLEVEL).RB_BASE_ADDR=$(PARAM_RB_BASE_ADDR) + COMPILE_ARGS += -P $(TOPLEVEL).RB_NEXT_PTR=$(PARAM_RB_NEXT_PTR) + + ifeq ($(WAVES), 1) + VERILOG_SOURCES += iverilog_dump.v + COMPILE_ARGS += -s iverilog_dump + endif +else ifeq ($(SIM), verilator) + COMPILE_ARGS += -Wno-SELRANGE -Wno-WIDTH + + COMPILE_ARGS += -GAXI_DATA_WIDTH=$(PARAM_AXI_DATA_WIDTH) + COMPILE_ARGS += -GAXI_ADDR_WIDTH=$(PARAM_AXI_ADDR_WIDTH) + COMPILE_ARGS += -GAXI_ID_WIDTH=$(PARAM_AXI_ID_WIDTH) + COMPILE_ARGS += -GAXI_MAX_BURST_LEN=$(PARAM_AXI_MAX_BURST_LEN) + COMPILE_ARGS += -GREG_ADDR_WIDTH=$(PARAM_REG_ADDR_WIDTH) + COMPILE_ARGS += -GREG_DATA_WIDTH=$(PARAM_REG_DATA_WIDTH) + COMPILE_ARGS += -GRB_BASE_ADDR=$(PARAM_RB_BASE_ADDR) + COMPILE_ARGS += -GRB_NEXT_PTR=$(PARAM_RB_NEXT_PTR) + + ifeq ($(WAVES), 1) + COMPILE_ARGS += --trace-fst + endif +endif + +include $(shell cocotb-config --makefiles)/Makefile.sim + +iverilog_dump.v: + echo 'module iverilog_dump();' > $@ + echo 'initial begin' >> $@ + echo ' $$dumpfile("$(TOPLEVEL).fst");' >> $@ + echo ' $$dumpvars(0, $(TOPLEVEL));' >> $@ + echo 'end' >> $@ + echo 'endmodule' >> $@ + +clean:: + @rm -rf iverilog_dump.v + @rm -rf dump.fst $(TOPLEVEL).fst diff --git a/fpga/app/dma_bench/tb/dram_test_ch/test_dram_test_ch.py b/fpga/app/dma_bench/tb/dram_test_ch/test_dram_test_ch.py new file mode 100644 index 000000000..39029d579 --- /dev/null +++ b/fpga/app/dma_bench/tb/dram_test_ch/test_dram_test_ch.py @@ -0,0 +1,283 @@ +""" + +Copyright 2023, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +""" + +import logging +import os + +import cocotb_test.simulator +import pytest + +import cocotb +from cocotb.clock import Clock +from cocotb.triggers import RisingEdge +from cocotb.regression import TestFactory + +from cocotbext.axi import AxiBus, AxiRam + + +class TB(object): + def __init__(self, dut): + self.dut = dut + + self.log = logging.getLogger("cocotb.tb") + self.log.setLevel(logging.DEBUG) + + cocotb.start_soon(Clock(dut.clk, 4, units="ns").start()) + + # Control + dut.reg_wr_addr.setimmediatevalue(0) + dut.reg_wr_data.setimmediatevalue(0) + dut.reg_wr_strb.setimmediatevalue(0) + dut.reg_wr_en.setimmediatevalue(0) + dut.reg_rd_addr.setimmediatevalue(0) + dut.reg_rd_en.setimmediatevalue(0) + + # DRAM + cocotb.start_soon(Clock(dut.m_axi_clk, 3.332, units="ns").start()) + dut.m_axi_rst.setimmediatevalue(0) + + self.ram = AxiRam(AxiBus.from_prefix(dut, "m_axi"), dut.m_axi_clk, dut.m_axi_rst, size=16*2**20) + + async def reset(self): + self.dut.rst.setimmediatevalue(0) + self.dut.m_axi_rst.setimmediatevalue(0) + for k in range(10): + await RisingEdge(self.dut.clk) + self.dut.rst.value = 1 + self.dut.m_axi_rst.value = 1 + for k in range(10): + await RisingEdge(self.dut.clk) + self.dut.rst.value = 0 + self.dut.m_axi_rst.value = 0 + for k in range(10): + await RisingEdge(self.dut.clk) + + async def write_reg(self, addr, data): + await RisingEdge(self.dut.clk) + self.dut.reg_wr_addr.value = addr + self.dut.reg_wr_data.value = data + self.dut.reg_wr_strb.value = 0xf + self.dut.reg_wr_en.value = 1 + await RisingEdge(self.dut.clk) + + k = 4 + while k > 0: + if self.dut.reg_wr_ack.value: + break + if not self.dut.reg_wr_wait.value: + k -= 1 + await RisingEdge(self.dut.clk) + + self.dut.reg_wr_en.value = 0 + + async def read_reg(self, addr): + self.dut.reg_rd_addr.value = addr + self.dut.reg_rd_en.value = 1 + await RisingEdge(self.dut.clk) + + k = 4 + while k > 0: + if self.dut.reg_rd_ack.value: + break + if not self.dut.reg_rd_wait.value: + k -= 1 + await RisingEdge(self.dut.clk) + + self.dut.reg_rd_en.value = 0 + return self.dut.reg_rd_data.value.integer + + +async def run_test(dut): + + tb = TB(dut) + + await tb.reset() + + # configure FIFO + await tb.write_reg(0x38, (16*2**20)-1) + await tb.write_reg(0x3C, 0x00000000) + + # reset FIFO + await tb.write_reg(0x20, 0x00000002) + + for k in range(10): + await RisingEdge(dut.clk) + + # enable FIFO + await tb.write_reg(0x20, 0x00000001) + + # enable data generation and checking (test read and write) + await tb.write_reg(0x68, 1024) + await tb.write_reg(0x6C, 1024) + await tb.write_reg(0x24, 0x00000101) + + # wait for transfer to complete + while True: + val = await tb.read_reg(0x24) + if val == 0: + break + + for k in range(200): + await RisingEdge(dut.clk) + + # enable data generation only (test write) + await tb.write_reg(0x68, 1024) + await tb.write_reg(0x6C, 0) + await tb.write_reg(0x24, 0x00000001) + + # wait for transfer to complete + while True: + val = await tb.read_reg(0x24) + if val == 0: + break + + for k in range(200): + await RisingEdge(dut.clk) + + # enable data generation and checking (test offset read and write) + await tb.write_reg(0x68, 1024) + await tb.write_reg(0x6C, 1024) + await tb.write_reg(0x24, 0x00000101) + + # wait for transfer to complete + while True: + val = await tb.read_reg(0x24) + if val == 0: + break + + for k in range(200): + await RisingEdge(dut.clk) + + # enable data checking only (test read) + await tb.write_reg(0x68, 0) + await tb.write_reg(0x6C, 1024) + await tb.write_reg(0x24, 0x00000100) + + # wait for transfer to complete + while True: + val = await tb.read_reg(0x24) + if val == 0: + break + + for k in range(200): + await RisingEdge(dut.clk) + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + +if cocotb.SIM_NAME: + + for test in [ + run_test, + ]: + + factory = TestFactory(test) + factory.generate_tests() + + +# cocotb-test + +tests_dir = os.path.dirname(__file__) +rtl_dir = os.path.abspath(os.path.join(tests_dir, '..', '..', 'rtl')) +lib_dir = os.path.abspath(os.path.join(rtl_dir, '..', 'lib')) +axi_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axi', 'rtl')) +axis_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'axis', 'rtl')) +eth_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'eth', 'rtl')) +pcie_rtl_dir = os.path.abspath(os.path.join(lib_dir, 'pcie', 'rtl')) + + +@pytest.mark.parametrize("data_width", [256, 512]) +def test_dram_test_ch(request, data_width): + dut = "dram_test_ch" + module = os.path.splitext(os.path.basename(__file__))[0] + toplevel = dut + + verilog_sources = [ + os.path.join(rtl_dir, f"{dut}.v"), + os.path.join(eth_rtl_dir, "lfsr.v"), + os.path.join(axi_rtl_dir, "axi_vfifo_raw.v"), + os.path.join(axi_rtl_dir, "axi_vfifo_raw_rd.v"), + os.path.join(axi_rtl_dir, "axi_vfifo_raw_wr.v"), + os.path.join(axi_rtl_dir, "axil_crossbar.v"), + os.path.join(axi_rtl_dir, "axil_crossbar_addr.v"), + os.path.join(axi_rtl_dir, "axil_crossbar_rd.v"), + os.path.join(axi_rtl_dir, "axil_crossbar_wr.v"), + os.path.join(axi_rtl_dir, "axil_reg_if.v"), + os.path.join(axi_rtl_dir, "axil_reg_if_rd.v"), + os.path.join(axi_rtl_dir, "axil_reg_if_wr.v"), + os.path.join(axi_rtl_dir, "axil_register_rd.v"), + os.path.join(axi_rtl_dir, "axil_register_wr.v"), + os.path.join(axi_rtl_dir, "arbiter.v"), + os.path.join(axi_rtl_dir, "priority_encoder.v"), + os.path.join(axis_rtl_dir, "axis_adapter.v"), + os.path.join(axis_rtl_dir, "axis_arb_mux.v"), + os.path.join(axis_rtl_dir, "axis_async_fifo.v"), + os.path.join(axis_rtl_dir, "axis_async_fifo_adapter.v"), + os.path.join(axis_rtl_dir, "axis_demux.v"), + os.path.join(axis_rtl_dir, "axis_fifo.v"), + os.path.join(axis_rtl_dir, "axis_fifo_adapter.v"), + os.path.join(axis_rtl_dir, "axis_pipeline_fifo.v"), + os.path.join(axis_rtl_dir, "axis_register.v"), + ] + + parameters = {} + + # AXI configuration + parameters['AXI_DATA_WIDTH'] = 256 + parameters['AXI_ADDR_WIDTH'] = 32 + parameters['AXI_STRB_WIDTH'] = parameters['AXI_DATA_WIDTH'] // 8 + parameters['AXI_ID_WIDTH'] = 8 + parameters['AXI_MAX_BURST_LEN'] = 256 + + # Register interface + parameters['REG_ADDR_WIDTH'] = 7 + parameters['REG_DATA_WIDTH'] = 32 + parameters['REG_STRB_WIDTH'] = parameters['REG_DATA_WIDTH'] // 8 + parameters['RB_BASE_ADDR'] = 0 + parameters['RB_NEXT_PTR'] = 0 + + extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()} + + sim_build = os.path.join(tests_dir, "sim_build", + request.node.name.replace('[', '-').replace(']', '')) + + cocotb_test.simulator.run( + python_search=[tests_dir], + verilog_sources=verilog_sources, + toplevel=toplevel, + module=module, + parameters=parameters, + sim_build=sim_build, + extra_env=extra_env, + )