-- Created:
--          JUSTIN DEARY
--          06/14/2006
--          updated 10/2/2006
--
-------------------------------------------------------------

-- This SPI controller is designed for the ADS1241 8 channel A/D.
-- Three of the four channels are being used 
--  channel 0  - 2489/2998 ambient temp for thermal controler
--  channel 1  - 2489/2998 bench temp for thermal controler
--  channel 2  - FLR thermistor or 2489/2998 reference

-- The controller reads the 3 channels from A/D and stores them into 
-- 3 24 bit registers.  The controller also writes the callibration
-- and setup registers.  their values can be changed by writing to their
-- CONSTANT vectors in this VHDL file.

-- Component Requirements
-- CLOCK must be as ENTITY describes


LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;

--******************************************************************
-- I/O DEFINITIONS


--EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
ENTITY SPI_CNTRL IS
  PORT(           
  SYSCLOCK        : IN  STD_LOGIC:='0';   --fastest system clock  
  CLOCK           : IN  STD_LOGIC:='0';   --FLR & 2489 us 64KHz
                      -- Max machine "CLOCK" freq must be no greater then 614.4kHZ
                      -- Max machine freq limited by Maximum speced SCLK freq
                      -- Min  machine "CLOCK" freq must be no smaller then 64kHZ
                      -- Min machine freq limited by Maximum sclk communication of 10msec 
                      -- Designed for ADS1241 A/D at Fosc of 2.4MHz or 4.9MHz with all spec sample rates  
  
  RESET             : IN  STD_LOGIC;    -- high active - PLD Reset  
  
  --THERMISTOR A/D SERIAL INTERFACE
  SCLK            : OUT   STD_LOGIC;   -- pos_edge      - data clock to thermistor A/D
  CS_n            : OUT   STD_LOGIC;   -- low active    - serial CS from fpga to thermistor A/D  
  DATA_FROM_AD    : IN    STD_LOGIC;   -- data          - data to FPGA from thermistor A/D 
  DATA2AD         : OUT   STD_LOGIC;   -- data          - data from FPGA to thermistor A/D 
  DATA_RDY        : IN    STD_LOGIC;   -- interupt in   - data ready 
  DATA_RDY_SYNC   : OUT   STD_LOGIC;   -- interupt out  - synced version of input DATA_RDY   

  DATA_CH0      : OUT  STD_LOGIC_VECTOR (23 downto 0);  -- Output Temperature DATA Registers
  DATA_CH1      : OUT  STD_LOGIC_VECTOR (23 downto 0); 
  DATA_CH2      : OUT  STD_LOGIC_VECTOR (23 downto 0)
  );                     
  
END SPI_CNTRL;
--EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE


--AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
--THE ARCHITECTURE BODY DESCRIBES THE FUNCTIONAL COMPOSITION OF THIS DESIGN
ARCHITECTURE rtl OF SPI_CNTRL IS     -- pulse starts the control sequence for charge amp electronics

CONSTANT  CALIBRATE  : STD_LOGIC_VECTOR (7 downto 0):=  B"1111_0000";                       -- cal Reg
CONSTANT  MUXCNTR    : STD_LOGIC_VECTOR (23 downto 0):= B"0101_0010_0000_0000_0000_0000";   --acr register
CONSTANT  MUXADC     : STD_LOGIC_VECTOR (15 downto 0):= B"0101_0001_0000_0000";             -- MUX reg
CONSTANT  CHAN0      : STD_LOGIC_VECTOR (7 downto 0):=  B"0000_1000";                       -- Mux Channel 0
CONSTANT  CHAN1      : STD_LOGIC_VECTOR (7 downto 0):=  B"0001_1000";                       -- Mux Channel 1
CONSTANT  CHAN2      : STD_LOGIC_VECTOR (7 downto 0):=  B"0010_1000";                       -- Mux Channel 2
CONSTANT  READCMD    : STD_LOGIC_VECTOR (7 downto 0):=  B"0000_0001";                       -- Read command

SIGNAL DRDY1           :STD_LOGIC;    -- half synced version of input DATA_RDY 
SIGNAL DRDY_SYNC       :STD_LOGIC;    -- synced version of input DATA_RDY
SIGNAL DRDY_SYNC2      :STD_LOGIC;    -- double synced version of input DATA_RDY

SIGNAL  MUX_CHAN_CNT   :INTEGER RANGE 2 downto 0;   --count for cycling through ADC channels

