Introduction: Digital Filters on Zybo Board

The Digilent Zybo board is built around Xilinx's Zynq SoC (System on Chip) part. This IC has a dual Arm-A9 cores that perform like any other microcontroller. What makes it special is that it also has FPGA hardware on the same IC as the processor.

This tutorial was written using our experience from the course EE 439 taken at California Polytechnic State University, San Luis Obispo in Spring 2015. A good application for this board is designing and developing real-time digital filters for Bass Guitar . The basic idea for this project is to take in audio data from the line input , convert it to digital data, process it, convert it back to analog signal and output it via the headphones out.The Zybo board contains a SSM 2603 Low Power Audio Codec onboard that is used for receiving and transmitting audio. This real-time filtering system was designed to give users the following specifications: Provide user with two filters: Distortion Clean Provide users the option to control the clipping effect. Provide users to shut-off the audio completely. Necessary components for this project are: Zybo Zynq-7000 Development Board AUX cable Headphones or Speakers Potentiometers This tutorial also assumes that you have the Vivado IDE and the Zybo drivers already installed. There's plenty of good information out there on how to do this if you don't!

Kindly use the attached zip file to extract all necessary files used throughout the instructable.

Step 1: Basic Setup and Adding the Zynq IP Block

To start off open up Vivado and create a new project. Once the project window opens up hit "Next>" and type a new project name and ensure project location is in an accessible location. Choose the RTL project option and then a new window should open up.In the following window search for xc7z010clg400-1 and ensure parts is selected. Vivado should open a new project window. In the right hand column called "Flow navigator" click on "Create Block Design" found under the IP integrator subsection. This opens up a block design window. Right click in this white space and click on "Add IP". In the search box type in "Zynq" to find the “ZYNQ7 Processing System”. This should place a ZYNQ7 IP block in your block window. You have now successfully setup your first IP block.

Step 2: Configuring the Zynq Block

Click on the import XPS setting and import the “Zybo_zyqn_def.xml” file that is also provided below.

Next click on MIO configuration seen in the right column in the figure above. Under I/O Peripherals open GPIO and select GPIO MIO. Under Application Proccesor Unit select select Timer 0 and Watchdog. Next click on Clock configuration seen in the right column in the figure above. Under PL Fabric Clocks select FCLK_CLK1 and FCLK_CLK2. Change the FLCK_CLK1 requested frequency to 50.

Step 3: Adding Custom Verilog Modules

We will be adding one custom VHDL modules. The module to be added will be the I2S controller which has been generated using the axi_i2s_adi_v1_0 pre production module. This module contains the files i2s controller that was used to generate bclk and lrclk. The files for this IP are provided in the main folder. The I2S communication protocol requires a bclk to be slower than mclk(master clk). The base clock ideally should be mclk/4 and the mclk should be 12.288MHz. We want to sample our audio at 48 MHz so these settings are taken off the audio codec's specification sheet. The lrclk signal needs to be at bclk/64 because for the left channel we will sample 32 bits and then for the right channel we will sample 32 bits. Basically as soon as data is detected the clocks are generated. As we are sampling all the time the clock will continuously be generated. All codec information can be accessed at .

The setup for this I2S controller file will be provided in the later steps.

Step 4: Creating the Custom VHDL Modules

Now you need to create the VHDL code to build a 64 bit audio sample with the incoming serial data from the codec. the 64 bits account for the 32 bits from the left channel and 32 bits from the right channel. This audio sample then gets divided into its left and right components and filtered before returning back to its full 64 bit sample length. In our implementation our actual audio is only 12 bits long. This requires adding zeros to the LSBs of the right and left audio samples separately after filtering and before the data is re-serialized.

The module created takes in serial data, the I2S LRCLK, the I2S BCLK, a 50MHz clock (CLK), a reset and a control pin to change the effects. The block diagram below details the internal functionality of the SerialEffects Block.

The functionality of each block as well as their inputs and outputs are detailed in the figures above.

