Go home AXI_AD7616, you're drunk! Problem with registers reading of the IP CORE, wrong values read.

Hi everyone,

I have noticed a strange behaviour of the IP core AXI_AD7616 (https://wiki.analog.com/resources/fpga/docs/axi_ad7616), so I've prepared the attached testbench in Vivado to investigate.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity test3 is
--
end test3;

architecture Behavior of test3 is
COMPONENT axi_ad7616_0
  PORT (
    rx_sclk : OUT STD_LOGIC;
    rx_cs_n : OUT STD_LOGIC;
    rx_sdo : OUT STD_LOGIC;
    rx_sdi_0 : IN STD_LOGIC;
    rx_sdi_1 : IN STD_LOGIC;
    rx_db_o : OUT STD_LOGIC_VECTOR(15 DOWNTO 0);
    rx_db_i : IN STD_LOGIC_VECTOR(15 DOWNTO 0);
    rx_db_t : OUT STD_LOGIC;
    rx_rd_n : OUT STD_LOGIC;
    rx_wr_n : OUT STD_LOGIC;
    rx_cnvst : OUT STD_LOGIC;
    rx_busy : IN STD_LOGIC;
    s_axi_aclk : IN STD_LOGIC;
    s_axi_aresetn : IN STD_LOGIC;
    s_axi_awvalid : IN STD_LOGIC;
    s_axi_awaddr : IN STD_LOGIC_VECTOR(15 DOWNTO 0);
    s_axi_awprot : IN STD_LOGIC_VECTOR(2 DOWNTO 0);
    s_axi_awready : OUT STD_LOGIC;
    s_axi_wvalid : IN STD_LOGIC;
    s_axi_wdata : IN STD_LOGIC_VECTOR(31 DOWNTO 0);
    s_axi_wstrb : IN STD_LOGIC_VECTOR(3 DOWNTO 0);
    s_axi_wready : OUT STD_LOGIC;
    s_axi_bvalid : OUT STD_LOGIC;
    s_axi_bresp : OUT STD_LOGIC_VECTOR(1 DOWNTO 0);
    s_axi_bready : IN STD_LOGIC;
    s_axi_arvalid : IN STD_LOGIC;
    s_axi_araddr : IN STD_LOGIC_VECTOR(15 DOWNTO 0);
    s_axi_arprot : IN STD_LOGIC_VECTOR(2 DOWNTO 0);
    s_axi_arready : OUT STD_LOGIC;
    s_axi_rvalid : OUT STD_LOGIC;
    s_axi_rresp : OUT STD_LOGIC_VECTOR(1 DOWNTO 0);
    s_axi_rdata : OUT STD_LOGIC_VECTOR(31 DOWNTO 0);
    s_axi_rready : IN STD_LOGIC;
    adc_valid : OUT STD_LOGIC;
    adc_data : OUT STD_LOGIC_VECTOR(15 DOWNTO 0);
    adc_sync : OUT STD_LOGIC;
    irq : OUT STD_LOGIC
  );
END COMPONENT;
     
            signal      rx_sclk_0 :        STD_LOGIC;
            signal      rx_cs_n_0 :        STD_LOGIC;
            signal      rx_sdo_0 :         STD_LOGIC;
            signal      rx_sdi_0_0 :       STD_LOGIC :='0';
            signal      rx_sdi_1_0 :       STD_LOGIC :='0';
            signal      rx_db_o_0 :        STD_LOGIC_VECTOR(15 DOWNTO 0);
            signal      rx_db_i_0 :        STD_LOGIC_VECTOR(15 DOWNTO 0) :=x"0000";
            signal      rx_db_t_0 :        STD_LOGIC :='0';
            signal      rx_rd_n_0 :        STD_LOGIC;
            signal      rx_wr_n_0 :        STD_LOGIC;
            signal      rx_cnvst_0 :       STD_LOGIC;
            signal      rx_busy_0 :        STD_LOGIC :='0';
            signal      s_axi_aclk_0 :     STD_LOGIC;
            signal      s_axi_aresetn_0 :  STD_LOGIC :='0';
            signal      s_axi_awvalid_0 :  STD_LOGIC :='0';
            signal      s_axi_awaddr_0 :   STD_LOGIC_VECTOR(15 DOWNTO 0) :=x"0000";
            signal      s_axi_awprot_0 :   STD_LOGIC_VECTOR(2 DOWNTO 0) :=b"100";
            signal      s_axi_awready_0 :  STD_LOGIC;
            signal      s_axi_wvalid_0 :   STD_LOGIC :='0';
            signal      s_axi_wdata_0 :    STD_LOGIC_VECTOR(31 DOWNTO 0) :=x"00000000";
            signal      s_axi_wstrb_0 :    STD_LOGIC_VECTOR(3 DOWNTO 0) :=x"f";
            signal      s_axi_wready_0 :   STD_LOGIC;
            signal      s_axi_bvalid_0 :   STD_LOGIC;
            signal      s_axi_bresp_0 :    STD_LOGIC_VECTOR(1 DOWNTO 0);
            signal      s_axi_bready_0 :   STD_LOGIC :='1';
            signal      s_axi_arvalid_0 :  STD_LOGIC :='0';
            signal      s_axi_araddr_0 :   STD_LOGIC_VECTOR(15 DOWNTO 0):=x"0000";
            signal      s_axi_arprot_0 :   STD_LOGIC_VECTOR(2 DOWNTO 0):=b"000";
            signal      s_axi_arready_0 :  STD_LOGIC;
            signal      s_axi_rvalid_0 :   STD_LOGIC;
            signal      s_axi_rresp_0 :    STD_LOGIC_VECTOR(1 DOWNTO 0);
            signal      s_axi_rdata_0 :    STD_LOGIC_VECTOR(31 DOWNTO 0);
            signal      s_axi_rready_0 :   STD_LOGIC :='1';
            signal      adc_valid_0 :      STD_LOGIC;
            signal      adc_data_0 :       STD_LOGIC_VECTOR(15 DOWNTO 0);
            signal      adc_sync_0 :       STD_LOGIC;
            signal      irq_0 :            STD_LOGIC;          
  
            -- Clock period definitions
            constant clk_period : time := 25 ns;
   
begin
instance_ad7616_0 : axi_ad7616_0
  PORT MAP (
    rx_sclk => rx_sclk_0,
    rx_cs_n => rx_cs_n_0,
    rx_sdo => rx_sdo_0,
    rx_sdi_0 => rx_sdi_0_0,
    rx_sdi_1 => rx_sdi_1_0,
    rx_db_o => rx_db_o_0,
    rx_db_i => rx_db_i_0,
    rx_db_t => rx_db_t_0,
    rx_rd_n => rx_rd_n_0,
    rx_wr_n => rx_wr_n_0,
    rx_cnvst => rx_cnvst_0,
    rx_busy => rx_busy_0,
    s_axi_aclk => s_axi_aclk_0,
    s_axi_aresetn => s_axi_aresetn_0,
    s_axi_awvalid => s_axi_awvalid_0,
    s_axi_awaddr => s_axi_awaddr_0,
    s_axi_awprot => s_axi_awprot_0,
    s_axi_awready => s_axi_awready_0,
    s_axi_wvalid => s_axi_wvalid_0,
    s_axi_wdata => s_axi_wdata_0,
    s_axi_wstrb => s_axi_wstrb_0,
    s_axi_wready => s_axi_wready_0,
    s_axi_bvalid => s_axi_bvalid_0,
    s_axi_bresp => s_axi_bresp_0,
    s_axi_bready => s_axi_bready_0,
    s_axi_arvalid => s_axi_arvalid_0,
    s_axi_araddr => s_axi_araddr_0,
    s_axi_arprot => s_axi_arprot_0,
    s_axi_arready => s_axi_arready_0,
    s_axi_rvalid => s_axi_rvalid_0,
    s_axi_rresp => s_axi_rresp_0,
    s_axi_rdata => s_axi_rdata_0,
    s_axi_rready => s_axi_rready_0,
    adc_valid => adc_valid_0,
    adc_data => adc_data_0,
    adc_sync => adc_sync_0,
    irq => irq_0
  );

   -- Clock process definitions
   clk_process :process
   begin
		s_axi_aclk_0 <= '0';
		wait for clk_period/2;
		s_axi_aclk_0 <= '1';
		wait for clk_period/2;
   end process;

   -- datain_process: process(clk)
   -- begin
   --  if rising_edge(clk) then
   --    data_from_adc <= not data_from_adc;
   --  end if;
   -- end process; 

   -- Stimulus process
   stim_proc: process
   
   begin		
        -- reset
        wait for 500 ns;	
        s_axi_aresetn_0 <= '0';        
        wait for clk_period;
        s_axi_aresetn_0 <= '1';
         
        --read 
        wait for clk_period;
        s_axi_araddr_0 <= x"0000";
        wait for clk_period;
        s_axi_arvalid_0 <= '1';
        s_axi_rready_0 <= '1';       
        wait until rising_edge(s_axi_arready_0);  --wait the receiver to be ready
        s_axi_arvalid_0 <= '0';       
        wait until rising_edge(s_axi_rvalid_0);   --wait the data from the receiver
        s_axi_rready_0 <= '0'; 
        wait for clk_period; 
        
        --read 
        wait for clk_period;
        s_axi_araddr_0 <= x"0400";
        wait for clk_period;
        s_axi_arvalid_0 <= '1';
        s_axi_rready_0 <= '1';       
        wait until rising_edge(s_axi_arready_0);  --wait the receiver to be ready
        s_axi_arvalid_0 <= '0';       
        wait until rising_edge(s_axi_rvalid_0);   --wait the data from the receiver
        s_axi_rready_0 <= '0'; 
        wait for clk_period; 
        
        --read 
        wait for clk_period;
        s_axi_araddr_0 <= x"0000";
        wait for clk_period;
        s_axi_arvalid_0 <= '1';
        s_axi_rready_0 <= '1';       
        wait until rising_edge(s_axi_arready_0);  --wait the receiver to be ready
        s_axi_arvalid_0 <= '0';       
        wait until rising_edge(s_axi_rvalid_0);   --wait the data from the receiver
        s_axi_rready_0 <= '0'; 
        wait for clk_period; 
    
    wait;   --wait forever
   end process;
   
end Behavior;

THE PROBLEM: I'll try to explain the problem by using the following 3 examples of AXI communication:

Example 1

  • I first read the register 0x00 (SPI Engine Version) and I get the hexadecimal value "00010071" (which I think is correct)
  • then I read the register 0x0400 (REG_VERSION) and I get "00001001"
  • if finally, I read again 0x00, I get "00011071" and not the previous value

Example 2

  • I read 0x00 and I get "00010071"
  • I read 0x440 and I get "00000000"
  • I write 3 to 0x440
  • I read 0x440 and I get "00000003"
  • I read 0x00 and I get "00010073" (wrong value!?)

Example 3

  • I read 0x00 and I get "00010071"
  • I read 0x440 and I get "00000000"
  • I write 3 to 0x440
  • I read 0x440 and I get "00000003"
  • I read 0x400 and I get "00001001"
  • I read 0x00 and I get "00011071" (wrong value!?)

In all 3 cases, I read the wrong values out of 0x00 register after reading/writing some register over 0x400. Looking at the numbers it looks like that a logic OR is performed with the previous value.

Why is this happening? Am I doing something wrong? The solution I have found at the moment is reading the 0x040C register, which in my case is always 0, before doing any operation on a low-address register. In this way, it works but this annoys me a lot...

Thanks for your help or opinion!

Andrea