Search:

PmWiki

pmwiki.org

edit SideBar

Main / Verification

Back to FPGAs

SV Xilinx AXI VIP Block

To 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 Templates

Example, 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

Tips

Testbench Clocks

Note 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/O

For 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.

Logs

When 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:

  • elaborate.log
  • xvhdl.log
  • xvlog.log

These can be found in the <project_name>.sim/sim_1/behav/xsim/ directory.

Mixed-Language Sim

These 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:

  • integer
  • real
  • string
  • boolean

So std_logic is out.

Sim View of Primitives

By 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.

Troubleshooting

AXI VIP in a mixed-lang design

When 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:
ERROR: [VRFC 10-900] incompatible complex type assignment

Sub-module inout ports in a mixed-lang design

Vivado 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.


Page last modified on June 07, 2023, at 02:00 PM