Jun 9, 2015

Listening to sound on Zedboard

The Zedboard is already capable of outting HDMI SPDIF audio, through the ADV7511 chip.  I have not yet tried to push sound out through either HDMI or the ADAU1761 codec chip, and some day, I will examine how the sound output works.  But for now, I want to study the mic input.  Someone has already looked at the ADAU1761 audio codec's connection on the Zedboard, in the context of a Zynq FPGA based averaging filter project--one that does NOT involve any SW.  But any non-trivial use of the mic will require some SW control, whether through a Linux driver, or a custom FW.

WARNING: unfinished work

All my free time is spent trying to write wearable FW running on uClinux on ARM Cortex M7.  I am unlikely to come back to this within a couple of years, so I am putting this into a deep freeze for now.

ADAU1761 codec IC on Zedboard

This Analog Devices chip packs a lot of signal processing horsepower:
  • Supports both SPI and I2C control (selected by pulling nCLATCH down 3 times)
    • I2C slave address ADDR0/1 pin selectable
  • Custom audio processing possible on the DSP if program and data are downloaded into the chip using the SigmaStudio GUI from ADI.
    • The same GUI allows graphical design of signal processing path using a library of blocks.
  • Internal 1.5 V digital power generator
  • Built-in digital ADC/DAC oversampling: 64x or 128x (default)
  • Programmable sampling rate
  • Multiple inputs
    • Generates MICBIAS for Electret mics, bias and current (incased current reduces RMS input noise) programmable through I2C exposed register (R10).
    • Programmable gain amplifier
    • Automatic level control on the L/RIN
  • Multiple outputs: can drive headphones, earpiece speaker, capless headphone (must use mono-output as virtual ground connection)
  • I still don't know why they call it a codec chip, but according to the datasheet, it packs SigmaDSP which can be programmed from Sigma Studio--(a IDE-like windows GUI).
  • High pass filter to remove DC offset
The mic is NOT connected to the MICIN pin of ADAU1761 chip (which is grounded) but rather the L/RINN.  The L/R mic inputs are single ended, because the positive of the differential pair are grounded to the analog ground, as shown in the screenshot of the mic input into the ADAU1761 chip below:
Q: Why not ground the N, rather than the P, which would be the more intuitive way?
A: The other name for the LINN and RINN pins are single-ended input 1 and single ended input 3, so the confusion is purely derived from the failure of the schematics to show the clearer pins names (which Altium--used for the Zedboard board design--has the full capability to do).

These 2 inputs are only 2 of the 6 input channels into the ADAU1761 IC, shown on the left side of the functional block diagram below:

Electric mic support on Zedboard

On the Zedboard, JP1 shorts the nominally R channel of the stereo mic to the MICBIAS pin of the ADAU1761, for Electret mics, which are the most commonly used mics today (because it is cheap; every cellphone and laptop used to have one, although now MEMS mics are replacing them) possessing wide BW (10 Hz ~ 30 KHz).  As shown in the electric mic circuit in the above link. the electret mics need a supply (bias) voltage for the transistor inside them.  On the Zedboard, ADAU1761 pin 5 (MICBIAS shown above) provides this bias voltage, across the 2K resistor shown below:
Since the bias voltage is also grounded across a 100 nF cap, the supply voltage experiences a low-pass filter time constant of 2E3 * 100E-9 = 200 us, or 5 kHz.  Note the RC circuit in the bottom is a parallel RC circuit, and therefore does NOT filter electret mic, which are voltage outputs; since the resistor in series is 0 Ohm, there is no low pass filter.  Above schematic shows that the Zedboard is ready to "listen to" an eletret mic on ONE of the MIC in.  Unlike the poor explanation in the Zedboard HW guide, JP1 does not enable bias for electret mic; it enables the bias voltage for the inner channel (the metal that is exposed in the middle of a stereo jack; not sure whether this is the left or the right) of a stereo mic input, but the outer channel is ALREADY electric mic enabled (ADAU1761 can supply the bias voltage).

Also according to the above link, Electret mic has a (relatively) high noise floor, which can potentially be mitigated to some extent by a mechanical design (e.g. directional mic has a pickup hole in the bottom).  The lowest frequency that can be picked up by an Electret mics are determined by the mechanical/material property of the mic design, but also the amplifier's input impedance--which is usually extremely high.  According to the link above,  this also keeps the amplifier noise high, to -120 ~ -110 dB, making it unsuitable to pick up very faint sound (<< -40 dB).  Since the ADAU1761 datasheet sites the total harmonic distortion + noise at ~-88 dB for single ended input and -70 dB for differential (Q: why is differential input noise higher for differential than single ended?), it would appear that the ADAU1761 is the dominant noise source in the system (yielding only -40 dB - -88 dB = 48 dB of SNR even for a relatively strong sound input).

Active (self-powered) mic support on Zedboard

