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