Zynq-7000 - Tutorial for PS and PL interconnect in ZedBoard

The Zynq-7000 SoC integrates the software programmability of an ARM-based processor with the hardware programmablility of an FPGA. The ZedBoard from Digilent is part of the Zynq-7000 family which contains a Dual-Core Cortex-A9 processor and 7-series programmable logic. These features make the ZedBoard a powerful tool in creating hybrid software and hardware programmable designs.

This tutorial will demonstrate the interconnect of ARM processing system (PS) of the ZedBoard with the FPGA, also known as the programmable logic (PL). To do this, we will model a NAND gate, where the AND logic of the gate is programmed into the PL and the NOT logic is programmed into the PS. The inputs will be switches and output will be an LED.


Figure 1: NAND gate schematic. Blue circle will be programmed into the PL and yellow circle will be programmed into the PS.

Materials: ZedBoard kit, ZedBoard constraint file, Vivado Design Suite
- The constraint file for the ZedBoard can be found here

Figure 2: The ZedBoard constraint file needed from Avnet's website.

In your Xilinx folder, create a folder named "Projects" and save the constraint file there. 

Step 1: Create a new project!

Open Vivado and click "Create Project". Title the project NAND_gate and make sure it is saved to the "Projects" folder as seen below in Figure 3.

Figure 3: Creating the NAND gate project. Be sure the project is saved inside the hierarchy of the Xilinx folder. This will be crucial later.

Click Next and make sure RTL project is selected as seen below.


Click Next again. On the "Add Sources" screen, click create file. This will be the verilog file that contains that AND gate logic of our NAND gate. Label it "AND_gate" as seen below and click "Ok" to add it too the project.


Click Next again. On the "Add Constraints" page, click add files. Add the ZedBoard constraint file that was downloaded earlier as seen below. The constraint file holds the current assigments for all the pins on the ZedBoard. This allows us to utilize the I/O devices on the ZedBoard.


Click Next once again and you will be on the add parts page. Click the boards tab that is highlighted below.


The boards tab will show all boards one can use for a project. Search ZedBoard and select the one in blue below. If you cannot find the ZedBoard, click "Update Board Repositories" and Vivado will download the latest boards. ZedBoard should be included in those.


Click Next one more time and Vivado should display the screen shown below.


Click "Finish" and Vivado will create the project for us. When it initially opens, there will be a few pop-up windows regarding the .v file we created. Just click 'OK' until you are through to the project.

Step 2: Writing the Verilog file

Implement a simple AND gate in the AND_gate.v file we created earlier.


Step 3: Creating the block design

On the Project Manager, click 'Create Block Design', and name it 'NAND_design' as seen below.


Click OK to generate the block design, which should be empty. Click the plus sign and add ZYNQ7's IP block to the design. Double click the IP block to add.


Then, click back to the sources tab, right click on AND_gate.v and select 'Add module to block design'


NAND_design should now look like this:


Now we must instantiate the switches and led. To connect them to the processing system, we must first instantiate AXI-GPIO blocks. Add two AXI-GPIO IP blocks to the diagram.


The block diagram should now look like this:


Next, double click on axi_gpio_0, then click on the IP configuration tab. We will configure this block to be our inputs via switches. Check the all input box, then change the GPIO width to be 1, as our inputs will only be one bit.


Click OK. Next, we will instantiate our switches as inputs. Right click the inputs 'a' and 'b' for our AND_gate module and select make external for both. (Ctrl T)


This allows us to make our own inputs for the module. In the constraint file, the pins for the switches are labeled SW0:SW7 as seen below.


Click on the external port and in the external port properties window, change the names a_0 and b_0 to SW0 and SW1. This connects the external ports to the switches on the board.


Now, click on the plus sign next to GPIO on the axi_gpio_0 block and gpio_io_i[0:0] will drop down. Connect our AND gate output y to gpio_io_i[0:0] by clicking the port and dragging to the other.


Now, double click on axi_gpio_1 then move to the IP configuration tab. This block will serve as an output from the PS to an LED. Check 'All Outputs' and change GPIO width to 1.


