-- Copyright (c)2023 Jeremy Seth Henry
|
-- Copyright (c)2023 Jeremy Seth Henry
|
-- All rights reserved.
|
-- All rights reserved.
|
--
|
--
|
-- Redistribution and use in source and binary forms, with or without
|
-- Redistribution and use in source and binary forms, with or without
|
-- modification, are permitted provided that the following conditions are met:
|
-- modification, are permitted provided that the following conditions are met:
|
-- * Redistributions of source code must retain the above copyright
|
-- * Redistributions of source code must retain the above copyright
|
-- notice, this list of conditions and the following disclaimer.
|
-- notice, this list of conditions and the following disclaimer.
|
-- * Redistributions in binary form must reproduce the above copyright
|
-- * Redistributions in binary form must reproduce the above copyright
|
-- notice, this list of conditions and the following disclaimer in the
|
-- notice, this list of conditions and the following disclaimer in the
|
-- documentation and/or other materials provided with the distribution,
|
-- documentation and/or other materials provided with the distribution,
|
-- where applicable (as part of a user interface, debugging port, etc.)
|
-- where applicable (as part of a user interface, debugging port, etc.)
|
--
|
--
|
-- THIS SOFTWARE IS PROVIDED BY JEREMY SETH HENRY ``AS IS'' AND ANY
|
-- THIS SOFTWARE IS PROVIDED BY JEREMY SETH HENRY ``AS IS'' AND ANY
|
-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
-- DISCLAIMED. IN NO EVENT SHALL JEREMY SETH HENRY BE LIABLE FOR ANY
|
-- DISCLAIMED. IN NO EVENT SHALL JEREMY SETH HENRY BE LIABLE FOR ANY
|
-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
-- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
-- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
-- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
-- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
-- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
-- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
--
|
--
|
-- VHDL units : adc12s022
|
-- VHDL units : adc12s022
|
-- Description: Provides higher-level control of a single ADC128S022 12-bit ADC
|
-- Description: Provides higher-level control of a single ADC128S022 12-bit ADC
|
-- Note that the base part has a maximum Fsclk of 3.2MHz. Note that to simplify
|
-- Note that the base part has a maximum Fsclk of 3.2MHz. Note that to simplify
|
-- downstream logic, the data is expanded to a 16-bit bus.
|
-- downstream logic, the data is expanded to a 16-bit bus.
|
--
|
--
|
-- Revision History
|
-- Revision History
|
-- Author Date Change
|
-- Author Date Change
|
------------------ -------- ---------------------------------------------------
|
------------------ -------- ---------------------------------------------------
|
-- Seth Henry 05/18/23 Initial Upload
|
-- Seth Henry 05/18/23 Initial Upload
|
|
|
library ieee;
|
library ieee;
|
use ieee.std_logic_1164.all;
|
use ieee.std_logic_1164.all;
|
use ieee.std_logic_unsigned.all;
|
use ieee.std_logic_unsigned.all;
|
use ieee.std_logic_arith.all;
|
use ieee.std_logic_arith.all;
|
use ieee.std_logic_misc.all;
|
use ieee.std_logic_misc.all;
|
|
|
entity adc12s022 is
|
entity adc12s022 is
|
generic(
|
generic(
|
Clock_Frequency : real;
|
Clock_Frequency : real;
|
Reset_Level : std_logic := '1'
|
Reset_Level : std_logic := '1'
|
);
|
);
|
port(
|
port(
|
Clock : in std_logic;
|
Clock : in std_logic;
|
Reset : in std_logic;
|
Reset : in std_logic;
|
--
|
--
|
Reinit : in std_logic;
|
Reinit : in std_logic := '0'; -- Optional sync reset
|
--
|
--
|
RAW_Channel : out std_logic_vector(2 downto 0);
|
RAW_Channel : out std_logic_vector(2 downto 0);
|
RAW_Data : out std_logic_vector(15 downto 0);
|
RAW_Data : out std_logic_vector(15 downto 0);
|
RAW_Valid : out std_logic;
|
RAW_Valid : out std_logic;
|
--
|
--
|
Busy_In : in std_logic;
|
Busy_In : in std_logic;
|
--
|
--
|
SDO : in std_logic;
|
SDO : in std_logic;
|
SDI : out std_logic;
|
SDI : out std_logic;
|
SCLK : out std_logic;
|
SCLK : out std_logic;
|
CSn : out std_logic
|
CSn : out std_logic
|
);
|
);
|
end entity;
|
end entity;
|
|
|
architecture behave of adc12s022 is
|
architecture behave of adc12s022 is
|
|
|
-- The ceil_log2 function returns the minimum register width required to
|
-- The ceil_log2 function returns the minimum register width required to
|
-- hold the supplied integer.
|
-- hold the supplied integer.
|
function ceil_log2 (x : in natural) return natural is
|
function ceil_log2 (x : in natural) return natural is
|
variable retval : natural;
|
variable retval : natural;
|
begin
|
begin
|
retval := 1;
|
retval := 1;
|
while ((2**retval) - 1) < x loop
|
while ((2**retval) - 1) < x loop
|
retval := retval + 1;
|
retval := retval + 1;
|
end loop;
|
end loop;
|
return retval;
|
return retval;
|
end function;
|
end function;
|
|
|
-- Per the datasheet, the _022 part has a Fsmax of 3.2MHz, which results in
|
-- Per the datasheet, the _022 part has a Fsmax of 3.2MHz, which results in
|
-- a maximum single-channel conversion rate of 200ksps, or maximum
|
-- a maximum single-channel conversion rate of 200ksps, or maximum
|
-- multiplexed rate of 25ksps.
|
-- multiplexed rate of 25ksps.
|
constant ADS128S022_FSCLK : real := 3200000.0;
|
constant ADS128S022_FSCLK : real := 3200000.0;
|
|
|
constant Clock_Ratio : real :=
|
constant Clock_Ratio : real :=
|
((Clock_Frequency + (0.5*ADS128S022_FSCLK)) / ADS128S022_FSCLK);
|
((Clock_Frequency + (0.5*ADS128S022_FSCLK)) / ADS128S022_FSCLK);
|
|
|
constant Half_Period_Clks : integer := integer(Clock_Ratio * 0.5);
|
constant Half_Period_Clks : integer := integer(Clock_Ratio * 0.5);
|
|
|
type ADC_STATES is ( INIT, IDLE, REQ_SP, SP_WAIT, INC_CH );
|
type ADC_STATES is ( INIT, IDLE, REQ_SP, SP_WAIT, INC_CH );
|
signal ADC_State : ADC_STATES := INIT;
|
signal ADC_State : ADC_STATES := INIT;
|
|
|
signal Channel : std_logic_vector(2 downto 0) := "000";
|
signal Channel : std_logic_vector(2 downto 0) := "000";
|
signal Conv_Start : std_logic := '0';
|
signal Conv_Start : std_logic := '0';
|
|
|
signal Data_Out : std_logic_vector(11 downto 0) :=
|
signal Data_Out : std_logic_vector(11 downto 0) :=
|
(others => '0');
|
(others => '0');
|
signal Valid : std_logic := '0';
|
signal Valid : std_logic := '0';
|
|
|
constant Clk_Div_i : integer := Half_Period_Clks - 1;
|
constant Clk_Div_i : integer := Half_Period_Clks - 1;
|
constant Clk_Div_Bits : integer := ceil_log2(Clk_Div_i);
|
constant Clk_Div_Bits : integer := ceil_log2(Clk_Div_i);
|
constant CLK_DIV_VAL : std_logic_vector(Clk_Div_Bits - 1 downto 0) :=
|
constant CLK_DIV_VAL : std_logic_vector(Clk_Div_Bits - 1 downto 0) :=
|
conv_std_logic_vector(Clk_Div_i,Clk_Div_Bits);
|
conv_std_logic_vector(Clk_Div_i,Clk_Div_Bits);
|
signal HT_Cntr : std_logic_vector(Clk_Div_Bits - 1 downto 0);
|
signal HT_Cntr : std_logic_vector(Clk_Div_Bits - 1 downto 0);
|
signal HT_Tick : std_logic := '0';
|
signal HT_Tick : std_logic := '0';
|
|
|
type SPI_STATES is ( IDLE, ALIGN, CSn_START, CLK_SETUP, CLK_HOLD, CSn_END );
|
type SPI_STATES is ( IDLE, ALIGN, CSn_START, CLK_SETUP, CLK_HOLD, CSn_END );
|
signal spi_state : SPI_STATES;
|
signal spi_state : SPI_STATES;
|
|
|
signal spi_wr_buffer : std_logic_vector(15 downto 0) := x"0000";
|
signal spi_wr_buffer : std_logic_vector(15 downto 0) := x"0000";
|
signal spi_rd_buffer : std_logic_vector(15 downto 0) := x"0000";
|
signal spi_rd_buffer : std_logic_vector(15 downto 0) := x"0000";
|
|
|
signal bit_cntr : std_logic_vector(3 downto 0) := x"0";
|
signal bit_cntr : std_logic_vector(3 downto 0) := x"0";
|
|
|
begin
|
begin
|
|
|
ADC_Control_FSM_proc: process( Clock, Reset )
|
ADC_Control_FSM_proc: process( Clock, Reset )
|
begin
|
begin
|
if( Reset = Reset_Level )then
|
if( Reset = Reset_Level )then
|
ADC_State <= INIT;
|
ADC_State <= INIT;
|
Channel <= (others => '0');
|
Channel <= (others => '0');
|
Conv_Start <= '0';
|
Conv_Start <= '0';
|
|
|
RAW_Channel <= (others => '0');
|
RAW_Channel <= (others => '0');
|
RAW_Data <= (others => '0');
|
RAW_Data <= (others => '0');
|
RAW_Valid <= '0';
|
RAW_Valid <= '0';
|
|
|
elsif( rising_edge(Clock) )then
|
elsif( rising_edge(Clock) )then
|
Conv_Start <= '0';
|
Conv_Start <= '0';
|
|
|
RAW_Channel <= (others => '0');
|
RAW_Channel <= (others => '0');
|
RAW_Data <= (others => '0');
|
RAW_Data <= (others => '0');
|
RAW_Valid <= '0';
|
RAW_Valid <= '0';
|
|
|
case ADC_State is
|
case ADC_State is
|
when INIT =>
|
when INIT =>
|
Channel <= (others => '0');
|
Channel <= (others => '0');
|
ADC_State <= IDLE;
|
ADC_State <= IDLE;
|
|
|
when IDLE =>
|
when IDLE =>
|
if( Reinit = '1' )then
|
if( Reinit = '1' )then
|
ADC_State <= INIT;
|
ADC_State <= INIT;
|
elsif( Busy_In = '0' )then
|
elsif( Busy_In = '0' )then
|
ADC_State <= REQ_SP;
|
ADC_State <= REQ_SP;
|
end if;
|
end if;
|
|
|
when REQ_SP =>
|
when REQ_SP =>
|
Conv_Start <= '1';
|
Conv_Start <= '1';
|
ADC_State <= SP_WAIT;
|
ADC_State <= SP_WAIT;
|
|
|
when SP_WAIT =>
|
when SP_WAIT =>
|
if( Valid = '1' )then
|
if( Valid = '1' )then
|
RAW_Channel <= Channel;
|
RAW_Channel <= Channel;
|
RAW_Data <= "0000" & Data_Out;
|
RAW_Data <= "0000" & Data_Out;
|
RAW_Valid <= '1';
|
RAW_Valid <= '1';
|
ADC_State <= INC_CH;
|
ADC_State <= INC_CH;
|
end if;
|
end if;
|
|
|
when INC_CH =>
|
when INC_CH =>
|
Channel <= Channel + 1;
|
Channel <= Channel + 1;
|
ADC_State <= IDLE;
|
ADC_State <= IDLE;
|
|
|
when others =>
|
when others =>
|
null;
|
null;
|
end case;
|
end case;
|
end if;
|
end if;
|
end process;
|
end process;
|
|
|
|
|
SPI_IO_FSM: process( Clock, Reset )
|
SPI_IO_FSM: process( Clock, Reset )
|
begin
|
begin
|
if( Reset = Reset_Level )then
|
if( Reset = Reset_Level )then
|
spi_state <= IDLE;
|
spi_state <= IDLE;
|
spi_wr_buffer <= (others => '0');
|
spi_wr_buffer <= (others => '0');
|
spi_rd_buffer <= (others => '0');
|
spi_rd_buffer <= (others => '0');
|
bit_cntr <= (others => '0');
|
bit_cntr <= (others => '0');
|
|
|
|
|
HT_Cntr <= (others => '0');
|
HT_Cntr <= (others => '0');
|
HT_Tick <= '0';
|
HT_Tick <= '0';
|
|
|
SDI <= '0';
|
SDI <= '0';
|
SCLK <= '0';
|
SCLK <= '0';
|
CSn <= '1';
|
CSn <= '1';
|
|
|
Data_Out <= (others => '0');
|
Data_Out <= (others => '0');
|
Valid <= '0';
|
Valid <= '0';
|
elsif( rising_edge(Clock) )then
|
elsif( rising_edge(Clock) )then
|
|
|
HT_Cntr <= HT_Cntr - 1;
|
HT_Cntr <= HT_Cntr - 1;
|
HT_Tick <= '0';
|
HT_Tick <= '0';
|
if( HT_Cntr = 0 )then
|
if( HT_Cntr = 0 )then
|
HT_Cntr <= CLK_DIV_VAL;
|
HT_Cntr <= CLK_DIV_VAL;
|
HT_Tick <= '1';
|
HT_Tick <= '1';
|
end if;
|
end if;
|
|
|
SCLK <= '1';
|
SCLK <= '1';
|
SDI <= '1';
|
SDI <= '1';
|
|
|
Valid <= '0';
|
Valid <= '0';
|
|
|
case( spi_state )is
|
case( spi_state )is
|
when IDLE =>
|
when IDLE =>
|
CSn <= '1';
|
CSn <= '1';
|
bit_cntr <= x"F";
|
bit_cntr <= x"F";
|
if( Conv_Start = '1' )then
|
if( Conv_Start = '1' )then
|
spi_wr_buffer <= "00" & Channel & "00000000000";
|
spi_wr_buffer <= "00" & Channel & "00000000000";
|
spi_state <= ALIGN;
|
spi_state <= ALIGN;
|
end if;
|
end if;
|
|
|
when ALIGN =>
|
when ALIGN =>
|
if( HT_Tick = '1' )then
|
if( HT_Tick = '1' )then
|
spi_state <= CSn_START;
|
spi_state <= CSn_START;
|
end if;
|
end if;
|
|
|
when CSn_START =>
|
when CSn_START =>
|
CSn <= '0';
|
CSn <= '0';
|
if( HT_Tick = '1' )then
|
if( HT_Tick = '1' )then
|
spi_state <= CLK_SETUP;
|
spi_state <= CLK_SETUP;
|
end if;
|
end if;
|
|
|
when CLK_SETUP =>
|
when CLK_SETUP =>
|
SCLK <= '0';
|
SCLK <= '0';
|
SDI <= spi_wr_buffer(conv_integer(bit_cntr));
|
SDI <= spi_wr_buffer(conv_integer(bit_cntr));
|
CSn <= '0';
|
CSn <= '0';
|
if( HT_Tick = '1' )then
|
if( HT_Tick = '1' )then
|
spi_rd_buffer <= spi_rd_buffer(14 downto 0) & SDO;
|
spi_rd_buffer <= spi_rd_buffer(14 downto 0) & SDO;
|
spi_state <= CLK_HOLD;
|
spi_state <= CLK_HOLD;
|
end if;
|
end if;
|
|
|
when CLK_HOLD =>
|
when CLK_HOLD =>
|
SDI <= spi_wr_buffer(conv_integer(bit_cntr));
|
SDI <= spi_wr_buffer(conv_integer(bit_cntr));
|
if( HT_Tick = '1' )then
|
if( HT_Tick = '1' )then
|
bit_cntr <= bit_cntr - 1;
|
bit_cntr <= bit_cntr - 1;
|
spi_state <= CLK_SETUP;
|
spi_state <= CLK_SETUP;
|
if( bit_cntr = 0 )then
|
if( bit_cntr = 0 )then
|
spi_state <= CSn_END;
|
spi_state <= CSn_END;
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
when CSn_END =>
|
when CSn_END =>
|
if( HT_Tick = '1' )then
|
if( HT_Tick = '1' )then
|
Data_Out <= spi_rd_buffer(11 downto 0);
|
Data_Out <= spi_rd_buffer(11 downto 0);
|
Valid <= '1';
|
Valid <= '1';
|
spi_state <= IDLE;
|
spi_state <= IDLE;
|
end if;
|
end if;
|
|
|
when others =>
|
when others =>
|
null;
|
null;
|
end case;
|
end case;
|
|
|
end if;
|
end if;
|
end process;
|
end process;
|
|
|
|
|