In this lab, you will create a digital audio system. You will stream raw PCM data from ROM to a PWM system. Later you will connect your PWM system to an audio buffer(implemented with a FIFO) which will allow you to load audio data through software.
Refer to the links for background information to complete this project.
1. Stream PCM data from a preinitialized ROM
Design a system that interfaces an 8-bit PWM block with a ROM containing 8-bit PCM samples. Your system will need to calculate addresses and feed the PWM system with samples from the rom at the desired sample rate(44.1KHz). Send the output of the modulator to the Headphone Jack.
You can initialize the block ram with the following file: unsigned 8-bit sound data.Refer to background information how to set up ROM. The data has the first sample at address 0, second at address 1 and so forth.
2. Connect your PCM system to the Zynq PS via the Xilinx FIFO generator IP.
Digital systems often use buffers for playing audio. Since the data required to store audio takes up a modest amount of storage (44.1KB per second for 8-bit PCM at 44.1KHz), it makes sense to store it in large, cheap memories, or physical formats like hard drives or optical discs, as opposed to the limited storage space provided by ROM (or block ram, in the case of FPGA). Audio data can be transferred from it’s storage by a CPU (or other device) to a buffer, which can output the data to audio output circuit at the desired rate.
The Blackboard’s CPU has access to 512MB of DDR meaning it can hold approximately 193 minutes of 8-bit PCM data at 44.1KHz. Thus, with an audio buffer you can play much longer audio clips than with with the, relatively tiny, block ram. Of course, connecting the buffer to the Zynq processing system also allows you to feed whatever audio data into the buffer. For the assignment you will feed raw PCM data from memory into the buffer, but you can provide audio samples from other sources, and even generate and/or modify samples programatically.
In this requirement we will use a FIFO (first-in, first-out) as an audio buffer between the Zynq’s ARM CPU and your PWM audio system. A FIFO is structured memory that allows data to be loaded sequentially and read in same order it was put in. This type of data structure can also allow reads to occur at a independant and even simultanously as writes.
- Setup a FIFO using xilinx’s FIFO Generator IP.
- Interface the fifo with the AXI bus.
- Create a controller that reads data from the FIFO.
- Write functions to control your FIFO from C.
- Make a test program for your system.
- Create a program to play long audio clips.
Setup a FIFO using xilinx’s FIFO Generator IP
Follow the instructions on FIFO to set up a FIFO in your block design.
Connect the FIFO to the AXI bus
Connect the write interface of the FIFO to a custom AXI IP block. You’ll need to figure out how to translate a write on the AXI bus to a write to the FIFO; Review what signals are involved in an axi write transaction and look at the generated code in the IP packager for ideas. Make sure the status of the queue is readable through software. Create an interface that allows control over the FIFO’s reset and enables reads from (and possibly writes to) the FIFO.
Create a controller that reads data from the FIFO
Design a read interface to your audio buffer that reads samples from the fifo at your audio sample rate. You should ensure data is available before performing a read. Keep in mind there is a 1 clock delay between asserting the read enable on the FIFO and the data appearing at the output. The FIFO will output one word of data for each cycle the read enable line is asserted; Make sure you only read one sample per cycle of the sample clock. You may want to implement this using a state machine to ensure proper timing. Add configuration bits to your AXI interface to control the read interface.
Write functions to control your FIFO from C
Write code to write samples to the FIFO and to check the status flags.
Make a test program for your system
In c generate a simple square wave and output it through your audio system.
Create a program to play a long audio clip.
Computer audio data is typically stored on disk, meaning a hard drive or flash memory. It can also be accesed through optical drives or streamed over network connections. The easiest way to load and access PCM data would be to link it to your program. This attaches additional data to your program’s elf, and can be accessed at runtime by the program. To do this in C, we can simply declare an array with initialized data. The compiler and linker will handle getting this data into memory, and we can access samples by treating it like an array.
Below are a few audio clips you can play on your digital audio system. These are C source files containing an array of 8-bit integers. they can be accessed as any other array. To make the data accessible to other soruce files, you will need to make a header file declaring the array as external. A code example is given below.
Making Arrays Accessible in other source files.
Simply declare a global array in the code
//source.c //define array in source file int array;
Then in a header, declare the same array as external
//header.h //make "int array Accessible by using extern extern int array ;
Now any source file that includes the header can access (read and write) the array.
3.Load audio data using Interrupts through the Zynq’s Programmable Logic.
Configure interrupts for programmable logic in your block design. Follow guide below
Configuring Programmable Logic interrupts
Hook up the empty output of the generated FIFO to the IRQ Pin. Write a C program that configures the Zynq’s GIC and enters an empty endless loop.
Use the guide below for help with configuring the GIC:
Write an interrupt handler that loads samples to the buffer, it should run whenever the fifo goes empty. The ARM will be able to load samples before the last sample finishes.
Make AXI IP to configure interrupts.
The FIFO Generator IP has a setting to allow a programmable threshold. Configure this and make AXI ip to program the threshold. Modify your system so interrupts can be triggered by the empty signal, or the threshold signal. Make this configurable so you can enable and disable both interrupts.
Write your own FIFO module using distributed ram Design and replace Xilinx’s IP FIFO with your own, refer to the background info on the structure of FIFO’s for assitance
Make Your Sample rate variable and configurable through memory mapped registers. Use a set of common sample rates or make it programmable.
Implement volume control for the audio output. Use the XADC value for the Potentiometer to change the volume of played sounds. How would you change the volume of PCM data? Would this affect the quality of the sound?
Use your knowledge of the Blackboard, Zynq, and embedded system concepts to take on these extra tasks
a. send audio data through a pc to be played on the blackboard:
Use the uart(or other communication port) to send audio files to the zynq system, this will involve writing a program on your computer that uses the serial port as well
b. use sd card for audio:
Put audio data onto an SD card and play it through your system. it’s your choice how to format the data on the SD card. Implement multiple tracks, and a way to queue tracks into a playlist.
c. Make an aribtrary waveform generator.
Programatically generate samples for your audio system; Generate square, triangle, sawtooth, and sine waves (others?) . Configure your system so you can choose waveform type and period (up to about 22khz). You can go even further and add amplitude control and dc bias (make sure you implement a clipping system). Design a hardware interface using blackboard peripherals so your system can function as a standalone unit.