Post Go back to editing

Functionality of AXI_ADCFIFO and AXI_DACFIFO with MIG Controller

Category: Hardware
Product Number: ADRV9008-1W + ZCU102
Software Version: 2021_r1

Hello   and Team ,

I am using ADI product ADRV9008-1W with ZCU102 and 2021_r1 HDL design. According to our need the design is modified in such a way that AXI_ADCFIFO and MIG controller is added so that DMA can have continous RF data from the RF frontend. Now, as the design advances we need to extract the saved data in a repetitive manner from DDR4 through MIG controller and direct to one of our IP core. The custom IP core will control when the frame ends and after that should start again from starting base address.For this purpose modified AXI_DACFIFIO is desgined, where only *_rd.v is used. Smartconnect from xilinx is used to which connects AXI_ADCFIFO and modified AXI_DACFIFO to the MIG Controller. Now the data can be successfully retrieved from AXI_ADCFIFO and sent to axi_dmac. From there the MATLAB can verify the correctness of data. The problem arises where the modified AXI_DACFIFO(AXI_DATAEXTACTOR in fig.) tries to read the data. The IQ samples at the end of the data (seen in ILA) does not verify with MATLAB. MATLAB and ILA shows the same RF data at the time. For this particular problem I want to ask


1. In AXI_ADCFIFO IP core this formula is used AXI_AWINCR = AXI_LENGTH * AXI_BYTE_WIDTH whereas in AXI_DACFIFO this formula is used AXI_AWINCR = (AXI_LENGTH + 1) * AXI_BYTE_WIDTH. I concur that ADCFIFO IP utilizes AXI3 protocol and DACFIFIO AXI4. Does this difference could arise the wrong reading of data from modified AXI_DACFIFIO IP core?

2. AXI_SIZE is the size of data to be transferred. DDR4 in ZCU102 has 128 bits. This translates to 2^n where n in this case is 4. In Axi_ADCFIFO AXI_LENGTH is configured as 4. So that means that in a burst it writes 4*(2^4) bytes in the memory(DDR4). Should modified AXI_DACFIO also have the same axi_length config. or it can also be configured for more , for e.g 32 or 64?
How AXI_Length impacts the buffer memory composed of BRAM in DACFIFO?
How one can calculate ideal AXI_LENGTH?

3. Here is the modified code

