The Eclypse Z7 is a Zynq-7000 FPGA development board from Digilent equipped with two SYZYGY interface referred to as Zmod ports. Zmods are Digilent's high-speed solution relative to their original Pmod interface that is more cost effective than some other high-speed interface connectors such as FMC.
Since the Zmod standard is intended for higher speed applications as compared to their Pmod counter parts, Digilent current offers a selection of ADC and DAC breakout Zmod carrier boards.
Given their perfect utility to act as test equipment when paired with a board like the Eclypse Z7 that has multiple host PC interfaces (UART, Ethernet, etc.), the ADC breakout Zmod carrier boards are labeled as Scope/Digitizer Zmods while the DAC breakout Zmod carrier boards are labeled as AWG Zmods.
The Digitizer Zmod is the newest to Digilent's Zmod carrier board lineup. Based on the Analog Devices AD9648 14-bit 125MS/s 1.8V dual channel analog to digital converter, the the Digitizer Zmod also adds an analog front end in the form of a high impedance buffer and anti-alias filter.
This sets the Digitizer Zmod apart from the Scope Zmods, making it ideal for use in applications such as SDR development and testing/verification. The Analog Devices ADC chips are a personal favorite of mine as they come with their configuration registers pre-loaded so they can fire right up and start pumping out samples. Digilent has stream-lined the process further by releasing IP that handles the SPI configuration interface and calibration process for you. It also handles the control of the clock domain of the incoming clock from the ADC chip used to sample its incoming data to the FPGA.
I'm using Vivado 2022.1 in this project, but the Digilent IP is currently only released for Vivado versions 2021.1 and earlier. I went ahead and used the current master branch of the Vivado-library repository from Digilent that is version 2021.1. Aside from one critical warning about a timing constraint in the Zmod Digitizer Controller IP that turned out to have no impact on the final design, I found there were no issues using the IPs in Vivado 2022.1.
Design OverviewThis project serves as a reference of how to insert the Zmod Digitizer into a custom design for the Eclypse Z7. Digilent does provide pre-built designs to utilize the Eclypse with Zmods as test equipment with WaveForms, which I'll cover in upcoming projects, however a plug-and-play ADC board such as the Digitizer Zmod is so handy I felt it was worthwhile to see how to integrate it into designs.
As I mentioned, the Zmod Digitizer controller IP from Digilent handles the SPI configuration interface and calibration process for you, as well as controlling the clock domain of the incoming clock from the ADC chip used to sample its incoming data to the FPGA. Ultimately, it outputs a stream of 4-byte wide AXI Stream data (32 bits) of the ADC's output data samples with tvalid and tready control signals.
This means once the Zmod Digitizer Controller IP is dropped into the design, an AXI Stream of ADC samples is available for putting through any desired signal processing (ie - FIR filter), then written to DDR memory so the application running on the ARM processor of the Zynq to access for further processing.
Since I just got my hands on the Digitizer Zmod, I thought I'd start by proving in the pipeline of data from the ADC, to DDR memory via DMA (direct memory access) and then access those stored samples from a bare-metal C application.
Create Vivado ProjectLaunch Vivado and create a new Vivado project targeting the Eclypse Z7.
I don't plan to do any hardware acceleration with this design so I did not check the option to make the project an extensible Vitis platform. However, you can go back an change this in the settings of the Vivado project at any point in time later.
Download IP from DigilentAs I mentioned, I used what was the master branch at the time of writing this (I also attached zip file below in case this post is being read at a later date and the current master branch is incompatible with the following design being detailed in this project):
~$ cd ./eclypseZ7_prj/
~/eclypseZ7_prj$ git clone https://github.com/Digilent/vivado-library.git
~/eclypseZ7_prj$ cd vivado-library/
~/eclypseZ7_prj/vivado-library$ git checkout master
Once downloaded, add the extracted IP folder as an IP repository in the Vivado project settings.
Open Settings from the Flow Navigator window, navigate to IP > Repository and click the + button. Point to the location of the extracted IP folder and click OK. Vivdao will acknowledge all of the IPs it finds in the specified directory which should be 65 IPs and 5 interfaces.
Click Apply then OK to close the Settings window.
Create Block DesignAs per usual with Zynq-based Vivado projects, the first thing to do is create a block design to instantiate the Zynq processing system in the project. Select Create Block Design from the Flow Navigator window. Once the new block design populates, add the Zynq PS IP and run block automation to apply board presets (the option to run block automation will appear automatically a few moments after adding the Zynq PS to the block design).
Next, add an AXI Direct Memory Access IP to the block design. Double-click on the DMA IP to open its configuration window.
Disable the scatter-gather engine, disable the read channel (we're only writing the ADC values to memory from the programmable logic), and enable the option to allow unaligned transfers on the write channel.
Run the connection automation that has appeared at this point.
Once completed, double-click on the Zynq PS IP to open its configuration window. Enable one of the Zynq high performance slave AXI interfaces (HP0): under PS-PL Configuration > HP Slave AXI Interface enable S AXI HP0 interface.
Wait a moment, and run connection automation again when it appears.
Once complete, the S_AXI_LITE
and M_AXI_S2MM
ports will all be connected. This leaves the S_AXIS_S2MM
port to be manually connected based on the desired data flow in the design.
At a high level, this hardware design is reading in the data from the ADC of the Digitizer Zmod and streaming it into DDR via DMA (direct memory access) for C code running on the ARM-core of the Zynq FPGA to be able to access it for processing.
Add the Zmod Digitizer Controller IP to the block design and make the diZmodADC_Data[13:0]
port and DcoClkIn
port external. Ignore the option to run connection automation that appears after adding the Zmod Digitizer Controller IP to the block design.
Connect the SysClk100
port and port to FCLK_CLK0
from the Zynq PS IP (Note: this clock must be 100MHz per the internal configuration of the Zmod Digitizer Controller IP). Then connect the DcoClkIn port to an external port (right-click on the port and select the menu option to Make External). The option to run connection automation will disappear that this point.
Also connect the following signals to external ports on the Zmod Digitizer Controller IP:
diZmodADC_Data
aZmodSync
sZmodADC_SDIO
sZmodADC_CS
sZmodADC_Sclk
CG_InputClk_p
CG_InputClk_n
aREFSEL
aHW_SW_CTRL
sPDNout_n
Add two constant IPs to the block design. Name one Vcc
and set its constant value to 1, name the other Gnd
and set its constant value to 0.
Connect the Vcc
constant to sEnableAcquisition
. Connect the Gnd
constant to ClockGenPriRefClk
and sTestMode
.
Connect aRst_n
to peripheral_aresetn
of the Processor System Reset IP.
Add a Utility Vector Logic IP to the block design and configure it as an AND
gate with a C_SIZE
of 1.
Connect its output to aCG_PLL_Lock
and its two inputs to external ports named GPIO1
and GPIO4
.
Data comes into the block design from the Digitizer's AD9648 ADC as an input to the Zmod Digitizer Controller IP via the diZmodADC_Data[13:0]
port. The AD9648 also provides a data clock out (DCO) signal for a host to use to clock in the data (the host being the Eclypse's FPGA in this case), which is the DcoClkIn
port of the Zmod Digitizer Controller IP. The Zmod Digitizer Controller IP then passes on the DCO clock for downstream logic via its ZmodDcoClkOut
port.
It is common for ADC chips like the AD9648 to provide the clock used to capture its output data into an external register. It leaves less room for timing and skew errors (and makes the logic on the FPGA side simpler) for the ADC chip to provide the clock versus the FPGA providing it. Note: this is not to be confused with the clock the FPGA is providing to the ADC as its master clock (SysClk100), which the ADC uses to generate its DCO clock.
The subtlety here is that this means the data going in and coming out of the Zmod Digitizer Controller IP is in a different clock domain than the AXI DMA IP and DDR by default. The data going into the the Zmod Digitizer Controller IP (diZmodADC_Data[13:0]
) and coming out of it (DataStream
) is in the ADC's DCO clock domain, while the AXI DMA IP and DDR are in the Zynq PS's FCLK_CLK0
domain.
One's first instinct might be to use the ADC's DCO clock for the AXI DMA and DDR as well, but this would not be wise as it would mean the DDR would lose its clock anytime the ADC was not actively outputting data.
The simplest solution for handling a clock domain crossing such as this is to implement a FIFO, which is what I did in this case. Add an AXI Stream Data FIFO to the block design and enable the independent clocks option, which allows for the master and slave AXIS ports of the FIFO to be connected to different clocks. I also enabled packet mode to control the transfer packet sizes since there is no tlast signal coming from the Zmod Controller IP. This means the FIFO will automatically start transferring samples out into DMA when the FIFO's almost full condition is met.
Keep this in mind when selecting the FIFO's depth. Since the output from the Zmod Digitizer is 32 bits, I set the FIFO's depth to 64 so only two samples would accumulate in it before it started outputting them to the DMA.
Connect the master AXIS port of the FIFO to the slave stream to memory map (S2MM) port of the AXI DMA, and connect the slave AXIS port of the FIFO to the DataStream port of the Zmod Digitizer Controller IP. Then connect the FIFO's m_axis_aclk
port to the Zynq PS's FCLK_CLK0
and s_axis_aclk
to ZmodDcoClkOut
on the Zmod Digitizer Controller IP.
Connect sZmodDcoPLL_Lock
from the Zmod Digitizer Controller to the reset of the AXIS data FIFO (s_axis_aresetn
). This signal indicates whether or not the the PLL for the ADC's DCO is in a locked state and therefore stable. It is zero when the PLL is out of lock and one when locked. Since the reset for the AXIS data FIFO is active low, I decided to use sZmodDcoPLL_Lock
as an asynchronous reset for the FIFO since I can't assume the validity of the data output from the ADC if its DCO is not in a locked state.
Connect the CDCE_IIC
interface to an external port (this is the interface the Zmod Digitizer Controller IP uses to configure the oscillator control chip on the Digitizer board).
Finally, disable the external calibration signals (there are default calibration coefficients in the ADC's configuration that will work unless you want to run calibration on the ADC again).
The final block design should look similar to the following:
Validate the block design and save it.
After the block design has been saved, right click on the block design file in the Sources window and select the option to create an HDL wrapper.
This will instantiate the block design in the overall project and provide the hooks for the external signals to be connected to specific FPGA package pins via the constraints file.
Select the option to allow Vivado to auto-manage the file.
As mentioned, the external signals created in the block design are connected to specific FPGA package pins via the constraints file.
Select the option to Add Sources from the Flow Navigator window. Choose to Add or create constraints in the pop-up window.
I've attached my constraints file below for the Digitizer Zmod to be connected to the Zmod A port of the Eclypse Z7. You can either import that file or create a new one and copy+paste my constraints into it.
With the design complete, it needs to be synthesized, place & route (implementation) run, and a bitstream needs to be generated. A neat trick about Vivado, is that it will automatically run synthesis and implementation as needed. So personally, I always just immediately select Generate Bitstream from the Flow Navigator window and let it run everything automatically (it's a built in coffee break!).
Once a bitstream has been successfully generated, the hardware needs to be exported in Vivado's XSA format in order to pull it into Vitis and/or PetaLinux to develop software with.
Select File > Export > Export Hardware. Walk through the export wizard in the window that pops up. Be sure to select the option to include the bitstream in the exported hardware platform. Select the desired export location for the XSA file, I personally like to export it to the top level directory of the corresponding Vivado project to keep everything organized.
At this point with the hardware design complete and exported, the next step is to create the bare-metal C application in Vitis. Since this post has gotten long enough, I'll post the Vitis project and testing of the design in my next project post!
Overall, I'm definitely a fan of the Eclypse+Zmod system. It's a quick and easy way to get an ADC/DAC application up an running. So I will definitely be using them more in the future!
Comments