LRCLK: Used by Personal RX and personal TX to distinguish between left and right channels.
BCLK: Used by personal RX and personal TX as the bit clock to read or write serial data CLK: 50MHz used by the Distortion filter to process the data.

The code for these modules is given below. Use the attached serialIP zip file over the one provided in the original folder for the serialIP IP block.

Step 5: Create and Package IP Block

To create an IP block simply create a new project in Vivado and click on “Add Sources” seen in the right column under project manager. Select “Add or create design sources” and add all the VHDL/Verilog Files associated with the IP block. Vivado should automatically create the hierachy associated with the VHDL/Verilog files. Upon adding the all files click on the “Tools” tab found at the top of the Vivado window. Under tools click on “Create and Package IP”. A new window should open. Click “Next >” to proceed. In the subsequent window, three options are provided a) Package your current project b) Package a specified director c) Create a new AXI4 peripheral. Select option a) Package your current project and click on “Next >”. In the following window be sure to select “Include .xci files” and note the IP location. You have now successfully created an IP Block that is named after the file that is highest in the heirachy. For this project two IP blocks are already created and provided. The IP blocks are called i2s_controller and serialIP.

Step 6: Adding IP Block to Repository

In your main project window click on the “Tools” tab found at the top of the window. Under tools click on “Project Settings” which opens up the project settings window. In the right column click on IP creating a window that looking like the image above. In this window click on “Add Repository” and select the location where the IP block was saved from the last step. After selecting the folder location of the IP block, Vivado should automatically add the IP and under the “IP in Selected Repository” the name of the IP block should be displayed. If there are no IP blocks in that sub-window the folder selected doesn’t contain any IP blocks. Once all IP blocks are added to the project, in this scenario add IP Blocks i2s_controller_v1_0 and SerialEffects_v1_0.

Step 7: Run Connection Automation and Connect IP Blocks

Right click the Block Design Window and select ADD IP option. Add the custom IP’s i2s_controller_v1_0 and SerialEffects_v1_0. Then run the Run Block Automation tool that should be in green label over the Block Design Window. Now connect FLCK_CLK1 to clk input in IP block i2s_controller_v1_0 and CLK input in IP block SerialEffects_v1_0. Connect FLCK_CLK2 to data_clk input in IP block i2s_controller_v1_0. Add the IP block called “const” to the block design. Connect the output of the IP block const to the inputs tx_enable, tx_stb and tx_enable in the IP block i2s_controller_v1_0. Add another const IP block, change the constant value to 0 of the IP block by double clicking on the IP block and then attach the output of the IP block to the reset pin found in the SerialEffects_v1_0 IP block. Now right click on all other pins in the SerialEffects_v1_0 IP block and select Make External. This creates external ports CTRL, onoff, SDATA_O and SDATA_I. Attach a wire from SDATA_I in i2s_controller_v1_0 to the SDATA_I external pin. For the i2s_controller_v1_0 create externals for BLCK_O, LRCLK_O and MUTEN_O pins. Now for the ZYNQ7 processing system right click on the output pin IIC_0 and make an external. This should create an IIC_0 output. Also right click on a blank space in the block design window, select create a port and create an output pin named ac_mclk. Create another output port and name it RECLRC. Attach ac_mclk output pin to FCLK_CLK2 and RECLRC output pin to LRCLK. Upon completing all the steps above the block design should look very similar to the figure above. Kindly ignore IP blocks axi_i2s_adi_v1_0 and output pins RXData1,RXDATA2, FDATA1, FDATA2,outdata.

Step 8: Generate the HDL Wrapper

Click the Sources tab in the Sources pane right next to the Flow Navigator. Right click on the “” file under “Design Sources” and select “Create HDL Wrapper”. Select "copy generated wrapper to allow user edits". Ensure that all external input and output ports generated on the block design are mapped in the wrapper. For example in the figure above CTRL, SDATA_O, SDATA_I are all external inputs and outputs in the block design that are also part of the wrapper.

Step 9: Adding and Editing the Constraints File

