Hardware Logic Design
First, a Verilog module performing basic bitwisee operations is created to serve as the core logic for the custom IP. This module will be instantiated with in the AXI wrapper.
module bitwise_logic_unit (
input wire [31:0] operand_a,
input wire [31:0] operand_b,
output wire [31:0] result_and,
output wire [31:0] result_or,
output wire [31:0] result_not
);
// Perform bitwise AND, OR, and NOT operations
assign result_and = operand_a & operand_b;
assign result_or = operand_a | operand_b;
assign result_not = ~operand_a;
endmodule
AXI IP Integration
After creating a new custom AXI IP using the wizard, the next step involves modifying the AXI slave module to instantiate the logic unit above. The inputs of the custom logic are connected to the AXI slave registers (slv_reg0 and slv_reg1), allowing the PS to write operands. The outputs are routed to internal wires, which are then mapped to the read registers (slv_reg2, slv_reg3, etc.).
Inside the AXI slave module, declare the wires for the logic outputs and modify the read multiplexer logic:
// Internal wires for logic module outputs
wire [31:0] w_result_and;
wire [31:0] w_result_or;
wire [31:0] w_result_not;
// Instantiate the custom logic
bitwise_logic_unit u_logic_core (
.operand_a (slv_reg0),
.operand_b (slv_reg1),
.result_and (w_result_and),
.result_or (w_result_or),
.result_not (w_result_not)
);
// Read address decoding logic
always @(*) begin
case (axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB])
3'h0 : reg_data_out <= slv_reg0;
3'h1 : reg_data_out <= slv_reg1;
3'h2 : reg_data_out <= w_result_and; // Read AND result
3'h3 : reg_data_out <= w_result_or; // Read OR result
3'h4 : reg_data_out <= w_result_not; // Read NOT result
default: reg_data_out <= 0;
endcase
end
Once the modifications are complete, package the IP. It is often necessary to re-package the IP if the initial merge fails or if the IP catalog does not update correctly. After packaging, the IP can be added to the Block Design (BD). Since the logic is self-contained within the AXI interface, no external I/O ports are required other than the AXI connection to the Processing System.
Software Application Development
With the hardware exported and the bitstream generated, a software application is developed in the SDK (or Vitis) to drive the IP. A standard "Hello World" template serves as a good starting point, as it includes necessary UART setup. The application writes values to the IP registers and reads back the computed results.
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xil_io.h"
#include "xparameters.h"
// Define Base Address and Register Offsets
#define IP_BASE_ADDR XPAR_MY_LOGIC_IP_0_S00_AXI_BASEADDR
#define REG_IN_A 0x00
#define REG_IN_B 0x04
#define REG_OUT_AND 0x08
#define REG_OUT_OR 0x0C
#define REG_OUT_NOT 0x10
int main() {
init_platform();
unsigned int val_a = 150;
unsigned int val_b = 90;
unsigned int res_and, res_or, res_not;
xil_printf("Starting Custom IP Logic Test...\n\r");
// Write inputs to the custom IP
Xil_Out32(IP_BASE_ADDR + REG_IN_A, val_a);
Xil_Out32(IP_BASE_ADDR + REG_IN_B, val_b);
// Read calculated results from the custom IP
res_and = Xil_In32(IP_BASE_ADDR + REG_OUT_AND);
res_or = Xil_In32(IP_BASE_ADDR + REG_OUT_OR);
res_not = Xil_In32(IP_BASE_ADDR + REG_OUT_NOT);
// Print results to UART terminal
xil_printf("Input A: %d\n\r", val_a);
xil_printf("Input B: %d\n\r", val_b);
xil_printf("Result AND: %d\n\r", res_and);
xil_printf("Result OR : %d\n\r", res_or);
xil_printf("Result NOT: %d\n\r", res_not);
cleanup_platform();
return 0;
}
Terminal Verification
To observe the output, configure the Terminal window in the SDK (or a standalone terminal tool like Putty/TeraTerm) to connect to the appropriate COM port with the correct baud rate (typically 115200 for Zynq). Upon running the application, the calculated values will be displayed, confirming that the custom AXI IP correctly processes data and that the PS can read the results via the AXI interface.