SIGNAL INC_CNT         :STD_LOGIC;   -- logic high increments machine counter
SIGNAL ZERO_CNT        :STD_LOGIC;   -- logic high zeros machine counter
SIGNAL COUNT           :INTEGER  RANGE 0 to 45 := 0;  -- machine counter

SIGNAL CURRENT_STATE   :INTEGER  RANGE 0 to 24; -- MACHINE CURRENT STATE DEFINITION
SIGNAL NEXT_STATE      :INTEGER  RANGE 0 to 24; -- MACHINE NEXT STATE DEFINITION

BEGIN
  


  
 
  -- THIS PROCESS SYNCS THE DATA_RDY SIGNAL TO SYSTEM CLOCK.  
  INTERUPT_SYNC: PROCESS (SYSCLOCK,RESET)  
  begin     
      if (RESET = '1') then
          DRDY_SYNC <= '1';
          DRDY1     <= '1';         
      elsif rising_edge(SYSCLOCK)  then  
          DRDY1      <= DATA_RDY;
          DRDY_SYNC  <= DRDY1; 
      end if;                             
  END PROCESS INTERUPT_SYNC;



 -- THIS PROCESS GENERATES A COUNTER USED FOR STATE MACHINE COUNTED LOOPS  
DELAY_COUNTER: PROCESS(CLOCK,RESET)     -- ZERO_CNT and INC_CNT should never both be high!                           
 begin                                       -- Define only one active during a single state.
   if RESET = '1' then
       COUNT <= 0;  
   elsif rising_edge(CLOCK) then    
     if ZERO_CNT = '1' then
       COUNT <= 0;
     elsif INC_CNT = '1' then
       COUNT <= COUNT + 1;
     else  
       COUNT <= COUNT;
     end if;
   end if; 
END PROCESS DELAY_COUNTER;




 -- THIS PROCESS GENERATES A LOOP COUNT FOR DEFINING WHAT CHANNEL TO LOAD TO THE A/D.
 -- THIS COUNT WAS USED TO SIMPLIFY THE STATE MACHINE BELOW. ONLY 3 OF THE 8 CHANNELS 
 -- ARE MUXED WITH THIS CYCLE LOOP.
MUX_CYCLE: PROCESS(CLOCK,RESET)     -- INCRIMENTS ON EVERY DATA READY FROM A/D                          
 begin 
  if RESET = '1'then
        MUX_CHAN_CNT <= 0;                            
  elsif rising_edge (CLOCK) then
        DRDY_SYNC2      <= DRDY_SYNC;
        if DRDY_SYNC = '1' and DRDY_SYNC2 = '0' and MUX_CHAN_CNT = 2 then
           MUX_CHAN_CNT <= 0;                     --reset count on rising edge of DRDY
        elsif DRDY_SYNC = '1' and DRDY_SYNC2 = '0' then
           MUX_CHAN_CNT <= MUX_CHAN_CNT + 1;      --increment count on rising edge of DRDY
        end if;     
  end if; 
END PROCESS MUX_CYCLE;


-- THIS PROCESS HOLDS THE SEQUENTIAL "CURRENT STATE" GENERATOR FOR THE SPI STATE MACHINE.  
  SPI_SEQ: PROCESS(CLOCK,RESET)                                
  begin
    if RESET = '1' then    
        CURRENT_STATE <= 0;  
    elsif rising_edge(CLOCK) then             -- every reset brings machine to initial state
        CURRENT_STATE <=  NEXT_STATE;         -- state change on rising  edge of clock          
    end if;
    
  END PROCESS SPI_SEQ;
  
 
  
-- THIS PROCESS HOLDS THE NEXT STATE COMBINATORIAL LOGIC FOR THE SPI STATE MACHINE.  
  SPI_COMB: PROCESS(CURRENT_STATE,DRDY_SYNC)     
  begin 

    Case (CURRENT_STATE) is
      
--************************************************************************
-- states 0 through 4 writes 8 bits to calibration register
--************************************************************************      
    when 0  =>       
       if (DRDY_SYNC = '0') then     -- wait untill falling edge of DRDY    
         NEXT_STATE <= 1;
       else
         NEXT_STATE <= 0;
       end if;
       
    when 1  =>  
      NEXT_STATE <= 2; 
     
    when 2  =>    
      NEXT_STATE  <= 3; 
         
    when 3  =>               
      if (COUNT < 7) then          -- write 8 bits of CALIBRATE register 
        NEXT_STATE <= 2;
      else          
        NEXT_STATE <= 4;        
      end if;
      
    when 4  =>  
        if (DRDY_SYNC = '0') then    -- wait for rising edge of DRDY     
          NEXT_STATE <= 4;
        else
          NEXT_STATE <= 5;
        end if;                       
      
