Zedboard Audio Hardware design (1/2)

Yuhei Horibe
9 min readMay 8, 2020

--

Zedboard Audio System Block Design

Summary

This is Hardware Designing part (1/2) of Zedboard audio system designing.

Preparation

To design the hardware for Zynq PL (Programmable Logic), Xilinx Vivado has to be installed. The latest version now is 2019.2. Go to Xilinx website, then register user, and download the software. It works on multiple platforms. I’m using Vivado on Ubuntu.

Block design

First, open Vivado, and select “Create Project”, then create the blank project.

Create Project

For “Default Part”, click “boards” tab, and search “Zedboard”.

Default Part

When you click “Finish”, it’ll create blank project.

Create Block Design

Click “Create Block Design” on left pane (as indicated red square).

Zynq processor IP

Right click anywhere in “Diagram”, and then, select “Add IP”, then search “zynq”. You will see ZYNQ7 Processor System. Click it and instantiate that block in Block Diagram.

Block automation for Zynq processor

On top of the “Diagram” pane, green bar showed up saying “Run Block Automation”. Click this, and output terminal “DDR” and “FIXED_IO” will show up.

Double click ZYNQ block.

Zynq Processor IP customization

Select “PS-PL Configuration” tab, then select “HP Slave AXI interface”. Check “S AXI HP0 Interface” to enable it. This will create AMBA High Performance bus for DMA (Direct Memory Access). This is necessary for audio data transfer.

Clocking

This is important part, because later on, this hardware will be used via Linux OS. The device driver for this device has to be modified and compiled into the kernel. To create device driver, the clock configuration has to be done properly.

The design will be like below;

  • AXI Lite4 bus, and AXI HP bus will be driven by 48 MHz clock
  • Audio master clock “aud_mclk” will use 24 MHz, and this will be supplied to Audio CODEC (ADAU1761)
  • Those are “Fixed frequency” and never be configured dynamically

3rd one is important since the default Xilinx PL sound card driver assumes that, this clock is configurable at run time. But due to accuracy of the clock frequency, and the complexity of the design, fixed frequency clock would be preferred.

To generate fixed clock sources, instantiate “Clocking Wizard” IP. Right click anywhere in “Diagram”, select “Add IP”, then search “clocking wizard”. When the IP is instantiated, double click and open “Re-customize IP” dialog.

Clocking wizard customization

Select “Output Clocks” tab, and enable clk_out2. Then, set the frequency for both “clk_out1” and “clk_out2”. “clk_out1” will be 48MHz, and “clk_out2” will be 24MHz.

Next, connect “FCLK_CLK0” in Zynq processor block, and “clk_in1” in clocking wizard.

Clock source connection

Instantiate “Processor System Reset” IP, and connect “Peripheral reset” and “reset” in clocking wizard.

Processor System reset

Then click “Run Connection Automation” on top.

Next, To use this hardware from Linux, 3 different IPs will be needed. First thing is, “Audio Formatter”. Right click diagram, select “Add IP”, and instantiate “Audio Formatter”. Before running “Connection Automation”, connect clock sources to appropriate places. This is very important.

“clk_out1” (48MHz) from clocking wizard should be connected to;

  • s_axi_lite_aclk … AXI Lite4 bus clock
  • s_axis_mm2s_aclk … AXI Stream bus (memory mapped to stream) clock
  • s_axis_s2mm_aclk … AXI Stream bus (stream to memory mapped) clock

“clk_out2” (24MHz) from clocking wizard should be connected to;

  • aud_mclk (Audio Master clock)
Clock routing

Then, click “Connection Automation”. This action will instantiate several blocks.

  • Processor System Reset for 48MHz clock
  • AXI interconnect (For High Performance bus)
  • AXI interconnect (For AXI Lite bus connection)

Then “Run Connection Automation” twice. It’ll connect signals listed below.

  • Reset signals
  • Clock signals
  • AXI Lite and AXI Stream buses
Connection Automation

Instantiate one more “Processor System Reset” for 24MHz clock. Then connect “peripheral_reset” signal to “aud_mreset” in Audio Formatter. Run connection automation.

I2S IPs

These will be the main components of this system. Instantiate “I2S transmitter”, and “I2S receiver”. BEFORE running “Run Connection Automation”, connect “aud_mclk” to “clk_out2” from clocking wizard (24MHz), and “aud_mreset” to Reset System for 24MHz clock. Then run connection automation. Also, this is important. Manually connect “s_axis_aud” in I2S transmitter, and “m_axis_mm2s” in Audio Formatter.

NOTE: Signal naming

  • Prefix “m_” means “master”, “s_” means “slave”.
  • “axi” in the middle means, AXI Lite connection, “axis” means AXI Stream connection (High Performance bus).
  • “mm2s” … Memory Mapped to Stream, “s2mm” … Stream to Memory Mapped.
I2S components

Instantiate “I2S receiver for recording. Same as I2S transmitter, but;

  • Connect “aud_mclk” to “clk_out2” (24MHz) from clocking wizard
  • Connect “aud_mreset” to “preripheral_reset” signal from Reset System for 24MHz
  • Connect “m_axis_aud” in I2S receiver to “s_axis_s2mm” in Audio Formatter
  • Run connection automation
I2S receiver

I2S clocking

I tried to supply clock from I2S transmitter, but it didn’t work well. So I would strongly recommend using Audio CODEC’s clock as master. To do so, both I2S transmitter and I2S receiver have to be configured as “slave”. Click I2S transmitter, and look at “Block Properties” pane indicated below. Click “Properties” tab in that pane.

