Introduction
In part 2, we have built the AXI4-Lite wrapper for GCD core and counters. In this part, we are going to create a testbench file for simulating the AXI4-Lite wrapper. This testbench simulates as if the CPU writes/reads address/data to or from the AXI4-Lite wrapper.
You can get the full source code of this part from here.
Testbench File
The following code shows the testbench file for simulating the axi_gcd_performance.v. First, we instantiate the axi_gcd_performance module as dut, in line 26-47. Then, we define Verilog task for writing (axi_write) and reading (axi_read) to and from the dut.
To simulate the AXI4-Lite write signal, we should set the address (on s_axi_awaddr) of the register that we want to write to, and set the s_axi_awvalid to one. After that, set the data on the s_axi_wdata, and set the s_axi_wvalid to one.
To simulate the AXI4-Lite read signal, we should set the address (on s_axi_araddr) of the register that we want to read from, and set the s_axi_arvalid to one. After that, the data will be available on the s_axi_ardata.
In line 76-88, we can send the input A and B to the GCD core. Then, we should initiate the operation by writing one to the START bit in status register. After that, we should wait for several clock cycles until the operation is finished. In real implementation, we should not wait for several clock cycles. Instead, we should wait until the READY bit in status register becomes one. We may use polling or interrupt method to wait for this bit becomes one. Finally, read the output R.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
`timescale 1ns / 1ps module axi_gcd_performance_tb(); localparam T = 10; reg aclk; reg aresetn; wire s_axi_arready; reg [31:0] s_axi_araddr; reg s_axi_arvalid; wire s_axi_awready; reg [31:0] s_axi_awaddr; reg s_axi_awvalid; reg s_axi_bready; wire [1:0] s_axi_bresp; wire s_axi_bvalid; reg s_axi_rready; wire [31:0] s_axi_rdata; wire [1:0] s_axi_rresp; wire s_axi_rvalid; wire s_axi_wready; reg [31:0] s_axi_wdata; reg [3:0] s_axi_wstrb; reg s_axi_wvalid; axi_gcd_performance dut ( .aclk(aclk), .aresetn(aresetn), .s_axi_araddr(s_axi_araddr), .s_axi_arready(s_axi_arready), .s_axi_arvalid(s_axi_arvalid), .s_axi_awaddr(s_axi_awaddr), .s_axi_awready(s_axi_awready), .s_axi_awvalid(s_axi_awvalid), .s_axi_bready(s_axi_bready), .s_axi_bresp(s_axi_bresp), .s_axi_bvalid(s_axi_bvalid), .s_axi_rdata(s_axi_rdata), .s_axi_rready(s_axi_rready), .s_axi_rresp(s_axi_rresp), .s_axi_rvalid(s_axi_rvalid), .s_axi_wdata(s_axi_wdata), .s_axi_wready(s_axi_wready), .s_axi_wstrb(s_axi_wstrb), .s_axi_wvalid(s_axi_wvalid) ); always begin aclk = 0; #(T/2); aclk = 1; #(T/2); end initial begin // *** Initial value *** s_axi_awaddr = 0; s_axi_awvalid = 0; s_axi_wstrb = 0; s_axi_wdata = 0; s_axi_wvalid = 0; s_axi_bready = 1; s_axi_araddr = 0; s_axi_arvalid = 0; s_axi_rready = 1; // *** Reset *** aresetn = 0; #(T*5); aresetn = 1; #(T*5); // *** Calculate gcd(35,25) *** axi_write(8'h04, 35); // A = 35 axi_write(8'h08, 25); // B = 25 axi_write(8'h00, 1); // START = 1 #(T*10); axi_read(8'hc); // Read R // *** Calculate gcd(128,72) *** axi_write(8'h04, 128); // A = 128 axi_write(8'h08, 72); // B = 72 axi_write(8'h00, 1); // START = 1 #(T*20); axi_read(8'hc); // Read R end task axi_write; input [31:0] awaddr; input [31:0] wdata; begin // *** Write address *** s_axi_awaddr = awaddr; s_axi_awvalid = 1; #T; s_axi_awvalid = 0; // *** Write data *** s_axi_wdata = wdata; s_axi_wstrb = 4'hf; s_axi_wvalid = 1; #T; s_axi_wvalid = 0; #T; end endtask task axi_read; input [31:0] araddr; begin // *** Read address *** s_axi_araddr = araddr; s_axi_arvalid = 1; #T; s_axi_arvalid = 0; #T; end endtask endmodule |
Result
The simulation was done using Vivado simulator. The following figure shows the timing diagram of the AXI4-Lite wrapper. Firstly, we write the write address. Secondly, we write the write data. Then, the GCD core starts the operation. Thirdly, we write the read address. Fourthly, we read the read data.
Every address/data transaction in AXI4-Lite occurs if both of the *valid and *ready signal are one. This is called handshake. The AXI4 master (CPU) will stretch the *valid signal if the *ready signal is zero, i.e. it waits until the *read becomes one. You can learn more on this AXI4-Lite protocol from the AXI protocol specification.
Summary
In this tutorial, we have built the testbench file for simulating the AXI4-Lite GCD. This testbench file is useful for verifying our AXI4-Lite module before we synthesize the design. Furthermore, by simulating the AXI4-Lite module, we can have a better understanding on how the AXI4-Lite protocol works.