--************************************************************************
-- state 5 through 9 writes 24 bits to control register
--************************************************************************           
    when 5  =>                  -- wait untill falling edge of DRDY   
       if (DRDY_SYNC = '0') then
         NEXT_STATE <= 6;
       else
         NEXT_STATE <= 5;
       end if;
       
    when 6  =>        
      NEXT_STATE <= 7; 
     
    when 7  =>       
      NEXT_STATE <= 8; 
         
    when 8  =>
      if (COUNT < 23) then          -- write 24 bits of ACR register 
        NEXT_STATE <= 7;
      else         
        NEXT_STATE <= 9;        
      end if;
      
    when 9  =>  
        if (DRDY_SYNC = '0') then    -- wait for rising edge of DRDY  
          NEXT_STATE <= 9;
        else
          NEXT_STATE <= 10;
        end if;                            
      
 --*********************************************************************************
  -- state 10 through state 23 
  -- Write ADC Mux register for muxing to channel 0,1,3 then read channel data 
  --*********************************************************************************                  
    when 10  =>                 -- wait untill falling edge of DRDY 
       if (DRDY_SYNC = '0') then         
         NEXT_STATE <= 11;
       else 
         NEXT_STATE <= 10;
       end if;
       
    when 11  =>                
      NEXT_STATE <= 12; 
     
    when 12  =>  
      NEXT_STATE <= 13;
        
    when 13  =>                 
      if (COUNT < 23) then        -- write 24 bits of Mux register      
        NEXT_STATE <= 12;        
      else       
        NEXT_STATE <= 14;
      end if;    
    
    when 14  =>       
        NEXT_STATE <= 15;    
    
    when 15  =>  
      NEXT_STATE <= 16;
        
    when 16  =>                  -- write 8 bits of Read Command
      if (COUNT < 7) then             
        NEXT_STATE <= 15;        
      else       
        NEXT_STATE <= 17;    
      end if;
          
    when 17  =>      
      if (COUNT < 40) then     -- wait time t6 on a/d spec (min 50 t(osc))       
        NEXT_STATE <= 18;
      else
        NEXT_STATE <= 19;        
      end if;
       
    when 18  =>               
        NEXT_STATE <= 17;      
        
    when 19  =>  
        NEXT_STATE <= 20;  
        
    when 20  =>         
        NEXT_STATE <= 21; 
        
    when 21  =>  
        if (COUNT < 24) then        -- read 24 bits of ADC data         
           NEXT_STATE <= 22;
        else          
           NEXT_STATE <= 23;      
        end if;      
            
    when 22  =>                               
       NEXT_STATE <= 21; 
       
    when 23  =>        
       NEXT_STATE <= 24;        
       
    when 24  =>                     -- wait for rising edge of DRDY 
        if (DRDY_SYNC = '0') then         
          NEXT_STATE <= 24;
        else
          NEXT_STATE <= 10;
        end if;                 
      
    when others =>  
           NEXT_STATE <= 0;      
  end Case;
 
END PROCESS SPI_COMB;



-- THIS PROCESS HOLDS THE OUTPUT COMBINATORIAL LOGIC FOR THE SPI STATE MACHINE.  
  SPI_OUTPUT: PROCESS(CURRENT_STATE)     
  begin 
       CS_n       <= '0';
       SCLK       <= '0';       
       INC_CNT    <= '0';
       ZERO_CNT   <= '0';  
       DATA2AD    <= '0';
       DATA_RDY_SYNC <= '0';
    Case (CURRENT_STATE) is
      
--************************************************************************
-- states 0 through 4 writes 8 bits to calibration register
--************************************************************************      
    when 0  => 
      CS_n         <= '1';           
    when 1  =>       
      ZERO_CNT     <= '1'; 
     
    when 2  =>  
      SCLK         <= '1';           
      DATA2AD      <= CALIBRATE(7-COUNT);      

    when 3  =>                           
      INC_CNT      <= '1';          -- Calibrate data bit write on falling edge of sclk
      DATA2AD      <= CALIBRATE(7-COUNT);              
       
    when 4  =>  
        CS_n       <= '1';        
                          