`timescale 1ns/100ps

module axi_dataextractor #(

  parameter   DAC_DATA_WIDTH = 64,
  parameter   AXI_DATA_WIDTH = 128,
  parameter   AXI_SIZE = 4,
  parameter   AXI_LENGTH = 4,
  parameter   AXI_ADDRESS = 32'h80000000,
  parameter   AXI_ADDRESS_LIMIT = 32'h9fffffff,
  parameter   AXI_LAST_ADDR = 32'h80012C50 
) (

// dac interface

  input                   dac_clk,
  input                   dac_rst,
  input                   dac_valid,
  input                   axi_xfer_req_s,
  output  reg [(DAC_DATA_WIDTH-1):0]  dac_data,
  output  reg             dac_dunf,
  output  reg             dac_xfer_out,

  // axi interface

  input                   axi_clk,
  input                   axi_resetn,
  output                  axi_awvalid,
  output      [ 3:0]      axi_awid,
  output      [ 1:0]      axi_awburst,
  output                  axi_awlock,
  output      [ 3:0]      axi_awcache,
  output      [ 2:0]      axi_awprot,
  output      [ 3:0]      axi_awqos,
  output      [ 7:0]      axi_awlen,
  output      [ 2:0]      axi_awsize,
  output      [ 31:0]     axi_awaddr,
  input                   axi_awready,
  output                  axi_wvalid,
  output      [(AXI_DATA_WIDTH-1):0]  axi_wdata,
  output      [(AXI_DATA_WIDTH/8-1):0]  axi_wstrb,
  output                  axi_wlast,
  input                   axi_wready,
  input                   axi_bvalid,
  input       [ 3:0]      axi_bid,
  input       [ 1:0]      axi_bresp,
  output                  axi_bready,
  output                  axi_arvalid,
  output      [ 3:0]      axi_arid,
  output      [ 1:0]      axi_arburst,
  output                  axi_arlock,
  output      [ 3:0]      axi_arcache,
  output      [ 2:0]      axi_arprot,
  output      [ 3:0]      axi_arqos,
  output      [ 7:0]      axi_arlen,
  output      [ 2:0]      axi_arsize,
  output      [ 31:0]     axi_araddr,
  input                   axi_arready,
  input                   axi_rvalid,
  input       [ 3:0]      axi_rid,
  input       [ 1:0]      axi_rresp,
  input                   axi_rlast,
  input       [(AXI_DATA_WIDTH-1):0]  axi_rdata,
  output                  axi_rready,
  output                  axi_rerror,
                
  // Debug output
  output      [4:0]       fsm_dacfifio_deb,  
  output      [31:0]      last_raddr_deb,
  output      [31:0]      axi_araddr_deb,
  output      [8:0]       axi_arincr_deb,
  output                  axi_rready_deb,
  output                  axi_rrvalid_deb,
  output                  axi_rrlast_deb,
  output reg              axi_xfer_req_deb,
  output                  axi_fifo_rst_deb                           
);

  reg                                axi_xfer_req_m_s1;
  reg                                axi_xfer_req_m;
  (* dont_touch = "true" *) wire    [31:0]                      axi_last_addr_s ;
  (* dont_touch = "true" *) wire    [ 7:0]                      axi_last_beats_s;
  reg    [ 3:0]                       dma_last_beats_s  = 4'b0000;
  wire    [(DAC_DATA_WIDTH-1):0]      dac_data_fifo_s;
  wire    [(DAC_DATA_WIDTH-1):0]      dac_data_bypass_s;
  wire                                dac_xfer_fifo_out_s;
  wire                                dac_dunf_fifo_s;
  wire                                dac_dunf_bypass_s;

  localparam  AXI_BYTE_WIDTH = AXI_DATA_WIDTH/8;
  localparam  AXI_ARINCR = (AXI_LENGTH + 1) * AXI_BYTE_WIDTH;
  
  assign axi_last_addr_s = AXI_LAST_ADDR & (~AXI_ARINCR + 1);
  assign axi_last_beats_s = AXI_LENGTH;
  
  // setting deafult values for Write channel. In our case,
  // the write channel should be disabled.
  
  assign axi_awvalid = 1'b0;
  assign axi_awid    = 4'b0000;
  assign axi_awburst = 2'b01;             // INCR (Incrementing address burst)
  assign axi_awlock  = 1'b0;              // Normal access
  assign axi_awcache = 4'b0010;           // Cacheable, but not allocate
  assign axi_awprot  = 3'b000;            // Normal, secure, data access
  assign axi_awqos   = 4'b0000;           // Not used
  assign axi_awlen   = AXI_LENGTH;
  assign axi_awsize  = AXI_SIZE;
  assign axi_awaddr  = AXI_ADDRESS;
  assign axi_wvalid  = 1'b0;
  assign axi_wdata   = {128{1'b0}};
  assign axi_wstrb   = {AXI_BYTE_WIDTH{1'b1}};
  assign axi_wlast   = 1'b0;
  assign axi_bready  = 1'b0;

  axi_dataextractor_rd #(
    .AXI_DATA_WIDTH (AXI_DATA_WIDTH),
    .AXI_SIZE (AXI_SIZE),
    .AXI_LENGTH (AXI_LENGTH),
    .AXI_ADDRESS (AXI_ADDRESS),
    .DAC_DATA_WIDTH (DAC_DATA_WIDTH),
    .DAC_MEM_ADDRESS_WIDTH (12)
  ) i_rd (
    .axi_xfer_req (axi_xfer_req_m_s1),
    .axi_last_raddr (axi_last_addr_s),
    .axi_last_beats (axi_last_beats_s),
    .axi_clk (axi_clk),
    .axi_resetn (axi_resetn),
    .axi_arvalid (axi_arvalid),
    .axi_arid (axi_arid),
    .axi_arburst (axi_arburst),
    .axi_arlock (axi_arlock),
    .axi_arcache (axi_arcache),
    .axi_arprot (axi_arprot),
    .axi_arqos (axi_arqos),
    .axi_arlen (axi_arlen),
    .axi_arsize (axi_arsize),
    .axi_araddr (axi_araddr),
    .axi_arready (axi_arready),
    .axi_rvalid (axi_rvalid),
    .axi_rid (axi_rid),
    .axi_rresp (axi_rresp),
    .axi_rlast (axi_rlast),
    .axi_rdata (axi_rdata),
    .axi_rready (axi_rready),
    .axi_rerror (axi_rerror),
    .dma_last_beats (dma_last_beats_s),
    .dac_clk (dac_clk),
    .dac_rst (dac_rst),
    .dac_valid (dac_valid),
    .dac_data (dac_data_fifo_s),
    .dac_xfer_out (dac_xfer_fifo_out_s),
    .dac_dunf (dac_dunf_fifo_s),
    .fsm_dacfifio_deb(fsm_dacfifio_deb),
    .last_raddr_deb(last_raddr_deb),
    .axi_araddr_deb(axi_araddr_deb),
    .axi_arincr_deb(axi_arincr_deb),
    .axi_rready_deb(axi_rready_deb),
    .axi_rrvalid_deb(axi_rrvalid_deb),
    .axi_rrlast_deb(axi_rrlast_deb),
    .axi_fifo_rst_deb(axi_fifo_rst_deb)   
    );
   /* if (~FIFO_BYPASS) */
    
    always @(posedge dac_clk) begin
      
      if (dac_valid) begin
        
        dac_data <= dac_data_fifo_s;
      
      end
      
      dac_xfer_out <= dac_xfer_fifo_out_s;
      dac_dunf     <= dac_dunf_fifo_s;
    
    end
    
    always @(posedge axi_clk) begin
    
        axi_xfer_req_m    <= axi_xfer_req_s;
        axi_xfer_req_m_s1 <= axi_xfer_req_m;
        axi_xfer_req_deb  <= axi_xfer_req_m_s1;
    
    end

endmodule


In this I have removed DMA and dacfifo_wr.v logic as I am only interested in reading from MM part and then sending the readed Data through a different lower clock. The axi_xfer_req_s signal comes from my custom IP core @122.88 MHz and as a result CDC so that it can register to AXI clock which is connected to MIG UI clk port. Other than that dacfifo_rd.v is untouched and no modification is done there. I am having trouble with three signals namely : axi_last_addr_s, axi_last_beats_s and dma_last_beats_s. With which deafult value should I initialize them? In my custom IP core I control axi_xfer_req_s in such a way that when counter reaches a demanded IQ samples value then the signal is put to LOW for a single clock and then again to HIGH so that I can again read the same value from starting address of the RAM.

4. If AXI_ADCFIFO only operates on AXI3 then how AXI_DMAC gets the right IQ samples and MATLAB decodes it correct whereas modified AXI_DACFIFO is not able to?

Thank you in advance.

With regards,



Added former thread members
[edited by: FPGA@noob at 8:52 AM (GMT -4) on 24 Mar 2023]
  • Hello,  

    I'm still investigating your issue, I will try to give you an answer as soon as possible!

    Best regards!

    Filip.

  • Hello,  

    1. Both AXI_ADCFIFO and AXI_DACFIFO use AXI4 interface you can see that in vivado if you click on the axi interface you would have this tab in the left called "Block Interface Properties" and select Properties and scroll down to CONFIG and you would see Protocol AXI4. The formula is a bit different because in ADCFIFO you should provide AXI_LENGTH as its already +1 and for DACFIFO you should provide it as it is -1 and in the IP 1 will be added so when you instantiate AXI_DACFIFO the AXI_LENGTH parameter should be called with the actual value -1 for example AXI_LENGTH = 255 will be considered 256(but this only for AXI_DACFIFO). This wont make any difference, both ADCFIFO and DACFIFO will behave the same.                                                   
    2. AXI_SIZE is used to specify the number of bytes one transfer/beat will have in a burst. A burst is having one or more transfers of 2^AXI_SIZE bytes, you can see that in the AMBA AXI protocol standard:                                                                                         In our IP you need to respect this formula AXI_DATA_WIDTH/8 == 2^AXI_SIZE and you respected it 128/8=2^4=16.                                                                                                                          AXI_LENGTH is the number of transfers/beats that will be send in a burst:                                                                                                                                                                There is no ideal value for AXI_LENGTH, in most of our projects we use it as 255(which is 256 for DACFIFO) because that is the maximum value that AXI4 bus supports and not all the transfers are sent/received successfully so its better to maximize the amount of transfers between the memory components in a single burst. I'm not sure what will happen if you use other values but we have tested everything with 256 and it works fine. AXI_LENGTH does not affect internal BRAM memory capacity or anything. The capacity of those internal memories are usually computed using the following parameters:          
    3. Again it's not very clear to me how are you handling the axi_xfer_req_s in your IP and I'm not sure about how that CDC would work.                                                                                                                                             I would try to initialize axi_last_addr_s with AXI_LAST_ADDR and remove this logic from your code  .                                                                                 This axi_last_beats_s I would let it equal to AXI_LENGTH but I guess this would work only if there are full bursts transfers (XFER_FULL_BURST), so try to make sure that your FSM in _rd.v won't get through XFER_PARTIAL_BURST state.                                                                                                                 I would initialize dma_last_beats with 0 but you have to make sure that MAX_WIDTH!=AXI_DATA_WIDTH so you have a MEM_RATIO of 1 from a _wr.v perspective. 
    4. We have other designs where DDR4 controller is used with DMA. Those changes to AXI_DACFIFO is really niche and beyond our use case and it will require a lot of simulations to make it work as desired. I suggest using an additional DMA between axi_dacfifo and DDR4 if is possible!

    Best regards!

    Filip.

  • Hi  

    Thank you for your reply.

    1. Yes, for axi_length it will not be of any significant difference but for the address mapping while in AXI_DACFIFO it will always be ahead by the amount of AXI_Length as compared to the AXI_ADCFIFO. So for the A(W/R)INCR formula, don't know, will impact drastically or not.

    2. I tried to set AXI_ADCFIFO and AXI_DACFIFO with 256/255 resp. as AXI_LENGTH. After running on live system, AXI_ADCFIFO was not working as DMA was not able to get the Data. So, I think regarding the model number of RAM installed in the ZCU102, it dosen't support large burst for Read/wroite operation as done by ADCFIFO IP. 

    3. Please do tell me what do you want to know in concrete for xfer signal? Moreover, if I set the last_addr as last address of RAM(x9FFF_FFFF) then it will always be in xfer_full_burst state while reading the data. dma_last_beats is initialized as 0. Currently, AXI_BIGGER is 1 as AXI_DATA_WIDTH is greater than any other width and therefore MEM_RATIO is 2 (128 / 64 bits). What is the impact of AXi_BIGGER and MEM_RATIO constant on the read logic.

    4. The idea is to keep less logic on the FPGA and moreover, please correct me If I am wrong, DMA IP core will have to be controlled by the PS which will need extra logic on my custom IP core.

    Regards,

  • Hi  ,

    1. You can try to use the same AXI_LENGTH(less if you want) for both AXI_ADCFIFO and AXI_DACFIFO to keep the symmetry.
    2. Try with AXI_LENGTH=4 for both FIFOs. Both FIFO's work on the same clock domain? I don't think we have a testcase of AXI_ADCFIFO or AXI_DACFIFO on zcu102
    3. I am not familiar with AXI_DACFIFO to that level of complexity, maybe I can try to simulate it to understand what is going on, but that will take a while.Those FIFO's where designed to be used with DMA.
    4. DMA IP has to be controlled by the PS but there are plenty of examples and I don't think that extra logic will drastically affect the FPGA resource utilization. What kind of extra logic would it be required for your IP? 

    Filip.

  • Hi  

    1. By default I tried with AXI_LENGTH = 4. 
    3. The FIFO's are there to output the data at different clock rate. So, I think, please correct me if I am wrong, DMA or any other application will not impact the functionality of it. 
     In your previous post, you mentioned about the MEM_RATIO and AXI_BIGGER. Does they play an important role?

    Thank you for your time.

    Regards,

  • Hi  ,

    As this matter requires testing and investigating, it takes some time to come up with an update. Taking in consideration the fact that we also have other issues to tend to, your patience is required.

    Note that these days there are holidays so this adds a few days delay in answering to threads.

    Thank you for your patience,
    Iulia

  • Hello  ,

    I have been investigating your issue for a couple of weeks but I am not that experienced with those old ADC/DAC FIFOs, on our most recent projects we are using Data Offload IP and unfortunately we are not planning to support this use case on our IP(traditionally those FIFOs are used with DMA, not directly connected on DDR controller) so I'm afraid I cannot help you on this issue anymore.

    Best regards!

    Filip.