Post Go back to editing

IP core for AXI streaming with IIO support

I have a board with an ad7768 adc, and I'm using the ad7768evb demo project to access that devices via libiio.  This works well.

My fpga design also has a custom IP core that accepts an AXIS stream.  I'd also like to use libiio to send data to this device.

Is there an HDL core I could use that would do this, and has iio support in the kernel?

fixed title
[edited by: jay_col at 9:42 PM (GMT -4) on 28 Apr 2021]
  • How do you want data to flow in your system? Can you just put the core inline with the current DMA and interface core?


  • Sorry for the confusion.  This other AXIS device is completely independent from the ad7768.

    I want data to stream from linux/zynq via libiio to an AXIS device as though it were a DAC.  I was thinking there might be an existing core for some other DAC that I could repurpose for this.

  • I would look at the different reference designs with DAC parts:

    Then the different associated devices trees to show how the drivers are enumerated.


  • Thanks Travis.  It looks like the ad9739a might have the simplest setup and the shortest driver code.  And, like you mentioned before, I can just pick off the stream right after the DMA that goes along with the ad9739a.

    I imagine that a driver like ad9739a.c is going to fail to load unless its actually able to find the chip over spi, and find the ip core over axi-lite.

    My plan is likely going to be making a modified version of ad9739a.c to bypass those checks, but can you think of anything clever I could do in hardware to be able to leave the driver exactly as-is without actually having an ad9739a on the board?

  • I searched the reference designs for instances of "CONFIG.DMA_TYPE_DEST 1" which correspond to a DMA engine having an AXIS output.

    It seems there are a number of projects (i.e. fmccoms and dac_fmc_ezb) that use this setup.  And it appears that all of those are JESD based transmitters.  Modifying the block designs to get rid of the JESD components seems pretty easy.

    The dac_fmc_ezb project supports a number of JESD based eval boards, like the one for the ad9172.  The iio subsytem driver for these components (like iio/ad9172.c) are pretty big, seemingly because of jesd setup.  So those drivers might be less than ideal to modify because of their complexity, but it seems like its the easiest path overall.

    So I'm currently thinking of modifying:

    This hdl project:

    This dts:

    This linux driver:

    OR using teh ad9162 from the fmcomms11 project, which has a slightly easier driver:

    This hdl project:

    This dts file:

    This linux driver:

  • So I started making a modified version of the ad9144 (mixed a bit with the ad9122 and ad9361).  Primarily this means making a new c file for my driver (based on ad9122.c, ad9144.c etc.) and also modifying cf_axi_dds.c to have the apporpraite references to my new driver.

    Getting rid of jesd in the driver wasn't really an issue.  And even getting rid of spi doesnt seem to be a big deal as I can just pretend that my device has no spi requirements.  This seems to be made easier by setting my driver to 'standalone' mode inside cf_axi_dds.c.

    The bigger issue are the standard registers for the cf_axi_dds framework.  Like the ADI_DRP_LOCKED bit in the ADI_REG_DRP_STATUS register.  It seems that there are only a handful of registers that have to be read.

    I could make something from scratch to mimic these and give the driver what it wants, but do you know of some common code in the HDL repo that could make this easier? Or maybe some other solution?

    Right now I'm thinking of using a combination of up_axi.v (which converts AXI lite to uP) and up_dac_common.v (which contains the register map in uP format), and up_dac_channel.v.  All of which are mentioned in

  • Ok, I have this working.  Here is the basic procedure:

    1. Find an example project in hdl/projects that uses the ADI DMAC in memory mapped to streaming mode (DMA_TYPE_DEST 1).  I believe these are all going to be cores that use JESD.  An example is fmcomms11.  Use this project to get the correct setup for the DMA engine.
    2. Make an IP core compatible with the generic axi dac.  THe terminology is very confusing here.  The terms DAC and DDS will be used interchangeably.  But basically what you need to do is provide an AXI-lite interface on your device which has the appropriate registers that cf_axi_dds.c (in the kernel) will look for.  Rather than copy a DAC's hdl code, when you want to look at is the AXI-lite interface of this block: (specifically the regmap module)  You will see that it uses a few common verilog files, that you can use exactly as they are, to act as the axilite interface (off hand i think its up_axi.v, up_dac_common.v and up_dac_channel.v)
    3. In addition to adding those modules to you core you also need to pull in the XDC constraints files associated with those, as they have clock crossing domains in them, again look at the jesd core I mentioned.
    4. Copy one of the existing linux drivers from the drivers/iio/frequency folder.  Something like ad9122 is a good starting point.  I cant really walk you through this part.  I assume you dont have a spi device, in which case get rid of all references to spi, and change the module type from module_spi_init to a regular module_init.
    5. Edit cf_axi_dds.c to add the information for your new driver.  Assuming that you aren't using spi, then copy the existing axidds_core_info struct for a device that has .standalone = true as this will make cf_axi_dds not try and use spi.
    6. Update your dts file.  You don't really need much in there, just enough that the cf_axi_dds.c code can find the correct driver.

    A couple misc notes:

    • One of the confusing things for me was that I assumed that cf_axi_dds.c was just helper code for accessing the iio framework.  This isnt the case.  Its actually a driver itself, and it will look for your driver to load it.
    • In standalone mode it seems that your driver doesnt really need to do anything at all.  It appears that the 'conv' struct is never used, and none of your functions other than 'init' will ever be called.  I guess this means you need to do all your configuration during init, but I'm not really sure how you then provide the appropriate helper functions to do things like set sample rate.  I'm guessing that's maybe all handled through the common registers we added.
    • I wasn't able to get this to work as an out of tree module, in fact I wasnt able to get this to work as a module at all.  I had to enable ALL the analog devices drivers as part of the kernel.