--************************************************************************
-- state 5 through 9 writes 24 bits to control register
--************************************************************************      
      
    when 5  => 
       CS_n       <= '1';  
       
    when 6  =>  
      ZERO_CNT    <= '1';         
     
    when 7  =>  
      SCLK        <= '1';    
      DATA2AD     <= MUXCNTR(23-COUNT);      
         
    when 8  =>
      INC_CNT     <= '1';     -- mux data bit write on falling edge of sclk
      DATA2AD     <= MUXCNTR(23-COUNT);   
      
    when 9  =>  
        CS_n       <= '1';        
                               
 --*********************************************************************************
  -- state 10 through state 23 
  -- Write ADC Mux register for muxing to channel 0,1,2 then read channel data 
  --*********************************************************************************        
           
    when 10  =>  
       CS_n       <= '1';

    when 11  =>  
      ZERO_CNT    <= '1';               

    when 12  =>  
      SCLK        <= '1';  
      if (COUNT < 16)then
         DATA2AD <= MUXADC(15-COUNT);  -- write next bit of MULTIPLEXER OP CODE register       
      else
            if (MUX_CHAN_CNT = 0) then
                DATA2AD <= CHAN0(23-COUNT);  -- write next bit of Channel 0 MULTIPLEXER register 
            elsif (MUX_CHAN_CNT = 1) then   
                DATA2AD <= CHAN1(23-COUNT);  -- write next bit of Channel 1 MULTIPLEXER register
            elsif (MUX_CHAN_CNT = 2) then 
                DATA2AD <= CHAN2(23-COUNT);  -- write next bit of Channel 2 MULTIPLEXER register
            else
                DATA2AD <= '0';
            end if;
      end if;  

        
    when 13  =>            -- Mux register data bit write on falling edge of sclk      
        INC_CNT     <= '1';
      if (COUNT < 16)then
         DATA2AD    <= MUXADC(15-COUNT);  -- write next bit of MULTIPLEXER OP CODE register       
      else
            if (MUX_CHAN_CNT = 0) then
                DATA2AD <= CHAN0(23-COUNT);  -- write next bit of Channel 0 MULTIPLEXER register 
            elsif (MUX_CHAN_CNT = 1) then   
                DATA2AD <= CHAN1(23-COUNT);  -- write next bit of Channel 1 MULTIPLEXER register
            elsif (MUX_CHAN_CNT = 2) then 
                DATA2AD <= CHAN2(23-COUNT);  -- write next bit of Channel 2 MULTIPLEXER register
            else
                DATA2AD <= '0';
            end if;
      end if;                
    
    when 14  =>       
        ZERO_CNT   <= '1'; 
        
    when 15  =>        
        SCLK       <= '1';       --sclk rising edge of data read command        
        DATA2AD    <= READCMD(7-COUNT);
                      
    when 16  =>  
        INC_CNT    <= '1';        -- Read command data bit write on falling edge of sclk
        DATA2AD    <= READCMD(7-COUNT);                      

    when 17  =>                  -- wait time t6 on a/d spec (min 50 t(osc)) 
    
    when 18  =>     
        INC_CNT    <= '1';                 

    when 19  =>        
        ZERO_CNT   <= '1';
   
    when 20  =>     
        SCLK       <= '1';       -- first sclk rising edge of data read from ADC   
    when 21  =>                          
             if (MUX_CHAN_CNT = 0) then
                 DATA_CH2(23-COUNT) <= DATA_FROM_AD;  -- Read next bit of ch 2 ADC 
             elsif (MUX_CHAN_CNT = 1) then   
                 DATA_CH0(23-COUNT) <= DATA_FROM_AD;  -- Read next bit of ch 0 ADC 
             elsif (MUX_CHAN_CNT = 2) then 
                 DATA_CH1(23-COUNT) <= DATA_FROM_AD;  -- Read next bit of ch 1 ADC 
             end if;  
         INC_CNT    <= '1';                  
    when 22  =>         
         SCLK       <= '1'; 
                         
    when 23  =>  
        CS_n        <= '1'; 
        if  MUX_CHAN_CNT = 2 then
          DATA_RDY_SYNC <= '1';
        end if;
                           
    when 24  =>  
        CS_n        <= '1';
                   
  end Case;
 
END PROCESS SPI_OUTPUT;




END rtl;
--AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa








