Zedboard Audio Hardware design (1/2)
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.
For “Default Part”, click “boards” tab, and search “Zedboard”.
When you click “Finish”, it’ll create blank project.
Click “Create Block Design” on left pane (as indicated red square).
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.
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.
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.
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.
Instantiate “Processor System Reset” IP, and connect “Peripheral reset” and “reset” in clocking wizard.
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)
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
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.
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 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.
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”.
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.
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”.
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.
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;
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.
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.
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.
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.
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.