|
Main / Verification
SV Xilinx AXI VIP BlockTo compare results, create axi_transaction objects and use the do_compare function, like so: axi_transaction rd_trans; axi_transaction wr_trans; //run the write and read here if (rd_trans.do_compare(wr_trans) == 0) //etc Testbench TemplatesExample, top level
`timescale 1ns / 1ps
module tb_sim;
`include "bch_params.vh"
parameter T = 4;
parameter OPTION = "SERIAL";
parameter DATA_BITS = 4096;
parameter BITS = 1024;
parameter REG_RATIO = 1;
parameter SEED = 0;
localparam BCH_PARAMS = bch_params(DATA_BITS, T);
reg [31:0] seed = SEED;
initial begin
$dumpfile("test.vcd");
$dumpvars(0);
end
localparam TCQ = 1;
reg clk = 0;
reg reset = 0;
reg [DATA_BITS-1:0] din = 0;
reg [$clog2(T+2)-1:0] nerr = 0;
reg [`BCH_CODE_BITS(BCH_PARAMS)-1:0] error = 0;
function [DATA_BITS-1:0] randk;
input [31:0] useless;
integer i;
begin
for (i = 0; i < (31 + DATA_BITS) / 32; i = i + 1)
if (i * 32 > DATA_BITS) begin
if (DATA_BITS % 32)
/* Placate isim */
randk[i*32+:(DATA_BITS%32) ? (DATA_BITS%32) : 1] = $random(seed);
end else
randk[i*32+:32] = $random(seed);
end
endfunction
function integer n_errors;
input [31:0] useless;
integer i;
begin
n_errors = (32'h7fff_ffff & $random(seed)) % (T + 1);
end
endfunction
function [`BCH_CODE_BITS(BCH_PARAMS)-1:0] rande;
input [31:0] nerr;
integer i;
begin
rande = 0;
while (nerr) begin
i = (32'h7fff_ffff & $random(seed)) % (`BCH_CODE_BITS(BCH_PARAMS));
if (!((1 << i) & rande)) begin
rande = rande | (1 << i);
nerr = nerr - 1;
end
end
end
endfunction
reg encode_start = 0;
wire wrong;
wire ready;
reg active = 0;
sim #(BCH_PARAMS, OPTION, BITS, REG_RATIO) u_sim(
.clk(clk),
.reset(1'b0),
.data_in(din),
.error(error),
.ready(ready),
.encode_start(active),
.wrong(wrong)
);
always
#5 clk = ~clk;
always @(posedge wrong)
#10 $finish;
reg [31:0] s;
always @(posedge clk) begin
if (ready) begin
s = seed;
#1;
din <= randk(0);
#1;
nerr <= n_errors(0);
#1;
error <= rande(nerr);
#1;
active <= 1;
$display("%b %d flips - %b (seed = %d)", din, nerr, error, s);
end
end
initial begin
$display("GF(2^%1d) (%1d, %1d/%1d, %1d) %s",
`BCH_M(BCH_PARAMS), `BCH_N(BCH_PARAMS), `BCH_K(BCH_PARAMS),
DATA_BITS, `BCH_T(BCH_PARAMS), OPTION);
@(posedge clk);
@(posedge clk);
reset <= #1 1;
@(posedge clk);
@(posedge clk);
reset <= #1 0;
end
endmodule
AXI Stimulus (useful if you can't use the VIP)
initial
begin
S_AXI_ACLK = 0;
S_AXI_ARESETN <= 0;
S_AXI_AWADDR <= 0;
S_AXI_WDATA <= 0;
S_AXI_AWVALID <= 0;
S_AXI_WVALID <= 0;
S_AXI_ARADDR <= 0;
S_AXI_ARVALID <= 0;
S_AXI_RREADY <= 0;
S_AXI_BREADY <= 0;
m_dac_ready <= 1;
m_dac_aclk <= 0;
s_axis_txdata_valid <= 1;
s_axis_txdata_data <= 0;
//wire [31:0] control = slv_reg[0]; //bytes[7:0]
//0: transmit 0
//1: transmit internal pn
//2: transmit raw bits
//3: differential manchester encode
//byte [8] hardware resetn
// byte [9] loadn/run
//wire [31:0] carrier_bias = slv_reg[1]; //16 bit
//wire [31:0] interp_rate = slv_reg[2]; //8 bit
//wire [31:0] dopplar = slv_reg[3]; //24 bit
//wire [31:0] gain = slv_reg[4]; // 16 bit
@ (posedge S_AXI_ACLK);
@ (posedge S_AXI_ACLK);
@ (posedge S_AXI_ACLK) S_AXI_ARESETN <= 1;
@ (posedge S_AXI_ACLK);
@ (posedge S_AXI_ACLK) axi_write32(1*4,'h0000);
@ (posedge S_AXI_ACLK) axi_write32(2*4,'b1);
@ (posedge S_AXI_ACLK) axi_write32(3*4,'h0004_0000);
@ (posedge S_AXI_ACLK) axi_write32(4*4,'h3800);// gain h3800 max output
// setup before run
@ (posedge S_AXI_ACLK) axi_write32(0,'b0000_0000_0000_0011);
repeat(200) @ (posedge S_AXI_ACLK);
@ (posedge S_AXI_ACLK) axi_write32(0,'b0000_0001_0000_0011);
repeat(200) @ (posedge S_AXI_ACLK);
@ (posedge S_AXI_ACLK) axi_write32(0,'b0000_0011_0000_0011);
// @ (posedge S_AXI_ACLK) axi_write32(3,5);
wait (s_axis_txdata_ready == 1);
@ (posedge S_AXI_ACLK) s_axis_txdata_data <= 'b0000_0000;
@ (posedge S_AXI_ACLK)
wait (s_axis_txdata_ready == 1);
@ (posedge S_AXI_ACLK)
wait (s_axis_txdata_ready == 1);
@ (posedge S_AXI_ACLK) s_axis_txdata_data <= 'b1111_1111;
@ (posedge S_AXI_ACLK)
wait (s_axis_txdata_ready == 1);
@ (posedge S_AXI_ACLK)
wait (s_axis_txdata_ready == 1);
@ (posedge S_AXI_ACLK) s_axis_txdata_data <= 'b0101_0101;
@ (posedge S_AXI_ACLK)
wait (s_axis_txdata_ready == 1);
@ (posedge S_AXI_ACLK)
wait (s_axis_txdata_ready == 1);
@ (posedge S_AXI_ACLK) s_axis_txdata_data <= 0;
@ (posedge S_AXI_ACLK)
wait (s_axis_txdata_ready == 1);
@ (posedge S_AXI_ACLK)
wait (s_axis_txdata_ready == 1);
@ (posedge S_AXI_ACLK) s_axis_txdata_data <= 1;
@ (posedge S_AXI_ACLK);
@ (posedge S_AXI_ACLK);
@ (posedge S_AXI_ACLK);
@ (posedge S_AXI_ACLK);
@ (posedge S_AXI_ACLK);
@ (posedge S_AXI_ACLK) axi_read32(3,rdata);
@ (posedge S_AXI_ACLK);
@ (posedge S_AXI_ACLK);
@ (posedge S_AXI_ACLK);
@ (posedge S_AXI_ACLK) axi_read32(0,rdata);
@ (posedge S_AXI_ACLK);
@ (posedge S_AXI_ACLK);
@ (posedge S_AXI_ACLK);
repeat(1000) @ (posedge S_AXI_ACLK);
end
task axi_write32;
input [3+2- 1 : 0] addr;
input [32 - 1 : 0] data;
begin
@(posedge S_AXI_ACLK)
begin
S_AXI_WDATA <= data;
S_AXI_AWADDR <= addr;
S_AXI_AWVALID <= 1;
S_AXI_WVALID <= 1;
S_AXI_WSTRB <= 'b1111;
S_AXI_BREADY <= 1;
end
wait(S_AXI_AWREADY && S_AXI_WREADY);
@(posedge S_AXI_ACLK)
begin
S_AXI_AWVALID <= 0;
S_AXI_WVALID <= 0;
S_AXI_BREADY <= 0;
end
end
endtask
task axi_read32;
input [6 - 1 : 0] addr;
output [32 - 1 : 0] data;
begin
@(posedge S_AXI_ACLK)
begin
S_AXI_ARADDR <= addr;
S_AXI_ARVALID <= 1;
S_AXI_RREADY <= 1;
end
wait(S_AXI_ARREADY);
@(posedge S_AXI_ACLK)
begin
S_AXI_ARVALID <= 0;
end
wait(S_AXI_RVALID);
@(posedge S_AXI_ACLK)
begin
S_AXI_ARVALID <= 0;
S_AXI_RREADY <= 0;
data <= S_AXI_RDATA;
end
end
endtask
TipsTestbench ClocksNote that you don't want to put multiple clock drivers inside the same always block because the delays will sum. // do this always #2.75 FPGA_AXI_CLK = ~FPGA_AXI_CLK; always #10 ADC_CLK = ~ADC_CLK; // not this always begin #2.75 FPGA_AXI_CLK = ~FPGA_AXI_CLK; #10 ADC_CLK = ~ADC_CLK; end File I/OFor Vivado Simulator, the relative paths in the VHDL/Verilog file are relative to the xsim folder location. Note that for System Verilog $fopen command, the default is to open a file for write. Need to add the "r" argument to open for read. LogsWhen you run a simulation, Vivado doesn't print out all the info/warnings/errors on the console for some reason. For the complete list, you need to examine the log files:
These can be found in the <project_name>.sim/sim_1/behav/xsim/ directory. Mixed-Language SimThese are a bit problematic. Note that the data types of generics module parameters are restricted in Vivado. The only VHDL types that can be used crossing a Verilog boundary are:
So std_logic is out. Sim View of PrimitivesBy default, Vivado will not allow you to add signals inside primitives to a waveform. In fact, this nefariously hides errors with obsoleted primitives. "ERROR : The following component IODELAY2 is not supported for retargeting in this architecture. Please modify your source code to use supported primitives. The complete list of supported primitives for this architectures is provided in the 7 Series HDL Libraries Guide available on www.xilinx.com." You won't see this error until you go to the Project Manager Settings and Simulation tab and change the xsim.elaborate.debug_level to all instead of typical. In theory, this should also allow you to view the primitives in the Scope hierarchy. TroubleshootingAXI VIP in a mixed-lang designWhen trying to use AXI VIP, what is this error? Node ACLK is not annotated. It's a real piece of crap, can't find any solution. Seems to appear randomly when certain IP blocks are hooked up. There are some hints: "The error only occurs during mixed-mode simulations. Set the target language to Verilog and regenerate all the outputs so there are no VHDL source files, and then simulation should run." But there doesn't seem to be way to do this. Turns out this line is the culprit: axi_mst_agent mst_agent; When you choose Verilog as the simulation target language, if there are underlying VHDL modules then the names of the signals will be changed when you look at the waveform. Seems like using the Mixed option will keep the names correctly. If there's a mismatch, this may be one of the errors you see: Sub-module inout ports in a mixed-lang designVivado simulator seems confused about an inout port in a VHDL submodule underneath a Verilog module. Even if only driven one way, it can't handle driving the line from the Verilog level, instead showing X despite only one driver. This works fine with a VHDL level testbench driving the signal, however. It would seem using inout ports is a bit of a problem at a sub-module level anyway: Generally you do not use inout except at the very top level of the design. This is because tristate signals are not routable inside the FPGA. So all internal modules should not use tristate signals, and anything that needs a tristate should split it out into separate in, out, and output enable signals, which you can then convert to tristate signals at the top-level right when you make the connection to the physical pin. I think the only thing you can do with inout signals is to directly connect them without using assign. So you just need to specify the name of the module pin when you instantiate the sub-module. There should be no need to declare any wires or assign anything. |