Thank you for help!
Following are the VHDL code of the FPGA machine and the test bench. Sorry if it looks like a mess. It is our first try to use FPGA. We have not much experience in VHDL, Quartus, and ModelSim. The SignalTap II sounds useful, we will learn it.
The state machine : adc01.vhd
----
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
entity adc01 is
port (
addr : in std_logic_vector (6 downto 0); -- address bus, 7-bit
data : inout std_logic_vector (12 downto 0) := (others => 'Z'); -- data bus, 13-bit, init to 'Z' or fail to feed data in modelsim
rd : in std_logic; -- read me, active low
wr : in std_logic; -- write me, active low
bs : in std_logic; -- board select, active low
--
hwst : in std_logic; -- hardware start command
--
convst : out std_logic := '0'; -- conversion start
-- stby : out std_logic := '1'; -- standby mode, active low
reset : out std_logic := '0'; -- reset ADCs, active high,pulse width > 50 ns
sclk : out std_logic := '1'; -- SPI SCLK
fs : out std_logic := '1'; -- SPI SS, active low, ADCs' frame synchronization
miso : in std_logic_vector (63 downto 0); -- SPI MISO
busy : in std_logic; -- DAC busy, active high
--
clk : in std_logic -- system clock
);
end adc01;
architecture behavior of adc01 is
-- Timing -- Symbols refer to ADS85x8 datasheet (oct 2011) page 9.
-- XXX - The timing values are set according system clock period 40 ns (25 MHz)
constant T_RESET : integer := 2; -- reset pulse width > 50 ns
-- constant T_TDCVB : integer := 15; -- CONVST_x high to BUSY delay, 25 ns max
-- XXX - Caution : The tDCVB will be 400+ ns if the ASLEEP pin is pulled high!
-- XXX - If the period of system clock (clk) is short than TDMSB (12 ns) or TPPDO (17 ns),
-- XXX - or the 1/2 SCLK period 23/2 ns, then T_TPPDO > 0.
-- XXX - The serial clock period must > 22 ns, and is obtained from 2 times of system clock.
-- XXX - TDMSB : FS low to MSB valid delay, 12 ns max
-- XXX - TPDDO : SCLK falling edge to new data valid propagation delay, 17 ns max
-- constant T_TPPDO : integer := 2;
-- Data bits --
constant SPI_BITS : integer := 32; -- serial data bits
-- Timing counters --
signal cnt_reset : std_logic_vector (1 downto 0) := (others => '0'); -- Reset pulse width
-- signal cnt_tdcvb : std_logic_vector (3 downto 0) := (others => '0'); -- TDCVB timing
-- signal cnt_tppdo : std_logic_vector (1 downto 0) := (others => '0'); -- TPPDO timing
-- Data bits counters --
signal cnt_spibits : std_logic_vector (5 downto 0) := (others => '0'); -- SPI data 32 bits
-- This bit indicates the busy status
-- 0 : busy, is converting signals and collecting data
-- 1 : data are available for read
signal data_readability : std_logic := '1';
constant STATUSBIT_DRABLTY : integer := 12; -- bit 12, data readability
-- constant ADDR_CMD : integer := 0; -- The command register
-- Command --
constant CMD_CNVST : integer := 1; -- Start conversion
constant CMD_RESET : integer := 8191; -- Reset ADCs, for 13-bit data bus
-- State machine --
type state_type is (
st_idle,
st_tdcvb, st_busy, st_spi_read_bit, st_spi_clear,
st_reset
);
-- XXX - If the state machine is not stable for one-hot mode,try enum it
-- attribute ENUM_ENCODING : string;
-- attribute ENUM_ENCODING of state_type : type is "000 001 010 011 100 101";
signal state : state_type := st_idle;
signal wr_last : std_logic := '1'; -- value of the wr signal in the last clock state
signal hwst_last : std_logic := '1'; -- value of the hwst signal in the last clock state
-- Internal buffer --
type memory_type is array (0 to 63) of std_logic_vector (SPI_BITS - 1 downto 0);
signal memory : memory_type;
signal clk_reg : std_logic := '1'; -- Regulated system clock
signal sclk_enable : std_logic := '0';
signal addr_reg : std_logic_vector (6 downto 0);
signal bs_reg : std_logic := '1';
signal rd_reg : std_logic := '1';
signal wr_reg : std_logic := '1';
signal hwst_reg : std_logic := '1';
begin
-- System Clock Regulating
system_clock : process (clk)
begin
clk_reg <= not clk;
end process system_clock;
-- SPI Clock
spi_clock: process (clk_reg, sclk_enable)
begin
if (sclk_enable = '0') then
sclk <= '1';
else
sclk <= clk_reg;
end if;
end process spi_clock;
-- Synchronize signals
synch_signals : process (clk_reg)
begin
if (clk_reg'event and clk_reg = '1') then
addr_reg <= addr;
bs_reg <= bs;
rd_reg <= rd;
wr_reg <= wr;
hwst_reg <= hwst;
end if;
end process synch_signals;
-- Read
state_read: process (bs_reg, rd_reg)
variable var_index : integer;
variable var_data : std_logic_vector (12 downto 0);
begin
if (bs_reg = '0' and state = st_idle) then
if (wr_reg = '1') then -- no writing
if (rd_reg = '0') then -- start of read cycle
var_index := TO_INTEGER(UNSIGNED(addr_reg) srl 1);
if addr_reg(0) = '0' then
var_data := memory(var_index)(28 downto 16);
else
var_data := memory(var_index)(12 downto 0);
end if;
--if TO_INTEGER(UNSIGNED(addr_reg)) = ADDR_STATUS then
-- data <= (STATUSBIT_DRABLTY => data_readability, others => '0');
var_data (STATUSBIT_DRABLTY) := data_readability;
--end if;
data <= var_data;
elsif (rd_reg = '1') then -- end of read cycle
data <= (others => 'Z');
end if;
end if;
end if;
end process state_read;
-- Clocked state machine
state_clocked: process (clk_reg)
variable command : integer;
begin
if (clk_reg'event and clk_reg = '1') then
wr_last <= wr_reg; -- this will happen in next cycle
hwst_last <= hwst_reg;
if (bs_reg = '0' and wr_reg = '0' and wr_last = '1') then
command := TO_INTEGER(UNSIGNED(data(12 downto 0)));
case command is
when CMD_CNVST =>
if (state = st_idle) then
convst <= '1';
data_readability <= '0';
-- cnt_tdcvb <= cnt_tdcvb + 1;
state <= st_tdcvb;
end if;
when CMD_RESET =>
-- data <= (others => 'Z');
convst <= '0';
-- stby <= '1';
fs <= '1';
sclk_enable <= '0';
cnt_spibits <= (others => '0');
-- cnt_tdcvb <= (others => '0');
cnt_reset <= (others => '0');
reset <= '1';
cnt_reset <= cnt_reset + 1;
state <= st_reset;
when others =>
-- do nothing --
end case;
else
case state is
when st_idle =>
-- do nothing but hardware start --
if (hwst_reg = '0' and hwst_last = '1') then
convst <= '1';
data_readability <= '0';
state <= st_tdcvb;
end if;
when st_tdcvb =>
-- if (TO_INTEGER(UNSIGNED(cnt_tdcvb)) < T_TDCVB) then
-- cnt_tdcvb <= cnt_tdcvb + 1;
-- else
-- cnt_tdcvb <= (others => '0');
convst <= '0';
state <= st_busy;
-- end if;
when st_busy =>
if (busy = '0') then
fs <= '0';
-- XXX - The system clock period is longer than TDMSB (12 ns)
-- XXX - so we can go to st_spi_read directly.
state <= st_spi_read_bit;
end if;
when st_spi_read_bit =>
-- shift data in --
for i in 0 to miso'length - 1 loop
memory(i) <= memory(i)(SPI_BITS - 2 downto 0) & miso(i);
end loop;
if (TO_INTEGER(UNSIGNED(cnt_spibits)) = SPI_BITS - 1) then
fs <= '1';
sclk_enable <= '0';
data_readability <= '1';
cnt_spibits <= (others => '0');
state <= st_idle;
else
cnt_spibits <= cnt_spibits + 1;
if (TO_INTEGER(UNSIGNED(cnt_spibits)) = 0) then
sclk_enable <= '1';
end if;
end if;
when st_reset =>
if (TO_INTEGER(UNSIGNED(cnt_reset)) < T_RESET) then
cnt_reset <= cnt_reset + 1;
else
reset <= '0';
cnt_reset <= (others => '0');
state <= st_idle;
wr_last <= '1';
end if;
when others =>
state <= st_idle;
end case;
end if;
end if; -- clk_reg'event
end process state_clocked;
end behavior;
----
End of adc01.vhd