Using Vivado Simulator to Simulate a perippheral with AXI4-Lite interface
The Integrated Logic Analyzer is a very powerful tool for debugging hardware. However, Since it adds complexity to your design, implementation may take a long time. Becuase of this, it’s best practice to write a test bench to simulate IP cores before implementation. Simulation of an AXI4 IPcore requires emulating the signals on the AXI bus for write and read transactions. The Following Guide goes over the process of creating a AXI4 IP testbench and running simulation.
Step 1: Create a Test Bench for the myled AXI4-Lite Custom IP Core
Open up the block design. Right click on myled IP and choose Edit in IP Packager as shown in figure 1. In your myled IP packager you can now create a a test bench and simulate your AXI4-Lite interfaces.

Right click on sim_1 under simulation sources and click Add Sources.

In the pop-up Add Sources dialog, select Add or create simulation sources and click Next.

Click on Create File to create the test bench file.

In the pop up window, name the file myled_tb
and select the hdl folder under myled_1.0 which can be found in your ip_repo directory. Click Finish to add myled_tb.v to simulation set sim_1 in the IP packager project.

Step 2: Form A Test Plan
Instead of going straight to writing a testbench, we should come up with a plan for how to simulate the IPcore. For our purpose we would like to simulate writing values, via the AXI bus, to the slave register which drives the LEDs. After the write is simulated properly, the 4 LED signals should reflect the 4 LSB’s of the 1st slave register.
Your plan, of course, should depend on your application. Make sure you have a suitable plan for each simulation you need.
Step 3: Implement the Test in Verilog
Instead of writing a testbench where the AXI4 signals are manipulated again and again in the main body of code, it would be best to write a single reusable sequence of code that takes an argument and drives signals accordingly. This can be accomplished using the task
structure in Verilog. A task can be written and called in verilog to execute a sequence of operations. They can be parameterized to include inputs and outputs which can affect the operations and return results. Using tasks to handle simulation of AXI transactions will reduce code bulk in the main body of your testbench as well as improve readability.
For simulating the AXI bus, your tasks will need to drive the address, data, and response lines relevant to the transaction. The task will also need to respond to the handhsaking signals from the AXI4-lite slave (in this case the LED IPcore).
First step in writing a AXI4 testbench is to define all the signals used by the AXI4-lite interface. Since our Task will be controlling the Master signals, those should be set as reg
type. The slave signals will be driven by our IPcore and should be wire
's. Next, instantiate your IP core, making the appropriate port connections to the previously defined AXI4-lite signals.
`timescale 1ns /1ps
module myled_tb();
//clock and reset_n signals
reg aclk =1'b0;
reg arstn = 1'b0;
//Write Address channel (AW)
reg [31:0] write_addr =32'd0; //Master write address
reg [2:0] write_prot = 3'd0; //type of write(leave at 0)
reg write_addr_valid = 1'b0; //master indicating address is valid
wire write_addr_ready; //slave ready to receive address
//Write Data Channel (W)
reg [31:0] write_data = 32'd0; //Master write data
reg [3:0] write_strb = 4'd0; //Master byte-wise write strobe
reg write_data_valid = 1'b0; //Master indicating write data is valid
wire write_data_ready; //slave ready to receive data
//Write Response Channel (WR)
reg write_resp_ready = 1'b0; //Master ready to receive write response
wire [1:0] write_resp; //slave write response
wire write_resp_valid; //slave response valid
//Read Address channel (AR)
reg [31:0] read_addr = 32'd0; //Master read address
reg [2:0] read_prot =3'd0; //type of read(leave at 0)
reg read_addr_valid = 1'b0; //Master indicating address is valid
wire read_addr_ready; //slave ready to receive address
//Read Data Channel (R)
reg read_data_ready = 1'b0; //Master indicating ready to receive data
wire [31:0] read_data; //slave read data
wire [1:0] read_resp; //slave read response
wire read_data_valid; //slave indicating data in channel is valid
//LED output of the IPcore
wire [3:0] LED;
//Instantiation of LED IP
myled_v1_0 # (
.C_S_AXI_DATA_WIDTH(32),
.C_S_AXI_ADDR_WIDTH(32)
) myled_v1_0_inst (
.led(LED),
.s_axi_aclk(aclk),
.s_axi_aresetn(arstn),
.s_axi_awaddr(write_addr),
.s_axi_awprot(write_prot),
.s_axi_awvalid(write_addr_valid),
.s_axi_awready(write_addr_ready),
.s_axi_wdata(write_data),
.s_axi_wstrb(write_strb),
.s_axi_wvalid(write_data_valid),
.s_axi_wready(write_data_ready),
.s_axi_bresp(write_resp),
.s_axi_bvalid(write_resp_valid),
.s_axi_bready(write_resp_ready),
.s_axi_araddr(read_addr),
.s_axi_arprot(read_prot),
.s_axi_arvalid(read_addr_valid),
.s_axi_arready(read_addr_ready),
.s_axi_rdata(read_data),
.s_axi_rresp(read_resp),
.s_axi_rvalid(read_data_valid),
.s_axi_rready(read_data_ready)
);
endmodule
Next add an always block to periodically toggle the clock value.
//clock signal
always
#5 aclk <=~aclk;
Now create an initial block to deassert resest and call our task. We’ll use a for loop so we can test writing different values.
integer i;
initial
begin
arstn = 0;
i=0;
#20 arstn=1;
for(i=0;i<=32'hF;i=i+1)
#20 axi_write(32'd0,i); //write i to slv_reg0\
$finish;
end
Now we can Define the axi_write task. Make sure you do this within the testbench module.
task axi_write;
input [31:0] addr;
input [31:0] data;
begin
#3 write_addr <= addr; //Put write address on bus
write_data <= data; //put write data on bus
write_addr_valid <= 1'b1; //indicate address is valid
write_data_valid <= 1'b1; //indicate data is valid
write_resp_ready <= 1'b1; //indicate ready for a response
write_strb <= 4'hF; //writing all 4 bytes
//wait for one slave ready signal or the other
wait(write_data_ready || write_addr_ready);
@(posedge aclk); //one or both signals and a positive edge
if(write_data_ready&&write_addr_ready)//received both ready signals
begin
write_addr_valid<=0;
write_data_valid<=0;
end
else //wait for the other signal and a positive edge
begin
if(write_data_ready) //case data handshake completed
begin
write_data_valid<=0;
wait(write_addr_ready); //wait for address address ready
end
else if(write_addr_ready) //case address handshake completed
begin
write_addr_valid<=0;
wait(write_data_ready); //wait for data ready
end
@ (posedge aclk);// complete the second handshake
write_addr_valid<=0; //make sure both valid signals are deasserted
write_data_valid<=0;
end
//both handshakes have occured
//deassert strobe
write_strb<=0;
//wait for valid response
wait(write_resp_valid);
//both handshake signals and rising edge
@(posedge aclk);
//deassert ready for response
write_resp_ready<=0;
//end of write transaction
end
endtask;
Step 4: Simulate the IP Core
Run your simulation like you would with any other Vivado project. Verify that the LED signal reflects the values given to the axi_write task.
Step 5: Package IP with Test Bench
You can repackage IP with the test bench so that next time you re-create a project to edit your IP, the test bench will be saved and imported as well. To do this, go to Package IP tab and select File Groups. Click Merge changes from file groups wizard in the yellow banner.

After changes are merged to file group, you can see the test bench file hdl/myled_tb.v under folder Test Bench.

Now go to Review and Package, click Re-package IP button to write IP meta-data back to IP repository.