Project 2 Creating custom IP for ZYNQ's processing system

An introduction to Zynq APSoC Design Flow



This project presents a simple digital system that includes both a custom IP block in the FPGA, and control software running on the ARM. Vivado’s “IP Integrator” tool is introduced and used to define the hardware system.

Note: “IP block”, or intellectual property block, refers to a reusable logic circuit that belongs to some individual or entity. New, original IP blocks can be created in Vivado, and preexisting IP blocks can be downloaded and/or included using the Vivado IP Manager. Some of the downloadable IP is produced by Xilinx, and some by third parties; some IP is free and can be included in any design, and some requires a purchased license. Visit the Xilinx Intellectual Property page for more information.

The IP Integrator tool allows IP blocks to be created (or selected from the IP library) and attached to the ARM’s peripheral bus. Any newly created IP blocks can be defined and implemented using Vivado/Verilog. Once the entire hardware system has been defined, the ZYNQ/Blackboard can be configured, and the SDK tool can be used to write system software. Before starting a system design like this, you should be comfortable using the ARM/SDK and FPGA/Vivado systems independently.


This project introduces all the steps required to configure the entire ZYNQ system. You are already familiar with configuring and using the ARM and the FPGA independently, so what remains is configuring these two major components to work together. The processor accesses FPGA circuits by reading and writing registers attached to the processor’s peripheral bus, so any FPGA circuits that will work with the processor must include such registers. In addition to the processor-accessible registers, the FPGA circuit can include as many features as desired.

As an example, consider Blackboard’s seven-segment display device (7sd). The 7sd is connected to pins on the FPGA, so the processor must write to registers in the FPGA to access the display. The registers could simply pass data through the FPGA, leaving the processor to deal with display timing by writing to individual bits in the anode register (i.e., the processor would write the 4-bit data values 0001, 0010, 0100, 1000 to the anode register in succession to turn on the digits, one at a time, at least 60 times per second). The processor would also need to write cathode data synchronous to driving the anodes, to cause the correct data to be displayed in each digit (this is shown in the “simple” controller on the lower left). Of course, a better system design would put 7sd timing control in a hardware circuit, thereby relieving the processor from having to control the display in real-time. In this case, the processor would only need to interact with data registers, and the hardware circuit would take care of getting the right data to the cathodes at the right time (shown in the “typical” controller on the lower right).

Figure 1. Two different ways to build a 7-segment IP block - one simple methods that forces the processor to control timing, and a better method that creates the required timing signals, and only consumes display data.
Figure 1. Two different ways to build a 7-segment IP block - one simple methods that forces the processor to control timing, and a better method that creates the required timing signals, and only consumes display data.

The ARM uses a 32-bit (4GByte) bus. In the ZYNQ system, the lower 1GByte of the ARM bus is reserved for external DDR memory, the middle two GBytes are routed into the FGPA, and the upper 1 GByte is used for accessing on-chip peripherals (like the USB controller, Ethernet controller, interrupt controller, etc.). Thus, any 32-bit address with the two most significant bits set to 01 or 10 target the FPGA. Users can define an FPGA circuit to decode an address within that 2GByte range, and select a register for read or write access. (Note: Although it is possible to manually connect FPGA IP blocks to the system bus, it is far easier to let the IP Integrator tool create the needed bus connections - see the tutorial below).

Figure 2. ARM’s main bus and IP blocks
Figure 2. ARM’s main bus and IP blocks

The ZYNQ uses the industry-standard “Advanced Microcontroller Bus Architecture” (AMBA) bus to connect the ARM and the FPGA. AMBA, an open standard introduced by ARM in 1996, is the most widely used bus for connecting any microcontroller to any peripheral device. It is now recognized as the de-facto standard embedded controller system bus. More recently (in 2010), the AXI4 bus interface standard was added to specify a simpler bus more suited for use inside programmable devices. The ZYNQ devices use the AXI4 bus to connect the ARM and FPGA. You can read more about the AMBA and AXI bus at the AMBA wiki page, in Xilinx user guide UG761, and in various other web resources. Xilinx has also produced an introductory ZYNQ book that you can feely download here: ZYNQ book

Figure 3. The ZYNQ book
Figure 3. The ZYNQ book

In order to use the ZYNQ system effectively, you need to understand the AXI bus, and how to configure it for use with custom IP bocks. This project illustrates how to setup a ZYNQ system that includes a custom IP block, and the next project looks more closely at the AXI bus.

The ZYNQ SoC includes many interfaces, ports, and peripheral circuits, all of which must be properly configured for the chip to function properly in a given system. The system designer defines many of the required configuration settings at the time the circuit board is created, based on what physical devices are available on the ZYNQ system board (for example, the amount and speed of external memory, the main clock frequency, which peripherals are connected to which pins and ports, etc.). These settings can be defined using a tool in the Vivado IDE, and they can be saved in a configuration file (a .tcl script) that can be applied at startup so board users don’t need to recreate all the hardware-specific settings. Such a configuration file has been created for the Blackboard, and in the tutorial below you will download and apply the file to configure the board.