Now we need to add a constraints file for the board to map the verilog and vhdl inputs and outputs to the actual pins on the board. The constraints file has been provided in the main folder. It is called "Zybo_Master.xdc". For your convenience it has been updated with all the appropriate pins. To add a constraints file click on "Add Sources" in the "Flow Navigator" and select "Add constraints file". Browse to the constraints file and add it. You should now be able to find it under constraints folder in the sources pane.

Double click on “ZYBO_Master.xdc”. Find the section that starts with “switches” and uncomment the first four lines that start with “set_property” in this section. Change the names of the text followed by get_ports such as “sw” to “CTRL” and “onoff” respectively. Next, find the section that is called “##I2S Audio Codec”. Uncomment all of the set property lines in this section. Use the figure above to setup all the signal port names. All port names must match the names set in the wrapper and the block design. For example if you name your ac_mclk as just mclk then in the constraints file ensure that ac_mclk is replaced with mclk. It is also import to add two extra lines in the constraints file set_property PULLUP true [get_ports iic_0_scl_io] and set_property PULLUP true [get_ports iic_0_sda_io]. Once again replace the names of the pins past the get_ports with the names that were respectively used in the block design and generated in the wrapper. Without the PULLUP statements the IIC won’t be pulled up in its OFF state and won’t generate an IIC signal required to control the audio block.

Step 10: Generating the Bitstream and Exporting the Hardware

A bitstream now needs to be generated for the FPGA to be programmed. In the Flow Navigator select “Generate Bitstream”. Kindly wait as Bitstream generation can be a prolonged process.

After that is complete and a confirmation is available called “write_bitstream Complete” at the top right of the screen. Next go to file, export and click on export hardware. A window should pop up. Check the “Include Bitstream” box and hit ok. Then go to file tab and select “Launch SDK”. You can also confirm your implementation by selecting on Implementation Design in the Flow Navigator. This should then refresh the Vivado window very similar to the figure posted above. Select the I/O ports at the bottom of the window and ensure that each external input and output port is mapped to the right pin. Each pin representation can be found in the Zybo Reference Manual ( ). If every pin is mapped properly you’re done with the Vivado portion of the project!

Step 11: Setting Up the SDK and Generating Board Support Package (BSP):

The SDK project should contain design_1_wrapper_hw_platform_0 in the project explorer that is located on the left side of the Xilinx SDK window. A new Board Support Package needs to be generated. To do so click on File and then Next followed by Board Support Package. Name the board support package “RTOSDemo_bsp”. Click Finish. Another window should come up but just hit OK. Next go to File and select "Import". Select existing projects under general. Navigate to the folder provided and locate FreeRTOS->Demo->CORTEX_A9_Zynq_ZC702. Click FINISH and the main code should import. Upon pressing “ctrl-s” to save the SDK should run some background tasks and compile the code. If no compilation errors you are ready for next step. If you are having compilation errors then right click on the RTOSDemo folder and select “Change Referenced BSP” and reselect “RTOSDemo_bsp”.

Step 12: Setting Up the Code

All the code and the files in the RTOSDemo folder are already edited to function audio processing. The primary method used in the code is shown in figure 1. The second figure describes the registers used to control the Audio Codec SSM 2603. These registers can be referenced via IIC. For further information about each register the reference manual can be accessed at .

Step 13: Setting Up the Debugger

To run the code and program the FPGA a new debug configuration needs to be set up. Right click on the RTOSDemo folder, and under "Debug As" select "Debug Configurations".

Select the “Xilinx C/C++ application (System Debugger)” tab on the left side of the screen. In debug type select "Standalone Application Debug". Make sure to select Reset Entire System and Program FGPA. Also select Run ps7_init and Run Ps7_post_config. Now select the "Application" tab click download application. Ensure that project name is RTOSDemo and Debug/RTOSDemo.elf. Now you can click on Apply and then click on Debug. You are now ready to go!

Step 14: Run Debugger

Upon running the debugger kindly use the step function and step to till the line while(1){} at the end of the main function. The debugger window is shown above and the step method is found in the second row right underneath "Run" in the first row. At this point the audio should be processing and based on the settings should be distorted or clean.