Block Properties

In “CONFIG” tab, find “C_IS_MASTER”, and set it to “0” to configure it as slave. Do it for both “I2S transmitter” and “I2S receiver”. What’s changed is, “lrclk_out” and “sclk_out” in both transmitter and receiver disappeared, instead, “lrclk_in” and “sclk_in” appeared in both transmitter and receiver. Right click on “lrclk_in” pin, and select “Make External” in dropdown. Do the same for “sclk_in”. Connect “lrclk_in” in another IP to the same pin. Do the same for “sclk_in”.

External pins

Rename those input terminals to “adau_lrclk” and “adau_bclk”.

Interrupt signal connection

This step is crucial. Both I2S transmitter and I2S receiver will use DMA (Direct Memory Access), and transfer large block of data between processor, and these IPs. When the block transfer is done, this block will generate interrupt. This must be handled properly.

First, Zynq processor has to be configured to make interrupt input signal. Double click Zynq processor IP.

Zynq Processor Configuration

Go to “Interrupts” tab, and check “Fabric Interrupts”. Then open “PL-PS Interrupt Ports”, and check “IRQ_F2P”. This will create input for Zynq processor. But, this is not ready to connect interrupt pins. Instantiate “Concat” IP, and connect “dout” to “IRQ_F2P”.

Interrupt signal input for Zynq processor

Right now, there are 4 interrupts to handle;

  • “s2mm” interrupt from Audio Formatter
  • “mm2s” interrupt from Audio Formatter
  • “irq” from I2S transmitter
  • “irq” from I2S receiver

Double click “Concat” IP, and increase the “number of ports” property to “5” (one more IP will be added, and that’ll generate interrupt as well). Then connect all those input signals to input in Concat IP.

80% completed block diagram

Interface between PL and Audio CODEC (ADAU1761)

All the above components will drive Audio CODEC outside of the chip. To make connection between them, check what signals to connect. Download Zedboard Hardware User Guide from Digilent website, and look at “Audio CODEC” section. There’s a table like below;

Zynq PL — Audio CODEC (ADAU1761) connection

There are 2 important parts;

  • I2S audio interface connection
  • I2C control interface connection

Those 2 have similar name and very confusing, but “I2S” is audio interface, and “I2C” is inter chip connection interface. Basically, “I2S” will carry audio signals, and “I2C” will transfer control signals like, “audio volume”, “mode”, “clock configuration”, “enable” and so on.

Firstly, we’ve already done everything for “I2S”. So let’s finish I2S part first. I2S interface consists of those parts below;

  • LR clock … “lrclk_in” in both I2S transmitter and receiver
  • Bit clock … “sclk_in” in both I2S transmitter and receiver
  • Audio master clock … “clk_out2” from clocking wizard (24MHz)
  • Serial data IN … “sdata_0_out” in I2S transmitter
  • Serial data OUT … “sdata_0_in” in I2S receiver

Audio master clock will be used as “System Clock” of Audio CODEC LSI. “clk_out2” (24MHz) from clocking wizard should be connected here. If Audio CODEC will be configured as “master” device, this clock will be used to generate “LR clock” and “Bit clock” above.

LR clock and Bit clock will be used to transfer audio data. Both “Serial Data IN” and “Serial Data OUT” signal will be synchronized with these clocks. LR clocks sends “Left” or “Right” signal. If this signal is “0”, this indicates “Left” channel signal is on the serial bus. If this is “1”, “Right” channel signal is on the bus.

NOTE: “IN” and “OUT” are described from “CODEC’s perspective”. So “Input” to CODEC is “output” from PL, “Output” from “CODEC” will be “input” to PL.

Right click all the above pins, and select “Make External”. Then rename all those.

Connecting PL and CODEC

I renamed signals like below;

  • Serial Data IN … adau_dac_in
  • Serial Data OUT … adau_adc_out
  • LR clock … adau_lrclk
  • Bit clock … adau_bclk
  • Audio Master Clock … adau_aud_mclk

I2C control interface

To make control interface, instantiate I2C modules. It’s confusing, but I2C module is named as “AXI IIC”. Search “iic” and instantiate IP. Connect “s_axi_aclk” manually, then run connection automation.

AXI IIC (Inter IC) IP

Let’s get back to connection between Audio CODEC and Zynq PL. For I2C, we need;

  • SCK … Serial Clock
  • SDA … Serial Data
  • I2C address … I2C address (2bits)

For now, “gpio” signal (it’ll be used as “address” bits) is just one bit. Customize it to generate 2 bits address.

AXI IIC customization

Change “General Purpose Output width” to 2.

Then, “Make External” for “IIC” terminal, and “gpio” signal. Lastly, connect interrupt signal from AXI IIC IP to Zynq processor (Concat).

Create HDL wrapper

This block design does not define actual interface between PL and on board components. For example, I2C “SCK” and “SDA” are bi-directional signal, but if you breakdown “IIC” output signal, both “SCK” and “SDA” are broken down into “*_t”, “*_i”, and “*_o” signals. These are 3-state control signal, but we just want output from 3-state buffer. To do this, we need “HDL wrapper”.

Go to left top pane, and open “Sources” tab, then right click Block Design (something .bd is that), and select “Create HDL wrapper”. This will create actual interface definition file (verilog source file) in the project.

Create HDL wrapper

Then you will see top module in Design Source pane.

We need to connect those interface pins to on board components, but this is already way too long. It will be next topic.

--

--