The Zedboard also has a stereo "line-in" jack, connected to the L/R AUX inputs of the ADAU1761.  
Unlike the mic jack, the line-in has a low pass filter (RC = 1K * 100 nF = 100 us, or 10 kHz), which is too low to capture an entire audible range, but plenty high for the human vocal range (telephony break frequency is < 4 kHz).

Zedboard audio Vivado design

The "Ubuntu on Zedboard" reference HW design uses a clocking wizard to generate 12.288 MHz (which is within the acceptable frequency range: 8~27 MHz, and requires the integer PLL mode) MCLK (AC-MCLK on the Zedboard schematic) from the FCLK_CLK1 (running at 200 MHz) through the Zynq pin AB2.  But oddly, the clock is constrained to 20 MHz rather than 12.288 MHz:

create_clock -name spdif_clk    -period 50.00 [get_pins i_system_wrapper/system_i/sys_audio_clkgen/clk_out1]

I thought the choice of the 12.288 MHz MCLK rate strange until I read the PLL section of the ADAU1761 datasheet:  12.288 MHz times 4 yields 49.152 MHz, which is 1024 times 48 kHz sampling rate.  Since the PLL generated clock is an integer multiple of the MCLK, this PLL mode is called the integer mode.

In the schematic, Zynq pins constrained for ADAU1761 are:

set_property  -dict {PACKAGE_PIN  AB2   IOSTANDARD LVCMOS33} [get_ports i2s_mclk]
set_property  -dict {PACKAGE_PIN  AA6   IOSTANDARD LVCMOS33} [get_ports i2s_bclk] # ADAU1761 port bit clk
set_property  -dict {PACKAGE_PIN  Y6    IOSTANDARD LVCMOS33} [get_ports i2s_lrclk] # ADAU1761 LRCLK/GPIO3: port frame clk
set_property  -dict {PACKAGE_PIN  Y8    IOSTANDARD LVCMOS33} [get_ports i2s_sdata_out] # ADAU1761 DAC_SDATA/GPIO0
set_property  -dict {PACKAGE_PIN  AA7   IOSTANDARD LVCMOS33} [get_ports i2s_sdata_in]

So it appears that the primary interface to this chip is through the I2C interface.  But the Zedboard schematic also shows traces to the I2C control interface of the chip, constrained on the following pins:

set_property  -dict {PACKAGE_PIN  AB4   IOSTANDARD LVCMOS33 PULLTYPE PULLUP} [get_ports iic_mux_scl[0]]
set_property  -dict {PACKAGE_PIN  AB5   IOSTANDARD LVCMOS33 PULLTYPE PULLUP} [get_ports iic_mux_sda[0]]

These 2 pins are externally pulled up to 3.3 V bus with 2 KOhm resistor shown on the Zedboard schematic.  This I2C is controlled by the AXI IIC IP in the Zynq system design.  Because it can only drive 1 I2C at a time, the I2C bus is shared between the ADV7511 control and the ADAU1761 control, through the I2C mixer logic (which is not much of a mux--it just copies the signal to 2 wires):
I don't know yet how the Linux manages mutual exclusion of 2 different HW, but at least the I2C bus address is different (for ADAU1761: 'b0111011, or 0x3B, since the 2 configurable LSB are both pulled up on the Zedboard vs. for ADV7511 'b0111001, or 0x39); that is why the mixer above does not have to be a mux.  Since my HW is driving the ADV7511 chip just fine (I see something!), I know that the AXI I2C module control from Linux is working fine.  The time sharing of the I2C bus is not as bad as it sounds, because the data is on the I2S bus.  To handle the I2S data, this Zynq design uses an IP from ADI, shown below:
The schematic shows that the same 12.288 MHz clock discussed above also drives the I2S data processing logic (the DATA_CLK_I pin).  DMA_REQ (peripheral request interface) interface connects DMAC (DMA controller) to DMA capable IP, and the PS7 has up to 4 DMA_REQ interface ports that can be connected to FPGA IP (Zynq TRM section 9.2.6 and on).  There is a lot of information in the TRM, but for now I just write down the mapping of the DMAC IRQ#: 0~3 maps to PS IRQ 46~49, and 4~7 maps to 72~75.

In the current design, PS7's DMA0_REQ is used for the SPDIF transmitter IP, and DMA1_REQ and DMA2_REQ are used for the I2S's TX and RX ports.  When I expanded the axi_i2s_adi IP above, the source code for the PL330 TX and RX mediator IPs are visible:
This hierarchical view shows that the axi_i2s_adi IP consists of 3 major interfaces:
  1. AXI Lite, for SW control from PS7 (base address 0x77600000)
  2. I2S to ADAU1761, for audio data in/out
  3. DMA REQ to the PS7's dAM_REQ 1 and 2 interface ports
Q: why not use the DMA IPs that Xilinx offers for free (I recently used VDMA as explained in this blog entry, but there are other VDMA IPs), rather than writing new FPGA code to control the ARM PL330 code?

