pluto_hdl_adi/library/axi_dmac/tb/regmap_tb.v

430 lines
12 KiB
Coq
Raw Normal View History

// ***************************************************************************
// ***************************************************************************
// Copyright 2014 - 2017 (c) Analog Devices, Inc. All rights reserved.
//
// In this HDL repository, there are many different and unique modules, consisting
// of various HDL (Verilog or VHDL) components. The individual modules are
// developed independently, and may be accompanied by separate and unique license
// terms.
//
// The user should read each of these license terms, and understand the
// freedoms and responsibilities that he or she has by using this source/core.
//
// This core is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE.
//
// Redistribution and use of source or resulting binaries, with or without modification
// of this file, are permitted under one of the following two license terms:
//
// 1. The GNU General Public License version 2 as published by the
// Free Software Foundation, which can be found in the top level directory
// of this repository (LICENSE_GPL2), and also online at:
// <https://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
//
// OR
//
// 2. An ADI specific BSD license, which can be found in the top level directory
// of this repository (LICENSE_ADIBSD), and also on-line at:
// https://github.com/analogdevicesinc/hdl/blob/master/LICENSE_ADIBSD
// This will allow to generate bit files and not release the source code,
// as long as it attaches to an ADI device.
//
// ***************************************************************************
// ***************************************************************************
`timescale 1ns/100ps
module regmap_tb;
parameter VCD_FILE = {`__FILE__,"cd"};
`define TIMEOUT 1000000
`include "tb_base.v"
localparam DMA_LENGTH_WIDTH = 24;
localparam BYTES_PER_BEAT = 1;
localparam DMA_AXI_ADDR_WIDTH = 32;
axi_dmac: Enforce transfer length and stride alignments In its current implementation the DMAC requires that the length of a transfer is aligned to the widest interface. E.g. if the widest interface is 128 bits wide the length of the transfer needs to be a multiple of 16 bytes. If the requested length is not aligned to the interface width it will be rounded up. This works fine as long as both interfaces have the same width. If they have different widths it is possible that the length is rounded up to different values on the source and destination side. In that case the DMA will deadlock because the transfer lengths don't match and either not enough of too much data is delivered from the source to the destination side. Currently it is up to software to make sure that such an invalid configuration is not possible. Also enforce this requirement in the DMAC itself by setting the LSBs of the transfer length to a fixed 1 so that the length is always aligned to the widest interface. Software can also use this to discover the length alignment requirement, by first writing a zero to the length register and then reading the register back. The LSBs of the read back value will be non-zero indicating the alignment requirement. In a similar way the stride needs to be aligned to the width of its respective interface, so the generated addresses stay aligned. Enforce this in the same way by keeping the LSBs cleared. Increment the minor version number to reflect these changes. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2017-09-21 09:15:45 +00:00
localparam LENGTH_ALIGN = 2;
localparam LENGTH_MASK = {DMA_LENGTH_WIDTH{1'b1}};
axi_dmac: Enforce transfer length and stride alignments In its current implementation the DMAC requires that the length of a transfer is aligned to the widest interface. E.g. if the widest interface is 128 bits wide the length of the transfer needs to be a multiple of 16 bytes. If the requested length is not aligned to the interface width it will be rounded up. This works fine as long as both interfaces have the same width. If they have different widths it is possible that the length is rounded up to different values on the source and destination side. In that case the DMA will deadlock because the transfer lengths don't match and either not enough of too much data is delivered from the source to the destination side. Currently it is up to software to make sure that such an invalid configuration is not possible. Also enforce this requirement in the DMAC itself by setting the LSBs of the transfer length to a fixed 1 so that the length is always aligned to the widest interface. Software can also use this to discover the length alignment requirement, by first writing a zero to the length register and then reading the register back. The LSBs of the read back value will be non-zero indicating the alignment requirement. In a similar way the stride needs to be aligned to the width of its respective interface, so the generated addresses stay aligned. Enforce this in the same way by keeping the LSBs cleared. Increment the minor version number to reflect these changes. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2017-09-21 09:15:45 +00:00
localparam LENGTH_ALIGN_MASK = {LENGTH_ALIGN{1'b1}};
localparam STRIDE_MASK = {{DMA_LENGTH_WIDTH-BYTES_PER_BEAT{1'b1}},{BYTES_PER_BEAT{1'b0}}};
localparam ADDR_MASK = {{DMA_AXI_ADDR_WIDTH-BYTES_PER_BEAT{1'b1}},{BYTES_PER_BEAT{1'b0}}};
localparam VAL_DBG_SRC_ADDR = 32'h76543210;
localparam VAL_DBG_DEST_ADDR = 32'hfedcba98;
localparam VAL_DBG_STATUS = 12'ha5;
localparam VAL_DBG_IDS0 = 32'h01234567;
localparam VAL_DBG_IDS1 = 32'h89abcdef;
localparam AW = 11;
localparam NUM_REGS = 'h200;
wire s_axi_aclk = clk;
wire s_axi_aresetn = ~reset;
reg s_axi_awvalid = 1'b0;
reg s_axi_wvalid = 1'b0;
reg [AW-1:0] s_axi_awaddr = 'h00;
reg [31:0] s_axi_wdata = 'h00;
wire [1:0] s_axi_bresp;
wire s_axi_awready;
wire s_axi_wready;
wire s_axi_bready = 1'b1;
wire [3:0] s_axi_wstrb = 4'b1111;
wire [2:0] s_axi_awprot = 3'b000;
wire [2:0] s_axi_arprot = 3'b000;
wire [1:0] s_axi_rresp;
wire [31:0] s_axi_rdata;
task write_reg;
input [31:0] addr;
input [31:0] value;
begin
@(posedge s_axi_aclk)
s_axi_awvalid <= 1'b1;
s_axi_wvalid <= 1'b1;
s_axi_awaddr <= addr;
s_axi_wdata <= value;
@(posedge s_axi_aclk)
while (s_axi_awvalid || s_axi_wvalid) begin
@(posedge s_axi_aclk)
if (s_axi_awready)
s_axi_awvalid <= 1'b0;
if (s_axi_wready)
s_axi_wvalid <= 1'b0;
end
end
endtask
reg [31:0] expected_reg_mem[0:NUM_REGS-1];
reg [AW-1:0] s_axi_araddr = 'h0;
reg s_axi_arvalid = 'h0;
reg s_axi_rready = 'h0;
wire s_axi_arready;
wire s_axi_rvalid;
task read_reg;
input [31:0] addr;
output [31:0] value;
begin
s_axi_arvalid <= 1'b1;
s_axi_araddr <= addr;
s_axi_rready <= 1'b1;
@(posedge s_axi_aclk) #0;
while (s_axi_arvalid) begin
if (s_axi_arready == 1'b1) begin
s_axi_arvalid <= 1'b0;
end
@(posedge s_axi_aclk) #0;
end
while (s_axi_rready) begin
if (s_axi_rvalid == 1'b1) begin
value <= s_axi_rdata;
s_axi_rready <= 1'b0;
end
@(posedge s_axi_aclk) #0;
end
end
endtask
task read_reg_check;
input [31:0] addr;
output match;
reg [31:0] value;
reg [31:0] expected;
input [255:0] message;
begin
read_reg(addr, value);
expected = expected_reg_mem[addr[11:2]];
match <= value === expected;
if (value !== expected) begin
$display("%0s: Register mismatch for %x. Expected %x, got %x",
message, addr, expected, value);
end
end
endtask
reg read_match = 1'b1;
always @(posedge clk) begin
if (read_match == 1'b0) begin
failed <= 1'b1;
end
end
task set_reset_reg_value;
input [31:0] addr;
input [31:0] value;
begin
expected_reg_mem[addr[AW-1:2]] <= value;
end
endtask
task initialize_expected_reg_mem;
integer i;
begin
for (i = 0; i < NUM_REGS; i = i + 1)
expected_reg_mem[i] <= 'h00;
/* Non zero power-on-reset values */
set_reset_reg_value('h00, 32'h00040361); /* PCORE version register */
set_reset_reg_value('h0c, 32'h444d4143); /* PCORE magic register */
set_reset_reg_value('h10, 32'h00002101); /* Interface Description*/
set_reset_reg_value('h80, 'h3); /* IRQ mask */
set_reset_reg_value('h40c, 'h3); /* Flags */
axi_dmac: Enforce transfer length and stride alignments In its current implementation the DMAC requires that the length of a transfer is aligned to the widest interface. E.g. if the widest interface is 128 bits wide the length of the transfer needs to be a multiple of 16 bytes. If the requested length is not aligned to the interface width it will be rounded up. This works fine as long as both interfaces have the same width. If they have different widths it is possible that the length is rounded up to different values on the source and destination side. In that case the DMA will deadlock because the transfer lengths don't match and either not enough of too much data is delivered from the source to the destination side. Currently it is up to software to make sure that such an invalid configuration is not possible. Also enforce this requirement in the DMAC itself by setting the LSBs of the transfer length to a fixed 1 so that the length is always aligned to the widest interface. Software can also use this to discover the length alignment requirement, by first writing a zero to the length register and then reading the register back. The LSBs of the read back value will be non-zero indicating the alignment requirement. In a similar way the stride needs to be aligned to the width of its respective interface, so the generated addresses stay aligned. Enforce this in the same way by keeping the LSBs cleared. Increment the minor version number to reflect these changes. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2017-09-21 09:15:45 +00:00
set_reset_reg_value('h418, LENGTH_ALIGN_MASK); /* Length alignment */
set_reset_reg_value('h434, VAL_DBG_DEST_ADDR);
set_reset_reg_value('h438, VAL_DBG_SRC_ADDR);
set_reset_reg_value('h43c, VAL_DBG_STATUS);
set_reset_reg_value('h440, VAL_DBG_IDS0);
set_reset_reg_value('h444, VAL_DBG_IDS1);
end
endtask
task check_all_registers;
input [255:0] message;
integer i;
begin
for (i = 0; i < NUM_REGS*4; i = i + 4) begin
read_reg_check(i, read_match, message);
end
end
endtask
task write_reg_and_update;
input [31:0] addr;
input [31:0] value;
integer i;
begin
write_reg(addr, value);
expected_reg_mem[addr[AW-1:2]] <= value;
end
endtask
task invert_register;
input [31:0] addr;
reg [31:0] value;
begin
read_reg(addr, value);
write_reg(addr, ~value);
end
endtask
task invert_all_registers;
integer i;
begin
for (i = 0; i < NUM_REGS*4; i = i + 4) begin
invert_register(i);
end
end
endtask
reg request_ready = 1'b0;
wire [31:BYTES_PER_BEAT] request_dest_address;
wire [31:BYTES_PER_BEAT] request_src_address;
wire [DMA_LENGTH_WIDTH-1:0] request_x_length;
wire [DMA_LENGTH_WIDTH-1:0] request_y_length;
wire [DMA_LENGTH_WIDTH-1:0] request_dest_stride;
wire [DMA_LENGTH_WIDTH-1:0] request_src_stride;
wire request_last;
reg response_eot = 1'b0;
wire [6:0] response_measured_burst_length = 'hff;
wire ctrl_enable;
wire ctrl_pause;
wire request_valid;
wire request_sync_transfer_start;
wire response_partial = 1'b1;
reg response_valid = 1'b0;
wire response_ready;
always @(posedge clk) begin
if (request_valid & request_ready) begin
response_valid <= 1'b1;
end else if (response_ready) begin
response_valid <= 1'b0;
end
end
integer i;
initial begin
initialize_expected_reg_mem();
@(posedge s_axi_aresetn)
set_reset_reg_value('h44c, 32'hxxxxxxxx);
set_reset_reg_value('h450, 2'bX);
check_all_registers("Initial");
/* Check scratch */
write_reg_and_update('h08, 32'h12345678);
check_all_registers("Scratch");
/* Check IRQ mask */
write_reg_and_update('h80, 32'h0);
check_all_registers("IRQ mask");
/* Check transfer registers */
write_reg_and_update('h40c, 'h7);
write_reg_and_update('h410, ADDR_MASK);
write_reg_and_update('h414, ADDR_MASK);
write_reg_and_update('h418, LENGTH_MASK);
write_reg_and_update('h41c, LENGTH_MASK);
axi_dmac: Enforce transfer length and stride alignments In its current implementation the DMAC requires that the length of a transfer is aligned to the widest interface. E.g. if the widest interface is 128 bits wide the length of the transfer needs to be a multiple of 16 bytes. If the requested length is not aligned to the interface width it will be rounded up. This works fine as long as both interfaces have the same width. If they have different widths it is possible that the length is rounded up to different values on the source and destination side. In that case the DMA will deadlock because the transfer lengths don't match and either not enough of too much data is delivered from the source to the destination side. Currently it is up to software to make sure that such an invalid configuration is not possible. Also enforce this requirement in the DMAC itself by setting the LSBs of the transfer length to a fixed 1 so that the length is always aligned to the widest interface. Software can also use this to discover the length alignment requirement, by first writing a zero to the length register and then reading the register back. The LSBs of the read back value will be non-zero indicating the alignment requirement. In a similar way the stride needs to be aligned to the width of its respective interface, so the generated addresses stay aligned. Enforce this in the same way by keeping the LSBs cleared. Increment the minor version number to reflect these changes. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2017-09-21 09:15:45 +00:00
write_reg_and_update('h420, STRIDE_MASK);
write_reg_and_update('h424, STRIDE_MASK);
check_all_registers("Transfer setup 1");
/* Check transfer registers */
write_reg_and_update('h40c, {$random} & 'h7);
write_reg_and_update('h410, {$random} & ADDR_MASK);
write_reg_and_update('h414, {$random} & ADDR_MASK);
axi_dmac: Enforce transfer length and stride alignments In its current implementation the DMAC requires that the length of a transfer is aligned to the widest interface. E.g. if the widest interface is 128 bits wide the length of the transfer needs to be a multiple of 16 bytes. If the requested length is not aligned to the interface width it will be rounded up. This works fine as long as both interfaces have the same width. If they have different widths it is possible that the length is rounded up to different values on the source and destination side. In that case the DMA will deadlock because the transfer lengths don't match and either not enough of too much data is delivered from the source to the destination side. Currently it is up to software to make sure that such an invalid configuration is not possible. Also enforce this requirement in the DMAC itself by setting the LSBs of the transfer length to a fixed 1 so that the length is always aligned to the widest interface. Software can also use this to discover the length alignment requirement, by first writing a zero to the length register and then reading the register back. The LSBs of the read back value will be non-zero indicating the alignment requirement. In a similar way the stride needs to be aligned to the width of its respective interface, so the generated addresses stay aligned. Enforce this in the same way by keeping the LSBs cleared. Increment the minor version number to reflect these changes. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2017-09-21 09:15:45 +00:00
write_reg_and_update('h418, {$random} & LENGTH_MASK | LENGTH_ALIGN_MASK);
write_reg_and_update('h41c, {$random} & LENGTH_MASK);
axi_dmac: Enforce transfer length and stride alignments In its current implementation the DMAC requires that the length of a transfer is aligned to the widest interface. E.g. if the widest interface is 128 bits wide the length of the transfer needs to be a multiple of 16 bytes. If the requested length is not aligned to the interface width it will be rounded up. This works fine as long as both interfaces have the same width. If they have different widths it is possible that the length is rounded up to different values on the source and destination side. In that case the DMA will deadlock because the transfer lengths don't match and either not enough of too much data is delivered from the source to the destination side. Currently it is up to software to make sure that such an invalid configuration is not possible. Also enforce this requirement in the DMAC itself by setting the LSBs of the transfer length to a fixed 1 so that the length is always aligned to the widest interface. Software can also use this to discover the length alignment requirement, by first writing a zero to the length register and then reading the register back. The LSBs of the read back value will be non-zero indicating the alignment requirement. In a similar way the stride needs to be aligned to the width of its respective interface, so the generated addresses stay aligned. Enforce this in the same way by keeping the LSBs cleared. Increment the minor version number to reflect these changes. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
2017-09-21 09:15:45 +00:00
write_reg_and_update('h420, {$random} & STRIDE_MASK);
write_reg_and_update('h424, {$random} & STRIDE_MASK);
check_all_registers("Transfer setup 2");
/* Start transfer */
write_reg_and_update('h400, 'h01);
write_reg_and_update('h408, 'h01);
set_reset_reg_value('h428, 32'h00000000);
set_reset_reg_value('h448, 24'h000000);
set_reset_reg_value('h44c, 32'hxxxxxxxx);
set_reset_reg_value('h450, 2'bX);
check_all_registers("Transfer submitted");
@(posedge clk) request_ready <= 1'b1;
/* Interrupt pending */
set_reset_reg_value('h84, 'h01);
set_reset_reg_value('h88, 'h01);
/* Transfer ID */
set_reset_reg_value('h404, 'h01);
/* Tansfer pending */
set_reset_reg_value('h408, 'h00);
set_reset_reg_value('h428, 32'h80000000);
set_reset_reg_value('h448, 24'h000080);
set_reset_reg_value('h44c, 32'h00000080);
set_reset_reg_value('h450, 'h0);
check_all_registers("Transfer accepted");
@(posedge clk) response_eot <= 1'b1;
@(posedge clk) response_eot <= 1'b0;
/* Interrupt registers */
set_reset_reg_value('h84, 'h01);
set_reset_reg_value('h88, 'h01);
set_reset_reg_value('h428, 'h00);
set_reset_reg_value('h42c, 'h00);
set_reset_reg_value('h44c, 32'h00000080);
set_reset_reg_value('h450, 'h0);
set_reset_reg_value('h448, 24'h000080);
check_all_registers("Transfer completed");
/* Clear interrupts */
write_reg('h84, 'h01);
set_reset_reg_value('h84, 'h00);
set_reset_reg_value('h88, 'h00);
check_all_registers("Clear interrupts 1");
write_reg('h84, 'h02);
set_reset_reg_value('h84, 'h00);
set_reset_reg_value('h88, 'h00);
check_all_registers("Clear interrupts 2");
/* Check that reset works for all registers */
do_trigger_reset();
initialize_expected_reg_mem();
write_reg_and_update('h40c, 'h00);
set_reset_reg_value('h44c, 32'h00000080);
check_all_registers("Reset 1");
invert_all_registers();
do_trigger_reset();
write_reg_and_update('h40c, 'h00);
set_reset_reg_value('h44c, 32'h00000080);
check_all_registers("Reset 2");
end
axi_dmac_regmap #(
.ID(0),
.DISABLE_DEBUG_REGISTERS(0),
.BYTES_PER_BEAT_WIDTH_DEST(BYTES_PER_BEAT),
.BYTES_PER_BEAT_WIDTH_SRC(BYTES_PER_BEAT),
.DMA_AXI_ADDR_WIDTH(DMA_AXI_ADDR_WIDTH),
.DMA_LENGTH_WIDTH(DMA_LENGTH_WIDTH),
.DMA_LENGTH_ALIGN(LENGTH_ALIGN),
.DMA_CYCLIC(1),
.BYTES_PER_BURST_WIDTH(7),
.HAS_DEST_ADDR(1),
.HAS_SRC_ADDR(1),
.DMA_2D_TRANSFER(1),
.SYNC_TRANSFER_START(0)
) i_axi (
.s_axi_aclk(s_axi_aclk),
.s_axi_aresetn(s_axi_aresetn),
.s_axi_awvalid(s_axi_awvalid),
.s_axi_awaddr(s_axi_awaddr),
.s_axi_awready(s_axi_awready),
.s_axi_awprot(s_axi_awprot),
.s_axi_wvalid(s_axi_wvalid),
.s_axi_wdata(s_axi_wdata),
.s_axi_wstrb(s_axi_wstrb),
.s_axi_wready(s_axi_wready),
.s_axi_bvalid(s_axi_bvalid),
.s_axi_bresp(s_axi_bresp),
.s_axi_bready(s_axi_bready),
.s_axi_arvalid(s_axi_arvalid),
.s_axi_araddr(s_axi_araddr),
.s_axi_arready(s_axi_arready),
.s_axi_arprot(s_axi_arprot),
.s_axi_rvalid(s_axi_rvalid),
.s_axi_rready(s_axi_rready),
.s_axi_rresp(s_axi_rresp),
.s_axi_rdata(s_axi_rdata),
.ctrl_enable(ctrl_enable),
.ctrl_pause(ctrl_pause),
.request_valid(request_valid),
.request_ready(request_ready),
.request_dest_address(request_dest_address),
.request_src_address(request_src_address),
.request_x_length(request_x_length),
.request_y_length(request_y_length),
.request_dest_stride(request_dest_stride),
.request_src_stride(request_src_stride),
.request_last(request_last),
.request_sync_transfer_start(request_sync_transfer_start),
.irq(irq),
.response_eot(response_eot),
.response_measured_burst_length(response_measured_burst_length),
.response_partial(response_partial),
.response_valid(response_valid),
.response_ready(response_ready),
.dbg_src_addr(VAL_DBG_SRC_ADDR),
.dbg_dest_addr(VAL_DBG_DEST_ADDR),
.dbg_status(VAL_DBG_STATUS),
.dbg_ids0(VAL_DBG_IDS0),
.dbg_ids1(VAL_DBG_IDS1)
);
endmodule