Click OK. Then, click the plus next to GPIO on the axi_gpio_1 block. Right click gpio_io_i[0:0] and select make external. (Ctrl T) This will be our LED. Since LEDs are labeled LD0:LD7 in the constraints file,  we will label this external port LD0 to connect it to the first LED on the board. Do this in the external port properties


Now, click Run Connection Automation above the block diagram. Select 'All Automation' and hit okay.


After, click Run Block Automation, then click the clockwise arrow icon that is highlighted below to regenerate the layout. The block design should now look like:


The axi_peripheral block is added to connect the axi_gpio's to the processing system. Also a system reset block is added during connection automation.

4. Create an HDL wrapper

That is all the hardware design needed. Now, go back to the sources tab, right click on NAND_design and then click 'Create HDL Wrapper'. Choose 'Let vivado manage wrapper'.


The HDL wrapper wraps all IP blocks up into one module, which can then be synthesized, implemented, and written into a bitstream file. On the bottom left of the flow navigator, click 'Generate Bitstream'. Once completed, click on file, export, then export hardware. Be sure include bitstream is selected.



Once exported, click on file again, then Launch SDK. Both options should be local to project.


5. Creating a .c file

Don't select anything at first and the hardware wrapper should open in SDK.


Next, click file - new - application project


Name the project "NOT_gate" then click next> and make sure the source file 'helloworld.c' is selected. This will be the template for our .c file.

   
This will generate a NOT_gate folder. Now, open the NOT_gate folder on the left, then open the src folder. Then, double click "helloworld.c" to open the .c file.


Now we can code up our .c file. First, we initialize XGpio variables named input and output. We then connect these variables to our AXI_GPIO blocks in the block design. We do this by using the XGpio_Initialize function. In our diagram, we designed the AND gate output to be input through AXI_GPI0_0. So, we initialize the input variable with the GPIO_0 device ID. We also initialize our output to GPIO_1 device ID as that block corresponds to our output LED.

After that, we set the data direction of the two variables accordingly. XGpio_SetDataDirection(&var, on/off(1/0), input/output(1/0)). Then, we are able to read data from our inputs using the discrete read function. Once we read in data from the AND gate, we set it into an integer a. Next, we invert the data, creating a NOT gate. We are then able to write the data back to the PL using the discrete write function.

Full .c code is given below:

/*
2-bit NOT gate to test PL and PS interconnect
 */

#include <stdio.h>
#include "platform.h"
#include "xgpio.h"
#include "xparameters.h"


int main()
{
    init_platform();

    XGpio input, output;
    int a; // input of NOT gate (output of AND gate)
    int y; // output of NOT gate (output of NAND gate)
 
    XGpio_Initialize(&input, XPAR_GPIO_0_DEVICE_ID);
    XGpio_Initialize(&output, XPAR_GPIO_1_DEVICE_ID);

    XGpio_SetDataDirection(&input, 1, 1);
    XGpio_SetDataDirection(&output, 1, 0);

    while(1)
    {
        a = XGpio_DiscreteRead(&input, 1);

        if(a==1)
        {
            y = 0;
        }
        else // a=0
        {
            y = 1;
        }

        XGpio_DiscreteWrite(&output, 1, y);
    }


    cleanup_platform();
    return 0;
}

6. Interconnecting the PL and PS

Now save the file and we are now ready to program the ZedBoard. Make sure the ZedBoard is plugged in and turned on, then connect it to your computer through the JTAG port via micro-USB. Next, click program FPGA. Don't change any settings, click program again to program the PL.


Then, right click on the NOT_gate folder, click Run As, then Launch on Hardware (System Debugger) to program the .c file to the PS.


Congratulations, you have now connected the programmable logic of the ZedBoard to the ARM processor. We can verify this using the switches. The truth table for a 2-bit NAND is as follows:


So, the LED should only be turned off when both switches our turned on. Outputs should look identical to the photos below:









These outputs demonstrate a successful interconnect between the programmable logic and the processing system!