Recapping on the Zynq PS/PL interface; to date we have examined the interfaces between the PS (processor system) and the PL (programmable logic) side of the Zynq All Programmable SoC. We’ve created a simple peripheral using the Vivado Design Suite, used SDK to communicate with the new peripheral, and we ran a self test on the peripheral. However, the peripheral that we created contained no functionality beyond four registers, which we could read from and write to. In the real world, we’ll want the peripheral to do something useful.
We will be again using Vivado to add real-world functionality to this peripheral.
The first thing to do is open the Vivado project and the block diagram that contains the peripheral we created. Right click on the peripheral and select the “Edit in IP Packager option.” This will open the IP Packager view in a new window which enables you to edit and update the peripheral.
This view looks resembles the standard project flow with the exception of the Package IP peripheral window. Beneath the design sources window, you will find two files that were created during the peripheral creation process.
These files are named:
Adams_Peripheral_v1_0.vhd – Top Level architectural files where you would define user I/O that leaves the module.
Adams_Peripheral_V1_0_S00_AXI.vhd – RTL file, which contains the functional AXI interface including the four registers initially created.
Both of these files include comments as to where the user code is to be inserted:
For this example, I am going to introduce use the first register as a control register. Specific bits in this register define whether the contents of registers 2 and 3 are to be added, subtracted, or multiplied together. The result of the operation will be stored in the fourth register. We make the fourth register read-only with respect to the microprocessor to ensure that the microprocessor cannot corrupt the results. In addition, the peripheral will be able to generate an interrupt if enabled by the control register.
The first step in the definition is to declare the four registers, the first three as output registers and the final one as an input register. (If we wanted to we could implement this function here however I am doing it at the top level to demonstrate what will be required on more complex functions).
I also edited this file to prevent the processor from writing to the fourth register by making it read-only.
Within the top level file I created an interrupt output and added the simple functional code within the architecture to perform the operations we desire.
Having added in all the necessary user VHDL, I synthesised the project to ensure I had not made any errors before I packaged the IP and returned to my project within Vivado. However, before I packaged the IP on the packager page, I incremented the version number to reflect the code changes. Clicking on re-package will run the packager and close the project, returning you to the original Vivado project.
Once back within the project that uses the peripheral, we can run the IP status report (under tools -> Reports -> Report IP status) to show the updated version being used within the design.
Now, the project needs then to be rebuilt prior to exporting to SDK. Within the SDK, we can use the same functions that we used before to write to and read from this peripheral. However, this time the self-test should fail as the final register cannot be written during the test.
The first test is to add together the two contents stored within register 2 and 3 using command 1 in register 0:
In the second test, we multiply the contents of register 2 and 3 together using command 2 in register 0:
In the final test, we subtract register 3 from register 2 using command 3 in register 0:
All of these tests use a polled approach. That’s OK because the simple add, subtract, and multiply functions complete within one clock cycle. However, more complicated multi-cycle functions require the use of an interrupt, which we will look at next time.
Please see the previous entries in this MicroZed series by Adam Taylor: