This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

ADS1256: Serial interface timings t6 and t11

Part Number: ADS1256

I've been using the ADS1256 for a while and so far I've inserted the t6 delay between every byte sent to or received from the ADS1256 just to be on the safe side and to simplify the logic. Now I'm considering to make the logic more efficient and use the t11 delays where applicable. However, I'm having a hard time to understand the t11 numbers in the figure 1 table in the datasheet.

What does 4 T_CLKIN mean for RDATA? Is it the delay between any other command and RDATA or is it the delay between RDATA and any other command? In any case I need to wait t6 = 50 T_CLKIN before driving SCLK to read the sample data so I don't see the meaning of 4 T_CLKIN.

And what about WREG? Is t11 the time between the two bytes that constitute the command? Or between the bytes being written to the ADS1256 registers?

What about WAKEUP? That command is not even listed in the table.

By the way, it would be nice if you could provide a simulation model for the converter. Preferably in VHDL or Verilog.

/F

  • Hi Fredda,

    Let me look into this and get back to you soon, today has been busy.

    I can let you know that we unfortunately do not have a simulation model for any of our delta-sigma ADCs at the moment, including the ADS1256

    -Bryan

  • Hi Fredda,

    The ADS1256 has internal logic that requires certain delays between commands

    The time t6 is for data / register readback in order to get valid information. Time t11 is a delay between adjacent commands.

    For example, if you issue an RDATA command, you need to wait at least 4 tCLKs before issuing any other command. If you do issue another command after 4 tCLKs (but before 50 tCLKs), the RDATA command will be interrupted and you will not be able to get valid data on DOUT per time t6. Instead, the new command would take affect assuming the other timing requirements are all met. If you wanted data at that point you would have to reissue the RDATA command and wait time t6.

    You do not need to wait time t11 between bytes of a multi-byte command e.g. RREG, WREG, etc.

    There is no delay required for issuing another command after the WAKEUP command.

    Let me know if this answers your questions

    -Bryan

  • Thanks. That makes it clearer. So, I've tried to implement the logic as aggressive as possible. Below is an example of figure 19 in the datasheet.

    WREG_1 & MUX register = "01010001"
    WREG_2  & Write one register = "00000000"
    DATA to MUX register = "01011000"

    t11 = 4 TCLKIN pause here before SYNC command. Not sure this is needed??

    SYNC command = "11111100"

    t11 = 24 TCLKIN pause here before WAKEUP command.

    WAKEUP command = "00000000"
    RDATA command = "00000001"

    t6 = 50 TCLKIN pause here before drivning SCLK to retrieve sample data

    Data byte 3 from MUX 58h
    Data byte 2 from MUX 58h
    Data byte 1 from MUX 58h



    It seems to be forking fine. Any comments on this?

    /F

  • Hi Fredda,

    I am glad everything is working well now. Have you been able to verify conversion data yet to make sure the ADC is converting correctly?

    I also did not see a CS signal in your plots (I assumed RDY was the DRDY signal). It would be good to make sure this is signal is being driven correctly as well.

    I will point out that the max SCLK speed is fCLK/4. When fCLK = 7.68 MHz (nominal), SCLK (max) = 1.92 MHz. Your plots show 1.99 MHz, which may not work properly, assuming of course your fCLK = 7.68 MHz.

    -Bryan

  • I haven't made any extensive tests, but at the same time I haven't noticed any odd samples. All sample data is within the expected range.

    The CS signal it tied low during normal operation. 

    Forgot to mention that fCLK = 10 MHz so the nominal 2 MHz SCLK frequency should be fine.

  • For what it's worth, if anyone is interested, here's an attempt to model the behavior of the ADS1256 ADC in VHDL. Obviously it's an interpretation of the datasheet and it's far from accurate, perfect, bug free etc. Use with care!

    -----------------------------------------------
    -- Description:
    --    The ads1256_tb module emulates an ADS1256 ADC. Note that the emulation is
    --    an interpretation of the information given in the ADS1256 data sheet and that
    --    far from all features are modeled.
    --
    ----------------------------------------------------------------------------------
    library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;
    use ieee.math_real.all;   -- for UNIFORM, TRUNC functions
    
    entity ads1256_tb is
      generic(
        G_ADC_CLOCK_FREQUENCY   : real := 7.68;   -- Unit is MHz
          -- Frequency of externally provided ADC clock
        G_MUX_SET_0             : std_logic_vector(7 downto 0) := x"01";
        G_MUX_SET_1             : std_logic_vector(7 downto 0) := x"23";
        G_MUX_SET_2             : std_logic_vector(7 downto 0) := x"45";
        G_MUX_SET_3             : std_logic_vector(7 downto 0) := x"46";
        G_MUX_SET_4             : std_logic_vector(7 downto 0) := x"78";
        G_MUX_SET_5             : std_logic_vector(7 downto 0) := x"01";
        G_MUX_SET_6             : std_logic_vector(7 downto 0) := x"01";
        G_MUX_SET_7             : std_logic_vector(7 downto 0) := x"01"
          -- The MUX generics define the ADC input configuration in the HW.
        );
      port(
        reset                   : in std_logic; 
        adc_sck                 : in std_logic;
        adc_sdi                 : in std_logic;
        adc_cs_n                : in std_logic;
        adc_rdy_n               : out std_logic; 
        adc_sdo                 : out std_logic
        );
    end ads1256_tb;
    
    architecture rtl of ads1256_tb is
    
      ---------------------------------------------------------------------------
      -- ADS1256 Address mappings constants
      ---------------------------------------------------------------------------
      -- The operation of the ADS1256 is controlled through a set of registers. Collectively, the registers
      -- contain all the information needed to configure the part, such as data rate, multiplexer settings,
      -- PGA setting, calibration, etc., and are listed in Table 23 in the datasheet.
      constant C_STATUS_ADDR        : std_logic_vector (3 downto 0) := x"0";
        -- Status register address
      constant C_MUX_ADDR           : std_logic_vector (3 downto 0) := x"1";
        -- MUX register address
      constant C_ADCON_ADDR         : std_logic_vector (3 downto 0) := x"2";
        -- ADC control register address
      constant C_DRATE_ADDR         : std_logic_vector (3 downto 0) := x"3";
        -- Data rate register address
      constant C_IO_ADDR            : std_logic_vector (3 downto 0) := x"4";
        -- IO register address
      constant C_OFC0_ADDR          : std_logic_vector (3 downto 0) := x"5";
        -- Offset calibration register 0 address
      constant C_OFC1_ADDR          : std_logic_vector (3 downto 0) := x"6";
        -- Offset calibration register 1 address
      constant C_OFC2_ADDR          : std_logic_vector (3 downto 0) := x"7";
        -- Offset calibration register 2 address
      constant C_FSC0_ADDR          : std_logic_vector (3 downto 0) := x"8";
        -- Full-scale calibration register 0 address
      constant C_FSC1_ADDR          : std_logic_vector (3 downto 0) := x"9";
        -- Full-scale calibration register 1 address
      constant C_FSC2_ADDR          : std_logic_vector (3 downto 0) := x"A";
        -- Full-scale calibration register 2 address
      constant C_NUM_OF_REG         : integer := 11;
        -- Total number of registers in the ADS1256 ADC.
    
      ---------------------------------------------------------------------------
      -- ADS1256 Command constants
      ---------------------------------------------------------------------------
      constant C_WAKEUP_CMD         : std_logic_vector (7 downto 0) := x"00";
        -- Completes SYNC and Exits Standby Mode. Also x"FF".
      constant C_RDATA_CMD          : std_logic_vector (7 downto 0) := x"01";
        -- Read Data.
      constant C_RDATAC_CMD         : std_logic_vector (7 downto 0) := x"03";
        -- Read Data Continuously.
      constant C_SDATAC_CMD         : std_logic_vector (7 downto 0) := x"0F";
        -- Stop Read Data Continuously.
      constant C_RREG_B1_CMD        : std_logic_vector (3 downto 0) := x"1";
        -- The constant is the upper nibble of byte 1 of the ADC register
        -- read command. It constitute the actual command. The lower
        -- nibble of Byte 1 of the ADC register read command sets the
        -- address of the register to read and is set in the FPGA logic.
      constant C_RREG_B2_CMD        : std_logic_vector (3 downto 0) := x"0";
        -- The constant is the upper nibble of byte 2 of the ADC register
        -- read command. It constitute the actual command. The lower
        -- nibble of Byte 2 of the ADC register read command sets the
        -- number of registers to read and is set in the FPGA logic.
        -- If multiple registers are read, the lower nibble of
        -- byte 1 of the ADC register read command sets the address
        -- of the starting register.
      constant C_RREG_CMD_NOB       : integer := 2;
        -- Number of bytes in the read register command.
      constant C_WREG_B1_CMD        : std_logic_vector (3 downto 0) := x"5";
        -- The constant is the upper nibble of byte 1 of the ADC register
        -- write command. It constitute the actual command. The lower
        -- nibble of Byte 1 of the ADC register write command sets the
        -- address of the register to write and is set in the FPGA logic.
      constant C_WREG_B2_CMD        : std_logic_vector (3 downto 0) := x"0";
        -- The constant is the upper nibble of byte 2 of the ADC register
        -- write command. It constitute the actual command. The lower
        -- nibble of Byte 2 of the ADC register write command sets the
        -- number of registers to write and is set in the FPGA logic.
        -- If multiple registers are written, the lower nibble of
        -- byte 1 of the ADC register write command sets the address
        -- of the starting register.
      constant C_WREG_CMD_NOB       : integer := 2;
        -- Number of bytes in the write register command.
      constant C_SELFCAL_CMD        : std_logic_vector (7 downto 0) := x"F0";
        -- Offset and Gain Self-Calibration.
      constant C_SELFOCAL_CMD       : std_logic_vector (7 downto 0) := x"F1";
        -- Offset Self-Calibration.
      constant C_SELFGCAL_CMD       : std_logic_vector (7 downto 0) := x"F2";
        -- Gain Self-Calibration.
      constant C_SYSOCAL_CMD        : std_logic_vector (7 downto 0) := x"F3";
        -- System Offset Calibration.
      constant C_SYSGCAL_CMD        : std_logic_vector (7 downto 0) := x"F4";
        -- System Gain Calibration.
      constant C_SYNC_CMD           : std_logic_vector (7 downto 0) := x"FC";
        -- Synchronize the A/D Conversion
      constant C_STANDBY_CMD        : std_logic_vector (7 downto 0) := x"FD";
        -- Begin Standby Mode
      constant C_RESET_CMD          : std_logic_vector (7 downto 0) := x"FE";
        -- Reset to Power-Up Values
      constant C_RDATA_NOB          : integer := 3;
        -- Number of bytes returned by the ADC after a C_RDATA_CMD command.
    
      ---------------------------------------------------------------------------
      -- ADS1256 Timing constants
      ---------------------------------------------------------------------------
      constant C_INT_ADC_CLOCK_FREQ         : real    := G_ADC_CLOCK_FREQUENCY*10.0;  -- Unit is MHz.
        -- Run the "internal" ADC clock 10 times faster than the external ADC
        -- clock driving the real ADC.
      constant C_ADS1256_T6_50              : real    := 50.0/G_ADC_CLOCK_FREQUENCY;  -- Unit is us.
        -- Delay from last SCLK edge for DIN to first SCLK rising edge for
        -- DOUT: RDATA, RDATAC, RREG Commands.
      constant C_ADS1256_T11_4              : real    := 4.0/G_ADC_CLOCK_FREQUENCY;  -- Unit is us.
        -- Final SCLK falling edge of command to first rising edge of next command.
        -- For RREG, WREG and RDATA commands.
      constant C_ADS1256_T11_24             : real    := 24.0/G_ADC_CLOCK_FREQUENCY;  -- Unit is us.
        -- Final SCLK falling edge of command to first rising edge of next command.
        -- For RDATAC and SYNC commands.
    
      ---------------------------------------------------------------------------
      -- Types
      ---------------------------------------------------------------------------
      type t_arr0_10_slv7_0 is array (0 to 10) of std_logic_vector(7 downto 0);
      type adc_state_type is (ST_IDLE, ST_PROCESS_DATA, ST_WREG_B2_CMD, ST_RREG_B2_CMD,
                              ST_WREG, ST_RREG, ST_SEND_ADC_DATA, ST_SELFCAL, ST_SYNC, ST_WAKEUP, ST_RESET);
      type spi_if_state_type is (ST_IDLE, ST_RECEIVING_DATA, ST_SENDING_DATA, ST_SPI_IF_BUSY);
      
      ---------------------------------------------------------------------------
      -- Other constants
      ---------------------------------------------------------------------------
      constant C_ADC_REG_BANK_RESET_VALUE   : t_arr0_10_slv7_0 := ("XXXX0001",  -- 0
                                                                   "00000001",  -- 1
                                                                   "00100000",  -- 2
                                                                   "11110000",  -- 3
                                                                   "11100000",  -- 4
                                                                   "XXXXXXXX",  -- 5
                                                                   "XXXXXXXX",  -- 6
                                                                   "XXXXXXXX",  -- 7
                                                                   "XXXXXXXX",  -- 8
                                                                   "XXXXXXXX",  -- 9
                                                                   "XXXXXXXX"); -- 10
        -- Reset array for the ADC registers. Values according to ADS1256 datasheet.
    
      ---------------------------------------------------------------------------
      -- Signals
      ---------------------------------------------------------------------------
      
      -- FSM signals
      signal adc_state    : adc_state_type    := ST_IDLE;
      signal spi_if_state : spi_if_state_type := ST_IDLE;
      
      -- Clock signals
      signal clk                  : std_logic := '0';
    
      -- Edge finding signals
      signal adc_sck_r1           : std_logic := '0';
      signal adc_sck_r2           : std_logic := '0';
      signal adc_sck_r3           : std_logic := '0';
      signal adc_sdi_r1           : std_logic := '0';
      signal adc_sdi_r2           : std_logic := '0';
      signal adc_cs_n_r1          : std_logic := '0';
      signal adc_cs_n_r2          : std_logic := '0';
      signal adc_sck_pos_edg      : std_logic := '0';
      signal adc_sck_neg_edg      : std_logic := '0';
    
      -- ADC control process (adc_control_proc) signals
      signal send_byte            : std_logic := '0';
      signal deassert_adc_rdy_n   : std_logic := '0';
      signal adc_reg_addr         : integer range 0 to 15 := 0;
      signal num_of_reg           : integer range 0 to 15 := 0;
      signal adc_reg_bank         : t_arr0_10_slv7_0 := C_ADC_REG_BANK_RESET_VALUE;
      signal reg_cnt              : integer range 0 to 15 := 0;
      signal data_to_fpga         : std_logic_vector(7 downto 0) := (others => '0');
      signal calibration_active   : std_logic := '0';
      signal t11_4                : std_logic := '0';
      signal t11_24               : std_logic := '0';
      signal t6                   : std_logic := '0';
    
      -- ADC data and sample control
      signal data_o_reg           : std_logic_vector(23 downto 0) := (others => '0');
      signal adc_data_00          : unsigned(23 downto 0) := x"000000";
      signal adc_data_01          : unsigned(23 downto 0) := x"111111";
      signal adc_data_02          : unsigned(23 downto 0) := x"222222";
      signal adc_data_03          : unsigned(23 downto 0) := x"333333";
      signal adc_data_04          : unsigned(23 downto 0) := x"444444";
      signal adc_data_05          : unsigned(23 downto 0) := x"555555";
      signal adc_data_06          : unsigned(23 downto 0) := x"666666";
      signal adc_data_07          : unsigned(23 downto 0) := x"777777";
      signal updating_sample      : std_logic := '0';
      signal sample_ready         : std_logic := '0';
    
      -- ADC timing signals
      signal calib_cnt            : integer range 0 to positive(500.0*C_INT_ADC_CLOCK_FREQ) - 1 := 0;
      signal calib_done           : std_logic := '0';
      signal sample_period_cnt    : integer range 0 to positive(50000.0*C_INT_ADC_CLOCK_FREQ) := 0;
      signal sample_period        : integer range 0 to positive(2.0*50000.0*C_INT_ADC_CLOCK_FREQ) := 0;
    
      -- SPI interface process (spi_interface_proc) signals
      signal fpga_data_received   : std_logic := '0';
      signal sending_data         : std_logic := '0';
      signal spi_if_busy          : std_logic := '0';
      signal data_from_fpga       : std_logic_vector(7 downto 0) := (others => '0');
      signal bit_cnt              : integer range 0 to 7 := 7;
      signal t_busy_cnt           : integer range 0 to 511 := 0;
      signal t_value              : integer range 0 to 511 := 0;
      signal t_busy               : std_logic := '0';
    
    begin
    
      clk <= not clk after integer((1000000.0/C_INT_ADC_CLOCK_FREQ)/2.0) * 1 ps;
    
      -- Antimeta
      antimeta_proc : process(clk)
      begin
        if rising_edge(clk) then
          adc_sck_r1  <= adc_sck;
          adc_sck_r2  <= adc_sck_r1;
          adc_sdi_r1  <= adc_sdi;
          adc_sdi_r2  <= adc_sdi_r1;
          adc_cs_n_r1 <= adc_cs_n;
          adc_cs_n_r2 <= adc_cs_n_r1;
        end if;
      end process;
    
      -- Find positive and negative edges of adc_sck_r2.
      adc_sck_edge_proc : process(clk)
      begin
        if rising_edge(clk) then
          adc_sck_pos_edg <= '0';
          adc_sck_neg_edg <= '0';
          adc_sck_r3 <= adc_sck_r2;
          if adc_sck_r2 = '1' and adc_sck_r3 = '0' then
            adc_sck_pos_edg <= '1';
          end if;
          if adc_sck_r2 = '0' and adc_sck_r3 = '1' then
            adc_sck_neg_edg <= '1';
          end if;
        end if;
      end process;
     
      -- The adc_control_proc process models the ADC command execution.
      adc_control_proc : process(clk)
        variable seed1  : positive;  -- Seed values for random generator
        variable seed2  : positive;  -- Seed values for random generator
        variable rand1  : real;      -- Random real number value in range 0 to 1.0
      begin
        if rising_edge(clk) then
          if reset = '1' then
            send_byte           <= '0';
            deassert_adc_rdy_n  <= '0';
            adc_state           <= ST_IDLE;
            adc_reg_addr        <= 0;
            num_of_reg          <= 0;
            adc_reg_bank        <= C_ADC_REG_BANK_RESET_VALUE;
            reg_cnt             <= 0;
            data_to_fpga        <= (others => '0');
            calibration_active  <= '0';
            t11_4               <= '0';
            t11_24              <= '0';
            t6                  <= '0';
          else
            send_byte           <= '0';
            deassert_adc_rdy_n  <= '0';
            t11_4               <= '0';
            t11_24              <= '0';
            t6                  <= '0';
            if adc_cs_n_r2 = '1' then    -- A logic high on Chip Select resets the ADC SPI interface.
              send_byte           <= '0';
              deassert_adc_rdy_n  <= '0';
              adc_state           <= ST_IDLE;
              adc_reg_addr        <= 0;
              num_of_reg          <= 0;
              reg_cnt             <= 0;
              data_to_fpga        <= (others => '0');
              calibration_active  <= '0';
            else
              case adc_state is
                when ST_IDLE =>
                  if fpga_data_received = '1' then
                    adc_state <= ST_PROCESS_DATA;
                  end if;
                when ST_PROCESS_DATA =>
                  if data_from_fpga(7 downto 4) = C_WREG_B1_CMD then
                    adc_reg_addr  <= to_integer(unsigned(data_from_fpga(3 downto 0)));
                    adc_state     <= ST_WREG_B2_CMD;
                  elsif data_from_fpga(7 downto 4) = C_RREG_B1_CMD then
                    adc_reg_addr <= to_integer(unsigned(data_from_fpga(3 downto 0)));
                    adc_state <= ST_RREG_B2_CMD;
                  else
                    case data_from_fpga is 
                      when C_WAKEUP_CMD =>
                        adc_state <= ST_WAKEUP;
                      when C_RDATA_CMD =>
                        t6        <= '1';
                        adc_state <= ST_SEND_ADC_DATA;
                      when C_RDATAC_CMD =>
                        t6        <= '1';
                        adc_state <= ST_IDLE;
                      when C_SDATAC_CMD =>
                        t11_24    <= '1';   --???
                        adc_state <= ST_IDLE;
                      when C_SELFCAL_CMD =>
                        adc_state <= ST_SELFCAL;
                      when C_SYNC_CMD =>
                        t11_24    <= '1';
                        adc_state <= ST_SYNC;
                      when C_STANDBY_CMD =>
                        adc_state <= ST_IDLE;
                      when C_RESET_CMD =>
                        adc_state <= ST_RESET;
                      when others =>
                        adc_state <= ST_IDLE;
                    end case;
                  end if;
                when ST_WREG_B2_CMD =>
                  if fpga_data_received = '1' then
                    num_of_reg  <= to_integer(unsigned(data_from_fpga(3 downto 0)));
                    adc_state   <= ST_WREG;
                  end if;
                when ST_RREG_B2_CMD =>
                  if fpga_data_received = '1' then
                    t6          <= '1';
                    num_of_reg  <= to_integer(unsigned(data_from_fpga(3 downto 0)));
                    adc_state   <= ST_RREG;
                  end if;
                when ST_WREG =>
                  if fpga_data_received = '1' then
                    adc_reg_bank(adc_reg_addr + reg_cnt) <= data_from_fpga;
                    if reg_cnt < num_of_reg then
                      reg_cnt <= reg_cnt + 1;
                    else
                      reg_cnt   <= 0;
                      t11_4     <= '1';
                      adc_state <= ST_IDLE;
                    end if;
                  end if;
                when ST_RREG =>
                  if spi_if_busy = '0' then
                    send_byte <= '1';
                    data_to_fpga <= adc_reg_bank(adc_reg_addr + reg_cnt);
                    if reg_cnt < num_of_reg then
                      reg_cnt <= reg_cnt + 1;
                    else
                      reg_cnt   <= 0;
                      adc_state <= ST_IDLE;
                    end if;
                  end if;
                when ST_SEND_ADC_DATA =>
                  if spi_if_busy = '0' then
                    send_byte     <= '1';
                    data_to_fpga  <= data_o_reg(23 - 8*reg_cnt downto 16 - 8*reg_cnt);
                    if reg_cnt < 2 then
                      reg_cnt <= reg_cnt + 1;
                    else
                      deassert_adc_rdy_n <= '1';
                      reg_cnt   <= 0;
                      adc_state <= ST_IDLE;
                    end if;
                  end if;
                when ST_SELFCAL =>
                  calibration_active <= '1';
                  if calib_done = '1' then
                    uniform(seed1, seed2, rand1);
                    adc_reg_bank(to_integer(unsigned(C_OFC0_ADDR))) <= std_logic_vector(to_unsigned(integer(rand1*255.0*0.2), 8));
                    adc_reg_bank(to_integer(unsigned(C_OFC1_ADDR))) <= std_logic_vector(to_unsigned(integer(rand1*255.0*0.4), 8));
                    adc_reg_bank(to_integer(unsigned(C_OFC2_ADDR))) <= std_logic_vector(to_unsigned(integer(rand1*255.0*0.6), 8));
                    adc_reg_bank(to_integer(unsigned(C_FSC0_ADDR))) <= std_logic_vector(to_unsigned(integer(rand1*255.0*0.8), 8));
                    adc_reg_bank(to_integer(unsigned(C_FSC1_ADDR))) <= std_logic_vector(to_unsigned(integer(rand1*255.0*0.9), 8));
                    adc_reg_bank(to_integer(unsigned(C_FSC2_ADDR))) <= std_logic_vector(to_unsigned(integer(rand1*255.0*1.0), 8));
                    calibration_active <= '0';
                    adc_state <= ST_IDLE;
                  end if;
                when ST_SYNC =>
                  adc_state <= ST_IDLE;
                when ST_WAKEUP =>
                  adc_state <= ST_IDLE;
                when ST_RESET =>  -- After releasing from RESET, self-calibration is performed, regardless of the reset method or the state of the ACAL bit before RESET.
                  adc_reg_bank  <= C_ADC_REG_BANK_RESET_VALUE;
                  adc_state     <= ST_SELFCAL;
                when others =>
                  adc_state <= ST_IDLE;
              end case;
            end if;
          end if;
        end if;
      end process;
    
      -- The calibration_proc process models the self-calibration time. The calibration time
      -- actually depends on the PGA and the data rate setting, but that dependency is not modeled here.
      calibration_proc : process(clk)
      begin
        if rising_edge(clk) then
          if reset = '1' then
            calib_cnt <= 0;
            calib_done <= '0';
          else
            calib_done <= '0';
            if calibration_active = '1' then
              if calib_cnt < positive(500.0*C_INT_ADC_CLOCK_FREQ) - 1 then
                calib_cnt <= calib_cnt + 1;
              else
                calib_cnt <= 0;
                calib_done <= '1';
              end if;
            else
              calib_cnt <= 0;
              calib_done <= '0';
            end if;
          end if;
        end if;
      end process;
      
      -- Assign data to adc_data_xx. Depends on the MUX setting (register C_MUX_ADDR).
      -- Only assign static data for making the test bench less complex.
      -- Also, it makes it is easier to see if the MUX setting is effective.
      -- The data_o_reg represents the ADC sample data output register, which isn't updated until
      -- the sample_ready signal is asserted. Otherwise the output data would change as soon
      -- as the MUX setting changes, which wouldn't model the ADC correctly.
      adc_data_proc : process(clk)
      begin
        if rising_edge(clk) then
          if reset = '1' then
            adc_data_00 <= x"000000";
            adc_data_01 <= x"111111";
            adc_data_02 <= x"222222";
            adc_data_03 <= x"333333";
            adc_data_04 <= x"444444";
            adc_data_05 <= x"555555";
            adc_data_06 <= x"666666";
            adc_data_07 <= x"777777";
            data_o_reg  <= (others => '0');
          else
            if sample_ready = '1' then
              if adc_reg_bank(to_integer(unsigned(C_MUX_ADDR))) = G_MUX_SET_0 then
                adc_data_00 <= adc_data_00 + 1;
                data_o_reg <= std_logic_vector(adc_data_00);
              elsif adc_reg_bank(to_integer(unsigned(C_MUX_ADDR))) = G_MUX_SET_1 then
                adc_data_01 <= adc_data_01 + 1;
                data_o_reg <= std_logic_vector(adc_data_01);
              elsif adc_reg_bank(to_integer(unsigned(C_MUX_ADDR))) = G_MUX_SET_2 then
                adc_data_02 <= adc_data_02 + 1;
                data_o_reg <= std_logic_vector(adc_data_02);
              elsif adc_reg_bank(to_integer(unsigned(C_MUX_ADDR))) = G_MUX_SET_3 then
                adc_data_03 <= adc_data_03 + 1;
                data_o_reg <= std_logic_vector(adc_data_03);
              elsif adc_reg_bank(to_integer(unsigned(C_MUX_ADDR))) = G_MUX_SET_4 then
                adc_data_04 <= adc_data_04 + 1;
                data_o_reg <= std_logic_vector(adc_data_04);
              elsif adc_reg_bank(to_integer(unsigned(C_MUX_ADDR))) = G_MUX_SET_5 then
                adc_data_05 <= adc_data_05 + 1;
                data_o_reg <= std_logic_vector(adc_data_05);
              elsif adc_reg_bank(to_integer(unsigned(C_MUX_ADDR))) = G_MUX_SET_6 then
                adc_data_06 <= adc_data_06 + 1;
                data_o_reg <= std_logic_vector(adc_data_06);
              elsif adc_reg_bank(to_integer(unsigned(C_MUX_ADDR))) = G_MUX_SET_7 then
                adc_data_07 <= adc_data_07 + 1;
                data_o_reg <= std_logic_vector(adc_data_07);
              end if;
            end if;
          end if;
        end if;
      end process;
    
      -- The data_rate_proc process sets the ADC sampling period, which depends on the data rate setting
      -- given by register C_DRATE_ADDR.
      -- The periods are not the actual values since the simulation would take too long with real values.
      -- Also, the period depends on the ADS1256 master clock. The period values below are 1/10, 1/100 or 1/1000.
      -- of the real values for a ADS1256 master clock = 7.68 MHz.
      data_rate_proc : process(clk)
      begin
        if rising_edge(clk) then
          if reset = '1' then
            sample_period <= positive(100.0*C_INT_ADC_CLOCK_FREQ);
          else
            case adc_reg_bank(to_integer(unsigned(C_DRATE_ADDR))) is
              when "11110000" =>    -- 30 000
                sample_period <= positive(2.0*3.3*C_INT_ADC_CLOCK_FREQ);
              when "11100000" =>    -- 15 000
                sample_period <= positive(2.0*6.7*C_INT_ADC_CLOCK_FREQ);
              when "11010000" =>    -- 7 500
                sample_period <= positive(2.0*13.3*C_INT_ADC_CLOCK_FREQ);
              when "11000000" =>    -- 3 750
                sample_period <= positive(2.0*26.6*C_INT_ADC_CLOCK_FREQ);
              when "10110000" =>    -- 2 000
                sample_period <= positive(2.0*5.0*C_INT_ADC_CLOCK_FREQ);
              when "10100001" =>    -- 1 000
                sample_period <= positive(2.0*10.0*C_INT_ADC_CLOCK_FREQ);
              when "10010010" =>    -- 500
                sample_period <= positive(2.0*20.0*C_INT_ADC_CLOCK_FREQ);
              when "10000010" =>    -- 100
                sample_period <= positive(2.0*100.0*C_INT_ADC_CLOCK_FREQ);
              when "01110010" =>    -- 60
                sample_period <= positive(2.0*166.6*C_INT_ADC_CLOCK_FREQ);
              when "01100011" =>    -- 50
                sample_period <= positive(2.0*200.0*C_INT_ADC_CLOCK_FREQ);
              when "01010011" =>    -- 30
                sample_period <= positive(2.0*333.3*C_INT_ADC_CLOCK_FREQ);
              when "01000011" =>    -- 25
                sample_period <= positive(2.0*400.0*C_INT_ADC_CLOCK_FREQ);
              when "00110011" =>    -- 15
                sample_period <= positive(2.0*666.6*C_INT_ADC_CLOCK_FREQ);
              when "00100011" =>    -- 10
                sample_period <= positive(2.0*111.0*C_INT_ADC_CLOCK_FREQ);
              when "00010011" =>    -- 5
                sample_period <= positive(2.0*222.0*C_INT_ADC_CLOCK_FREQ);
              when "00000011" =>    -- 2.5
                sample_period <= positive(2.0*500.0*C_INT_ADC_CLOCK_FREQ);
              when others =>
                sample_period <= positive(100.0*C_INT_ADC_CLOCK_FREQ);
            end case;
          end if;
        end if;
      end process;
    
      -- The sample_proc process counts the sampling period and controls, together with the adc_rdy_proc process, the assertion of adc_rdy_n.
      -- adc_rdy_n goes low to indicate that a sample is ready. After all 24 bits have been shifted out on adc_sdo,
      -- adc_rdy_n goes high. It is not necessary to read back all 24 bits, but adc_rdy_n will then not return high until new
      -- data is being updated. This is modeled by the updating_sample signal. It takes 16 ADC master clock cycles to update
      -- the ADC data. t17 in the ADS1256 datasheet.
      -- adc_rdy_n = /DRDY, adc_sdo = DOUT
      sample_proc : process(clk)
      begin
        if rising_edge(clk) then
          if reset = '1' then
            sample_period_cnt <= 0;
            sample_ready <= '0';
            updating_sample <= '0';
          else
            sample_ready <= '0';
            if sample_period_cnt = sample_period - 16 then
              updating_sample <= '1';
            else
              updating_sample <= '0';
            end if;
            if calibration_active = '1' then
              sample_period_cnt  <= 0;
            elsif sample_period_cnt < sample_period - 1 then
              sample_period_cnt <= sample_period_cnt + 1;
            else
              sample_period_cnt   <= 0;
              sample_ready <= '1';
            end if;
          end if;
        end if;
      end process;
      
      -- The adc_rdy_proc process controls the assertion of adc_rdy_n.
      adc_rdy_proc : process(clk)
      begin
        if rising_edge(clk) then
          if reset = '1' then
            adc_rdy_n <= '1';
          else
            if calibration_active = '1' then
              adc_rdy_n <= '1';
            elsif updating_sample = '1' then
              adc_rdy_n <= '1';
            elsif sample_ready = '1' then
              adc_rdy_n <= '0';
            elsif deassert_adc_rdy_n = '1' then
              adc_rdy_n <= '1';
            end if;
          end if;
        end if;
      end process;
    
      spi_if_busy <= sending_data or send_byte or t6 or t11_4 or t11_24 or t_busy;
    
      -- The spi_interface_proc process controls the adc_sdi and adc_sdo data interface.
      -- Not modeled:
      --    - If SCLK is held low for 32 DRDY periods, the serial interface will reset and the
      --      next SCLK pulse will start a new communication cycle.
      --    - A special pattern on SCLK will reset the chip.
      --
      -- The ST_SPI_IF_BUSY state models the t6 and t11 ADC SPI serial interface timings.
      spi_interface_proc : process(clk)
      begin
        if rising_edge(clk) then
          if reset = '1' then
            fpga_data_received  <= '0';
            sending_data        <= '0';
            spi_if_state        <= ST_IDLE;
            data_from_fpga      <= (others => '0');
            bit_cnt             <= 7;
            adc_sdo             <= 'Z';
            t_value             <= 0;
            t_busy_cnt          <= 0;
            t_busy              <= '0';
          else
            fpga_data_received <= '0';
            if adc_cs_n_r2 = '1' then    -- A logic high on Chip Select resets the ADC SPI interface.
              fpga_data_received  <= '0';
              sending_data        <= '0';
              spi_if_state        <= ST_IDLE;
              data_from_fpga      <= (others => '0');
              bit_cnt             <= 7;
              adc_sdo             <= 'Z'; -- When CS is taken high, the serial interface is reset and DOUT enters a high impedance state. 
              t_value             <= 0;
              t_busy_cnt          <= 0;
              t_busy              <= '0';
            else
              case spi_if_state is
                when ST_IDLE =>
                  if t6 = '1' then
                    spi_if_state  <= ST_SPI_IF_BUSY;
                    t_value       <= positive(C_ADS1256_T6_50*C_INT_ADC_CLOCK_FREQ) - 6;  -- Compensate for inter process communications.
                    t_busy        <= '1';
                  elsif t11_4 = '1' then
                    spi_if_state  <= ST_SPI_IF_BUSY;
                    t_value       <= positive(C_ADS1256_T11_4*C_INT_ADC_CLOCK_FREQ) - 5; 
                    t_busy        <= '1';
                  elsif t11_24 = '1' then
                    spi_if_state  <= ST_SPI_IF_BUSY;
                    t_value       <= positive(C_ADS1256_T11_24*C_INT_ADC_CLOCK_FREQ) - 5;
                    t_busy        <= '1';
                  elsif send_byte = '1' then
                    sending_data  <= '1';
                    spi_if_state  <= ST_SENDING_DATA;
                  elsif adc_sck_pos_edg = '1' then
                    spi_if_state  <= ST_RECEIVING_DATA;
                  end if;
                when ST_RECEIVING_DATA =>
                  if adc_sck_neg_edg = '1' then
                    data_from_fpga(bit_cnt) <= adc_sdi_r2;
                    if bit_cnt > 0 then
                      bit_cnt <= bit_cnt - 1;
                    else
                      fpga_data_received  <= '1';
                      bit_cnt             <= 7;
                      spi_if_state        <= ST_IDLE ;
                    end if;
                  end if;
                when ST_SENDING_DATA =>
                  if adc_sck_pos_edg = '1' then
                    adc_sdo <= data_to_fpga(bit_cnt);
                    if bit_cnt > 0 then
                      bit_cnt <= bit_cnt - 1;
                    else
                      sending_data  <= '0';
                      bit_cnt       <= 7;
                      spi_if_state <= ST_IDLE ;
                    end if;
                  end if;
                when ST_SPI_IF_BUSY =>
                  if t_busy_cnt < t_value - 1 then
                    t_busy_cnt <= t_busy_cnt + 1;
                  else
                    t_busy_cnt    <= 0;
                    t_busy        <= '0';
                    spi_if_state  <= ST_IDLE;
                  end if;
                when others =>
                  spi_if_state  <= ST_IDLE;
              end case;
            end if;
          end if;
        end if;
      end process;
      
    
    end rtl;
    
    

  • Thanks Fredda, this will be visible and useful to anyone who visits the E2E forum!

    -Bryan