After applying the settings from the .tcl file provided, you can see an overview of the ZYNQ system, and examine configuration details. Take a few momements to examine the system configuration, and note the IP blocks that constitute the processing system. Many IP blocks connect to external pins through the MIO interface, and some connect to the FPGA (via the EMIO interface). You can see the pin connections for the QSPI ROM, the UART, the SPI bus, etc., and you can check default settings for some of the ports as well. You can also examine system clock configurations - the main system clock input is set to 33.3333Mhz (if you check the ZYNQ schematic, you’ll find that is the oscillator frequency loaded on the Blackboard). The 33.33MHz clock sets the base frequency from which all peripheral controller clock are derived. Note the DDR controller clock is set to 533.33MHz, the QSPI module is configured at 200MHz, and the first clock going to the FPGA is set at 100MHz (the FPGA also has in independent, external clock input, also at 100MHz). All of these settings can be changed, but if you change them for the Blackboard, it is likely your system will not work (so don’t).

When the ZYNQ system is programmed with a hardware definition file (.hdf), the settings in the .tcl script are an integral part of the overall chip configuration. But they are not the only part – more information can be added to further specify a given hardware system. In our case, we will define a new IP block and add it to the project, add AXI bus addresses so the ARM can access the IP block, define the IP behavior/function (this is the FPGA hardware configuration that forms the IP block), and then create a new .hdf file that can configure the entire ZYNQ hardware system. Once the .hdf file is complete, it can be used to program the ZYNQ hardware system, and then the SDK tool can be used to write software for the configured hardware system.

The ZYNQ device also contains an Artix FPGA. So far, you’ve considered the FPGA from a relatively abstract, high-level perspective – you’ve written and implemented Verilog projects, and trusted that somehow, after running the “implement” app in the IDE, the FPGA would behave according to your design. In upcoming projects, you’ll need to know more about what resources are available in the FPGA. The reading list is a good start to learning more about the FPGA hardware.


1. Verify the Demo Project

Follow this TUTORIAL: First Zynq Soc Project and then run the demo project on your blackboard. If you configured everything correctly, you should see the demo program; the first 4 non-RGB LED’s on the blackboard should turn on and off at regular intervals.

2. Test Various Blinking Patterns by modifying the Software

Modify the demo program to toggle the LEDs at a different rate. Try out shifting patterns and other ways to manipulate the LED pattern via software.

3. Create functions to control LED’s

Every AXI IPCore you create should have a well defined programming interface. Following this practice makes for more readable and reusable code. At the bare minimum this should include preprocessor definitions for register addresses, but also should include functions relavant to operation of the IPCore.

An example read function is given below, it will return the state of the 4 LED’s as an integer.

//Preprocessor directives, (should go in header file)
#include "xil_types.h"
#define LED_CTL *((uint32_t *)0x4BB00000)
//end 'header' section	

uint32_t read_LED_val()
	uint32_t val=0;
	//read value from IPCore and
	//mask so we only get 4LSB
	val = LED_CTL & 0xF;
	return val;

Create a software driver for your LED controller. Include a header and source file. Please make sure you name the header and source file appropriatly. Put any register address definitions in your header file and write functions to control the state of the LEDs in the source file. Your functions at a minimum need to include the above read function as well as function(s) to write values to the registers in your custom IP Core. You can also write any additional software functionality you might need.

4. Add an enable LED Functionality to your myled IP

Modify your custom IP core again so that slv_reg0 enables/disables 4 non-RGB LED’s seperately. Also use slv_reg1's contents to turn the individual non-RGB LEDs on and off as long as their respspective enable is set in slv_reg0. After you update your IP, regenerate the bitstream, and export the Hardware Platform with the bitstream again. You might run into an issue when using Vivado 2017.2 that the bitstream file is not being updated. If you do, go to this answer record to fix this issue with a patch provided by Xilinx. Make sure you update your software to accommodate the change in hardware.

The memory map for your custom IP core should look as follows:

Address Name Bits used Function
0x4BB00000 LED_EN 3:0 Enable each LED Separately
0x4BB00004 LED_CTL 3:0 Turn ON/OFF each LED separately if the corrsponding LED has been enabled in the LED_EN register


1. Create an IP core for RGB LEDs with Enable LED functionality

Create an IP core to control the 2 RGB LEDs (LD10 and LD11). It must be able to individually enable each channel of the RGB LEDs seperately. For any LED in an RGB LED to be on, its bit in both the RGB_LED_EN register and its data register must be a ‘1’. Make sure you write software to control the RGB LEDs too.

The memory map for this IP should look as follows (Remember to set the base address in the Vivado Block Design):

Address Name Bits used Function
Base + 0x0 RGB_LED_EN 5:0 Enable each individual LED of each RGB LED separately
Base + 0x4 RGB_LED_0_DATA 2:0 Turn ON/OFF RGB LED 0 colors separately if the LEDs have been enabled in the RGB_LED_EN register
Base + 0x8 RGB_LED_1_DATA 2:0 Turn ON/OFF RGB LED 1 colors separately if the LEDs have been enabled in the RGB_LED_EN register

2. Control the 7-Segment Display through Software

Create an AXI IP Core to control the anodes and cathodes of the seven segment display on the Blackboard. Use one register to hold the values to drive the cathodes, and another to hold the values for the anodes. Using this IP, use software control to display differing values on the different display digits.

3. Control the 7-Segment Display through Hardware and Software

Create a seven-segment display controller that decodes input and multiplixes the data driving the cathodes and anodes using digital logic. Control the data displayed using AXI-connected registers; One register per digit. The value written to the bottom 4 bits of each register should be reflected on the 7-seg display.