mirror of
https://github.com/corundum/corundum.git
synced 2025-01-30 08:32:52 +08:00
fpga/app/dma_bench: Add DRAM test channel module
Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
parent
4245317b15
commit
d6bac395f3
656
fpga/app/dma_bench/rtl/dram_test_ch.v
Normal file
656
fpga/app/dma_bench/rtl/dram_test_ch.v
Normal file
@ -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
|
57
fpga/app/dma_bench/syn/vivado/dram_test_ch.tcl
Normal file
57
fpga/app/dma_bench/syn/vivado/dram_test_ch.tcl
Normal file
@ -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"
|
||||
}
|
126
fpga/app/dma_bench/tb/dram_test_ch/Makefile
Normal file
126
fpga/app/dma_bench/tb/dram_test_ch/Makefile
Normal file
@ -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
|
283
fpga/app/dma_bench/tb/dram_test_ch/test_dram_test_ch.py
Normal file
283
fpga/app/dma_bench/tb/dram_test_ch/test_dram_test_ch.py
Normal file
@ -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,
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user