Since I studied the AXI Lite interface in a previous blog entry, and the PL330 interface is rather daunting for now, I start off examining the I2S interface to the ADAU1761.

Serial audio data interface

As shown in the ADAU1761 block diagram above, the data interface consists of only ADC out, DAC in, and 2 clocks.  Since the ADAU1761's LRCLK pin has a 47 pF cap, I know LRCLK (left/right clock) is in a pulse mode.  This capacitor is necessary to align the LRCLK to the data stream.  Initially, I thought that the chip only supports I2S, but is quite flexible, supporting the following data formats:
  • I2S: 16/18/20/24 bits per channel
  • Left justified or right justified mode
  • TDM (time division multiplexing), where the left/right concept is replaced by the first/second groups
    • TDM mode has a variant where the LRCLK is a short pulse, indicating the beginning of a new sample group.
In all cases, the BCLK (bit clock) moves much faster than the LRCLK.

ADAU1761 Linux device driver

ADI supports ADAU1761 on Linux with the ADAU1361 driver.  The Zedboard DTS file (arch/arm/boot/dts/zynq-zed-adv7511.dtsi) already has the declarations for the 2 ADI chips listening on the AXI I2C bus:

                i2c@41600000 {
                        compatible = "xlnx,axi-iic-1.01.b", "xlnx,xps-iic-2.00.a";
                        interrupt-parent = <&gic>;
                        interrupts = <0 58 0x4>;
                        reg = <0x41600000 0x10000>;

                        adv7511: adv7511@39 {
                                compatible = "adi,adv7511";
                                reg = <0x39>;
...                        };

                        adau1761: adau1761@3b {
                                compatible = "adi,adau1761";
                                reg = <0x3b>;
                        };
                };

Note the I2C address matches the what I found in the Zedboard schematic.  To pull the Linux driver in, several kernel configs are required:
  • CONFIG_I2C=y
  • CONFIG_SND_SOC=y
  • CONFIG_SND_SOC_ADI=y
  • CONFIG_SND_SOC_ZED_ADAU1761=y
The last config pulls in additional required configs, as you can see in the last lines of <kernel>/sound/soc/adi/Kconfig:

config SND_SOC_ZED_ADAU1761
tristate "ZED board sound support"
depends on SND_SOC_ADI
depends on I2C
select SND_SOC_ADI_AXI_I2S
select SND_SOC_ADAU1761_I2C

Driver probes

The driver is compiled into the kernel ("=y" above), so the driver probing happens during system init, after the DTB (device tree blob) is parsed.  In fact, I have the stack trace from when I was studying how Linux drivers load firmware during probing.  BUT Lars seems to have deprecated that driver in favor of a mainlined driver adau1761_i2c, which uses probe() function common to both I2C and SPI: adau1761_probe().  The FW name, required by ADAU1761 is hard-coded to "adau1761.bin":

struct snd_soc_dai_driver *dai_drv;
...
dai_drv = &adau1761_dai_driver;
firmware_name = ADAU1761_FIRMWARE;

ret = adau17x1_probe(dev, regmap, type, switch_mode, firmware_name);
...
return snd_soc_register_codec(dev, &adau1761_codec_driver, dai_drv, 1);

adau17x1_probe() mainly loads the FW, through sigmadsp_firmware_load().  The code reveals some structure of the FW.  The binary starts with a magic string in the header: "ADISIGM"

struct sigma_firmware_header {
unsigned char magic[7];
u8 version;
__le32 crc;
} __packed;

The 32-bit CRC is just for the FW data itself: fw->size - sizeof(*ssfw_head).

There is apparently only 1 FW versions:

switch (ssfw_head->version) {
case 1:
ret = sigmadsp_fw_load_v1(sigmadsp, fw);
break;
case 2:
ret = sigmadsp_fw_load_v2(sigmadsp, fw);
break;

And the adau1761.bin I have shows that the FW version is 1, and the CRC is 0.

henry@W540:~/work/zynq/buildroot/output/build/linux-xcomm_zynq/firmware$ hexdump adau1761.bin
0000000 4441 5349 4749 014d 0000 0000 
000000c

Note that there is NO data in this FW (and that's OK).

Testing the driver

modprobe snd-soc-adau1761

DSP

The DSP program restarts at the beginning for EVERY audio frame.  The program and data must be written to the DSP RAM every time the DSP is (re)started.  The DSP uses 28 bit integer, or 5.23 fixed point numbers, with the MSB being the sign bit; therefore, the range is +/- 16.0.   When outputting a number to the serial port (which supports only up to 24 bit), the top 4 most significant bits (NOT including the sign bit) are chopped off, producing a range of +/ - 1.0.

2 comments:

  1. One thing about low pass filter in line-in - as it is stated on the schema, capacitors are not loaded by default, so there is no low pass filter there, like near microphone input. I just checked this on my Zedboard - there are no caps there.

    ReplyDelete
    Replies
    1. Oh, thank you for pointing that out! I missed it because it did not say do not stuff, or do not populate.

      Delete