I am trying to implement my pmod mic3 on Basys3 board. My code seems true but I couldn't read any data.
I mentioned my code in below.
There are 3 modules: SPI_master, SPI_master_withCS, mic3
SPI_Master.vhd
------------------------------------------------------------------------------/
-- Description: SPI (Serial Peripheral Interface) Master
-- Creates master based on input configuration.
-- Sends a byte one bit at a time on MOSI
-- Will also receive byte data one bit at a time on MISO.
-- Any data on input byte will be shipped out on MOSI.
--
-- To kick-off transaction, user must pulse i_TX_DV.
-- This module supports multi-byte transmissions by pulsing
-- i_TX_DV and loading up i_TX_Byte when o_TX_Ready is high.
--
-- This module is only responsible for controlling Clk, MOSI,
-- and MISO. If the SPI peripheral requires a chip-select,
-- this must be done at a higher level.
--
-- Note: i_Clk must be at least 2x faster than i_SPI_Clk
--
-- Generics: SPI_MODE, can be 0, 1, 2, or 3. See above.
-- Can be configured in one of 4 modes:
-- Mode | Clock Polarity (CPOL/CKP) | Clock Phase (CPHA)
-- 0 | 0 | 0
-- 1 | 0 | 1
-- 2 | 1 | 0
-- 3 | 1 | 1
-- More: https:--en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Mode_numbers
-- CLKS_PER_HALF_BIT - Sets frequency of o_SPI_Clk. o_SPI_Clk is
-- derived from i_Clk. Set to integer number of clocks for each
-- half-bit of SPI data. E.g. 100 MHz i_Clk, CLKS_PER_HALF_BIT = 2
-- would create o_SPI_CLK of 25 MHz. Must be >= 2
-- tsclk = 20MHz
-- fpga clock =100MHz
------------------------------------------------------------------------------/
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity SPI_Master is
generic (
SPI_MODE : integer := 2;
CLKS_PER_HALF_BIT : integer := 5
);
port (
-- Control/Data Signals,
i_Rst_L : in std_logic; -- FPGA Reset
i_Clk : in std_logic; -- FPGA Clock
-- TX (MOSI) Signals
i_TX_Byte : in std_logic_vector(11 downto 0); -- Byte to transmit on MOSI
i_TX_DV : in std_logic; -- Data Valid Pulse with i_TX_Byte
o_TX_Ready : inout std_logic; -- Transmit Ready for next byte
-- RX (MISO) Signals
o_RX_DV : out std_logic; -- Data Valid pulse (1 clock cycle)
o_RX_Byte : out std_logic_vector(11 downto 0); -- Byte received on MISO
-- SPI Interface
o_SPI_Clk : out std_logic;
i_SPI_MISO : in std_logic;
o_SPI_MOSI : out std_logic
);
end entity SPI_Master;
architecture RTL of SPI_Master is
-- SPI Interface (All Runs at SPI Clock Domain)
signal w_CPOL : std_logic; -- Clock polarity
signal w_CPHA : std_logic; -- Clock phase
signal r_SPI_Clk_Count : integer range 0 to CLKS_PER_HALF_BIT*2-1;
signal r_SPI_Clk : std_logic;
signal r_SPI_Clk_Edges : integer range 0 to 16;
signal r_Leading_Edge : std_logic;
signal r_Trailing_Edge : std_logic;
signal r_TX_DV : std_logic;
signal r_TX_Byte : std_logic_vector(11 downto 0);
signal r_RX_Bit_Count : unsigned(2 downto 0);
signal r_TX_Bit_Count : unsigned(2 downto 0);
begin
-- CPOL: Clock Polarity
-- CPOL=0 means clock idles at 0, leading edge is rising edge.
-- CPOL=1 means clock idles at 1, leading edge is falling edge.
w_CPOL <= '1' when (SPI_MODE = 2) or (SPI_MODE = 3) else '0';
-- CPHA: Clock Phase
-- CPHA=0 means the "out" side changes the data on trailing edge of clock
-- the "in" side captures data on leading edge of clock
-- CPHA=1 means the "out" side changes the data on leading edge of clock
-- the "in" side captures data on the trailing edge of clock
w_CPHA <= '1' when (SPI_MODE = 1) or (SPI_MODE = 3) else '0';
-- Purpose: Generate SPI Clock correct number of times when DV pulse comes
Edge_Indicator : process (i_Clk, i_Rst_L)
begin
if i_Rst_L = '0' then
o_TX_Ready <= '0';
r_SPI_Clk_Edges <= 0;
r_Leading_Edge <= '0';
r_Trailing_Edge <= '0';
r_SPI_Clk <= w_CPOL; -- assign default state to idle state
r_SPI_Clk_Count <= 0;
elsif falling_edge(i_Clk) then
if i_TX_DV = '1' then
o_TX_Ready <= '0';
r_SPI_Clk_Edges <= 16; -- Total # edges in one byte ALWAYS 16
elsif r_SPI_Clk_Edges > 0 then
o_TX_Ready <= '0';
if r_SPI_Clk_Count = CLKS_PER_HALF_BIT*2-1 then
r_SPI_Clk_Edges <= r_SPI_Clk_Edges - 1;
r_Trailing_Edge <= '1';
r_SPI_Clk_Count <= 0;
r_SPI_Clk <= not r_SPI_Clk;
elsif r_SPI_Clk_Count = CLKS_PER_HALF_BIT-1 then
r_SPI_Clk_Edges <= r_SPI_Clk_Edges - 1;
r_Leading_Edge <= '1';
r_SPI_Clk_Count <= r_SPI_Clk_Count + 1;
r_SPI_Clk <= not r_SPI_Clk;
else
r_SPI_Clk_Count <= r_SPI_Clk_Count + 1;
end if;
else
o_TX_Ready <= '1';
end if;
end if;
end process Edge_Indicator;
-- Purpose: Register i_TX_Byte when Data Valid is pulsed.
-- Keeps local storage of byte in case higher level module changes the data
Byte_Reg : process (i_Clk, i_Rst_L)
begin
if i_Rst_L = '0' then
r_TX_Byte <= X"000";
r_TX_DV <= '0';
elsif falling_edge(i_clk) then
r_TX_DV <= i_TX_DV; -- 1 clock cycle delay
if i_TX_DV = '1' then
r_TX_Byte <= i_TX_Byte;
end if;
end if;
end process Byte_Reg;
-- Purpose: Generate MOSI data
-- Works with both CPHA=0 and CPHA=1
MOSI_Data : process (i_Clk, i_Rst_L)
begin
if i_Rst_L = '0' then
o_SPI_MOSI <= '0';
r_TX_Bit_Count <= "111"; -- Send MSB first
elsif falling_edge(i_Clk) then
-- If ready is high, reset bit counts to default
if o_TX_Ready = '1' then
r_TX_Bit_Count <= "111";
-- Catch the case where we start transaction and CPHA = 0
elsif (r_TX_DV = '1' and w_CPHA = '0') then
o_SPI_MOSI <= r_TX_Byte(7);
r_TX_Bit_Count <= "110"; -- 6
elsif (r_Leading_Edge = '1' and w_CPHA = '1') or (r_Trailing_Edge = '1' and w_CPHA = '0') then
r_TX_Bit_Count <= r_TX_Bit_Count - 1;
o_SPI_MOSI <= r_TX_Byte(to_integer(r_TX_Bit_Count));
end if;
end if;
end process MOSI_Data;
-- Purpose: Read in MISO data.
MISO_Data : process (i_Clk, i_Rst_L)
begin
if i_Rst_L = '0' then
o_RX_Byte <= X"000";
o_RX_DV <= '0';
r_RX_Bit_Count <= "111"; -- Starts at 7
elsif falling_edge(i_Clk) then
-- Default Assignments
o_RX_DV <= '0';
if o_TX_Ready = '1' then -- Check if ready, if so reset count to default
r_RX_Bit_Count <= "111"; -- Starts at 7
elsif (r_Leading_Edge = '1' and w_CPHA = '0') or (r_Trailing_Edge = '1' and w_CPHA = '1') then
o_RX_Byte(to_integer(r_RX_Bit_Count)) <= i_SPI_MISO; -- Sample data
r_RX_Bit_Count <= r_RX_Bit_Count - 1;
if r_RX_Bit_Count = "000" then
o_RX_DV <= '1'; -- Byte done, pulse Data Valid
end if;
end if;
end if;
end process MISO_Data;
-- Purpose: Add clock delay to signals for alignment.
SPI_Clock : process (i_Clk, i_Rst_L)
begin
if i_Rst_L = '0' then
o_SPI_Clk <= w_CPOL;
elsif falling_edge(i_Clk) then
o_SPI_Clk <= r_SPI_Clk;
end if;
end process SPI_Clock;
end architecture RTL;
SPI_master_withCS.vhd
-------------------------------------------------------------------------------
-- Description: SPI (Serial Peripheral Interface) Master
-- With single chip-select (AKA Slave Select) capability
--
-- Supports arbitrary length byte transfers.
--
-- Instantiates a SPI Master and adds single CS.
-- If multiple CS signals are needed, will need to use different
-- module, OR multiplex the CS from this at a higher level.
--
-- Note: i_Clk must be at least 2x faster than i_SPI_Clk
--
-- Parameters: SPI_MODE, can be 0, 1, 2, or 3. See above.
-- Can be configured in one of 4 modes:
-- Mode | Clock Polarity (CPOL/CKP) | Clock Phase (CPHA)
-- 0 | 0 | 0
-- 1 | 0 | 1
-- 2 | 1 | 0
-- 3 | 1 | 1
--
-- CLKS_PER_HALF_BIT - Sets frequency of o_SPI_Clk. o_SPI_Clk is
-- derived from i_Clk. Set to integer number of clocks for each
-- half-bit of SPI data. E.g. 100 MHz i_Clk, CLKS_PER_HALF_BIT = 2
-- would create o_SPI_CLK of 25 MHz. Must be >= 2
--
-- MAX_BYTES_PER_CS - Set to the maximum number of bytes that
-- will be sent during a single CS-low pulse.
--
-- CS_INACTIVE_CLKS - Sets the amount of time in clock cycles to
-- hold the state of Chip-Selct high (inactive) before next
-- command is allowed on the line. Useful if chip requires some
-- time when CS is high between trasnfers.
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- TX (MOSI) Signals
i_TX_Count : in std_logic_vector; -- # bytes per CS low
i_TX_Byte : in std_logic_vector(11 downto 0); -- Byte to transmit on MOSI
i_TX_DV : in std_logic; -- Data Valid Pulse with i_TX_Byte
o_TX_Ready : out std_logic; -- Transmit Ready for next byte
-- RX (MISO) Signals
o_RX_Count : inout std_logic_vector(11 downto 0); -- Index RX byte
o_RX_DV : inout std_logic; -- Data Valid pulse (1 clock cycle)
o_RX_Byte : out std_logic_vector(11 downto 0); -- Byte received on MISO
-- SPI Interface
o_SPI_Clk : out std_logic;
i_SPI_MISO : in std_logic;
o_SPI_CS_n : out std_logic
);
end entity spi_master_cs;
architecture RTL of spi_master_cs is
type t_SM_CS is (IDLE, TRANSFER, CS_INACTIVE);
signal r_SM_CS : t_SM_CS;
signal r_CS_n : std_logic;
signal r_CS_Inactive_Count : integer range 0 to CS_INACTIVE_CLKS;
signal r_TX_Count : integer range 0 to MAX_BYTES_PER_CS + 1;
signal w_Master_Ready : std_logic;
begin
-- Instantiate Master
SPI_Master_1 : entity work.spi_master
generic map (
SPI_MODE => SPI_MODE,
CLKS_PER_HALF_BIT => CLKS_PER_HALF_BIT)
port map (
-- Control/Data Signals,
i_Rst_L => i_Rst_L, -- FPGA Reset
i_Clk => i_Clk, -- FPGA Clock
-- TX (MOSI) Signals
i_TX_Byte => i_TX_Byte, -- Byte to transmit
i_TX_DV => i_TX_DV, -- Data Valid pulse
o_TX_Ready => w_Master_Ready, -- Transmit Ready for Byte
-- RX (MISO) Signals
o_RX_DV => o_RX_DV, -- Data Valid pulse
o_RX_Byte => o_RX_Byte, -- Byte received on MISO
-- SPI Interface
o_SPI_Clk => o_SPI_Clk,
i_SPI_MISO => i_SPI_MISO
);
-- Purpose: Control CS line using State Machine
SM_CS :process(i_Clk, i_Rst_L)
begin
if i_Rst_L = '0' then
r_SM_CS <= IDLE;
r_CS_n <= '1'; -- Resets to high
r_TX_Count <= 0;
r_CS_Inactive_Count <= CS_INACTIVE_CLKS;
elsif falling_edge(i_Clk) then
case r_SM_CS is
when IDLE =>
if r_CS_n = '1' and i_TX_DV = '1' then -- Start of transmission
r_TX_Count <= to_integer(unsigned(i_TX_Count) - 1); -- Register TX Count
r_CS_n <= '0'; -- Drive CS low
r_SM_CS <= TRANSFER; -- Transfer bytes
end if;
when TRANSFER =>
-- Wait until SPI is done transferring do next thing
if w_Master_Ready = '1' then
if r_TX_Count > 0 then
if i_TX_DV = '1' then
r_TX_Count <= r_TX_Count - 1;
end if;
else
r_CS_n <= '1'; -- we done, so set CS high
r_CS_Inactive_Count <= CS_INACTIVE_CLKS;
r_SM_CS <= CS_INACTIVE;
end if;
end if;
when CS_INACTIVE =>
if r_CS_Inactive_Count > 0 then
r_CS_Inactive_Count <= r_CS_Inactive_Count - 1;
else
r_SM_CS <= IDLE;
end if;
when others =>
r_CS_n <= '1'; -- we done, so set CS high
r_SM_CS <= IDLE;
end case;
end if;
end process SM_CS;
-- Purpose: Keep track of RX_Count
RX_COUNT : process (i_Clk)
begin
if falling_edge(i_Clk) then
if r_CS_n = '1' then
o_RX_Count <= std_logic_vector(to_unsigned(0, o_RX_Count'length));
elsif o_RX_DV = '1' then
o_RX_Count <= std_logic_vector(unsigned(o_RX_Count) + 1);
end if;
end if;
end process RX_COUNT;
o_SPI_CS_n <= r_CS_n;
o_TX_Ready <= '1' when i_TX_DV /= '1' and ((r_SM_CS = IDLE) or (r_SM_CS = TRANSFER and w_Master_Ready = '1' and r_TX_Count > 0)) else '0';
end architecture RTL;
mic3.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity mic3 is
port (
-- Control/Data Signals,
i_Switch_1 : in std_logic; -- Used as FPGA Reset
i_Clk : in std_logic; -- FPGA Clock
-- PMOD SPI Interface
o_SPI_Clk : out std_logic;
i_SPI_MISO : in std_logic;
o_SPI_CS_n : out std_logic;
-- Output
r_ADC_Word : out std_logic_vector(11 downto 0)
);
end entity mic3;
architecture RTL of mic3 is
-- CPOL = 1 and CPHA = 1, based on ADC part ADC08S021
constant SPI_MODE : integer := 2;
-- Go Board operates at 25 MHz, so divide by 10 to get to 2.5 MHz.
-- This is clocks per half bit, so divide 10 by 2 to get 5.
constant CLKS_PER_HALF_BIT : integer := 5;
-- Number of clock cycles to leave CS high after transaction is done
constant CS_INACTIVE_CLKS : integer := 100;
signal w_Rst_L : std_logic;
-- SPI Signals
signal w_Master_RX_Count : std_logic_vector(11 downto 0);
signal w_Master_RX_DV : std_logic;
signal w_Master_RX_Byte : std_logic_vector(11 downto 0);
signal w_Master_TX_Ready : std_logic;
signal r_Master_TX_DV : std_logic;
signal w_LED_Enable : std_logic;
signal w_Ambient_Val : std_logic_vector(11 downto 0);
begin -- architecture RTL
w_Rst_L <= not i_Switch_1; -- switch defaults to low.
SPI_Master_With_Single_CS_1: entity work.spi_master_cs
generic map (
SPI_MODE => SPI_MODE,
CLKS_PER_HALF_BIT => CLKS_PER_HALF_BIT,
MAX_BYTES_PER_CS => 2, -- always 2 bytes per CS
CS_INACTIVE_CLKS => CS_INACTIVE_CLKS)
port map (
i_Rst_L => w_Rst_L, -- FPGA Reset (push button)
i_Clk => i_Clk, -- FPGA Clock
-- TX (MOSI Signals)
i_TX_Count => x"10", -- Always 2 bytes per CS
i_TX_Byte => x"000", -- Can stuff with zeros
i_TX_DV => r_Master_TX_DV, -- Data valid pulse with i_TX_Byte
o_TX_Ready => w_Master_TX_Ready, -- Transmit ready for byte
-- RX (MISO) Signals
o_RX_Count => w_Master_RX_Count, -- Index of RX'd byte
o_RX_DV => w_Master_RX_DV, -- Data valid pulse (1 clock cycle)
o_RX_Byte => w_Master_RX_Byte, -- Byte received on MISO
-- SPI Interface
o_SPI_Clk => o_SPI_Clk,
i_SPI_MISO => i_SPI_MISO,
o_SPI_CS_n => o_SPI_CS_n
);
-- Purpose: Handle read requests from ADC.
-- Just request data as often as possible
-- Also creates simple counter to count continuously.
process (i_Clk) is
begin
if falling_edge(i_Clk) then
r_Master_TX_DV <= w_Master_TX_Ready;
end if;
end process;
-- Purpose: Handle data being read back from SPI
-- Pack up response into a single 8-bit value for ambient light.
process (i_Clk) is
begin
if falling_edge(i_Clk) then
if w_Master_RX_DV = '1' then
if w_Master_RX_Count = "00" then
r_ADC_Word <= w_Master_RX_Byte;
end if;
end if;
end if;
end process;
Question
Abdullah Ozum Duzgun
I am trying to implement my pmod mic3 on Basys3 board. My code seems true but I couldn't read any data.
I mentioned my code in below.
There are 3 modules: SPI_master, SPI_master_withCS, mic3
SPI_Master.vhd
------------------------------------------------------------------------------/
-- Description: SPI (Serial Peripheral Interface) Master
-- Creates master based on input configuration.
-- Sends a byte one bit at a time on MOSI
-- Will also receive byte data one bit at a time on MISO.
-- Any data on input byte will be shipped out on MOSI.
--
-- To kick-off transaction, user must pulse i_TX_DV.
-- This module supports multi-byte transmissions by pulsing
-- i_TX_DV and loading up i_TX_Byte when o_TX_Ready is high.
--
-- This module is only responsible for controlling Clk, MOSI,
-- and MISO. If the SPI peripheral requires a chip-select,
-- this must be done at a higher level.
--
-- Note: i_Clk must be at least 2x faster than i_SPI_Clk
--
-- Generics: SPI_MODE, can be 0, 1, 2, or 3. See above.
-- Can be configured in one of 4 modes:
-- Mode | Clock Polarity (CPOL/CKP) | Clock Phase (CPHA)
-- 0 | 0 | 0
-- 1 | 0 | 1
-- 2 | 1 | 0
-- 3 | 1 | 1
-- More: https:--en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Mode_numbers
-- CLKS_PER_HALF_BIT - Sets frequency of o_SPI_Clk. o_SPI_Clk is
-- derived from i_Clk. Set to integer number of clocks for each
-- half-bit of SPI data. E.g. 100 MHz i_Clk, CLKS_PER_HALF_BIT = 2
-- would create o_SPI_CLK of 25 MHz. Must be >= 2
-- tsclk = 20MHz
-- fpga clock =100MHz
------------------------------------------------------------------------------/
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity SPI_Master is
generic (
SPI_MODE : integer := 2;
CLKS_PER_HALF_BIT : integer := 5
);
port (
-- Control/Data Signals,
i_Rst_L : in std_logic; -- FPGA Reset
i_Clk : in std_logic; -- FPGA Clock
-- TX (MOSI) Signals
i_TX_Byte : in std_logic_vector(11 downto 0); -- Byte to transmit on MOSI
i_TX_DV : in std_logic; -- Data Valid Pulse with i_TX_Byte
o_TX_Ready : inout std_logic; -- Transmit Ready for next byte
-- RX (MISO) Signals
o_RX_DV : out std_logic; -- Data Valid pulse (1 clock cycle)
o_RX_Byte : out std_logic_vector(11 downto 0); -- Byte received on MISO
-- SPI Interface
o_SPI_Clk : out std_logic;
i_SPI_MISO : in std_logic;
o_SPI_MOSI : out std_logic
);
end entity SPI_Master;
architecture RTL of SPI_Master is
-- SPI Interface (All Runs at SPI Clock Domain)
signal w_CPOL : std_logic; -- Clock polarity
signal w_CPHA : std_logic; -- Clock phase
signal r_SPI_Clk_Count : integer range 0 to CLKS_PER_HALF_BIT*2-1;
signal r_SPI_Clk : std_logic;
signal r_SPI_Clk_Edges : integer range 0 to 16;
signal r_Leading_Edge : std_logic;
signal r_Trailing_Edge : std_logic;
signal r_TX_DV : std_logic;
signal r_TX_Byte : std_logic_vector(11 downto 0);
signal r_RX_Bit_Count : unsigned(2 downto 0);
signal r_TX_Bit_Count : unsigned(2 downto 0);
begin
-- CPOL: Clock Polarity
-- CPOL=0 means clock idles at 0, leading edge is rising edge.
-- CPOL=1 means clock idles at 1, leading edge is falling edge.
w_CPOL <= '1' when (SPI_MODE = 2) or (SPI_MODE = 3) else '0';
-- CPHA: Clock Phase
-- CPHA=0 means the "out" side changes the data on trailing edge of clock
-- the "in" side captures data on leading edge of clock
-- CPHA=1 means the "out" side changes the data on leading edge of clock
-- the "in" side captures data on the trailing edge of clock
w_CPHA <= '1' when (SPI_MODE = 1) or (SPI_MODE = 3) else '0';
-- Purpose: Generate SPI Clock correct number of times when DV pulse comes
Edge_Indicator : process (i_Clk, i_Rst_L)
begin
if i_Rst_L = '0' then
o_TX_Ready <= '0';
r_SPI_Clk_Edges <= 0;
r_Leading_Edge <= '0';
r_Trailing_Edge <= '0';
r_SPI_Clk <= w_CPOL; -- assign default state to idle state
r_SPI_Clk_Count <= 0;
elsif falling_edge(i_Clk) then
-- Default assignments
r_Leading_Edge <= '0';
r_Trailing_Edge <= '0';
if i_TX_DV = '1' then
o_TX_Ready <= '0';
r_SPI_Clk_Edges <= 16; -- Total # edges in one byte ALWAYS 16
elsif r_SPI_Clk_Edges > 0 then
o_TX_Ready <= '0';
if r_SPI_Clk_Count = CLKS_PER_HALF_BIT*2-1 then
r_SPI_Clk_Edges <= r_SPI_Clk_Edges - 1;
r_Trailing_Edge <= '1';
r_SPI_Clk_Count <= 0;
r_SPI_Clk <= not r_SPI_Clk;
elsif r_SPI_Clk_Count = CLKS_PER_HALF_BIT-1 then
r_SPI_Clk_Edges <= r_SPI_Clk_Edges - 1;
r_Leading_Edge <= '1';
r_SPI_Clk_Count <= r_SPI_Clk_Count + 1;
r_SPI_Clk <= not r_SPI_Clk;
else
r_SPI_Clk_Count <= r_SPI_Clk_Count + 1;
end if;
else
o_TX_Ready <= '1';
end if;
end if;
end process Edge_Indicator;
-- Purpose: Register i_TX_Byte when Data Valid is pulsed.
-- Keeps local storage of byte in case higher level module changes the data
Byte_Reg : process (i_Clk, i_Rst_L)
begin
if i_Rst_L = '0' then
r_TX_Byte <= X"000";
r_TX_DV <= '0';
elsif falling_edge(i_clk) then
r_TX_DV <= i_TX_DV; -- 1 clock cycle delay
if i_TX_DV = '1' then
r_TX_Byte <= i_TX_Byte;
end if;
end if;
end process Byte_Reg;
-- Purpose: Generate MOSI data
-- Works with both CPHA=0 and CPHA=1
MOSI_Data : process (i_Clk, i_Rst_L)
begin
if i_Rst_L = '0' then
o_SPI_MOSI <= '0';
r_TX_Bit_Count <= "111"; -- Send MSB first
elsif falling_edge(i_Clk) then
-- If ready is high, reset bit counts to default
if o_TX_Ready = '1' then
r_TX_Bit_Count <= "111";
-- Catch the case where we start transaction and CPHA = 0
elsif (r_TX_DV = '1' and w_CPHA = '0') then
o_SPI_MOSI <= r_TX_Byte(7);
r_TX_Bit_Count <= "110"; -- 6
elsif (r_Leading_Edge = '1' and w_CPHA = '1') or (r_Trailing_Edge = '1' and w_CPHA = '0') then
r_TX_Bit_Count <= r_TX_Bit_Count - 1;
o_SPI_MOSI <= r_TX_Byte(to_integer(r_TX_Bit_Count));
end if;
end if;
end process MOSI_Data;
-- Purpose: Read in MISO data.
MISO_Data : process (i_Clk, i_Rst_L)
begin
if i_Rst_L = '0' then
o_RX_Byte <= X"000";
o_RX_DV <= '0';
r_RX_Bit_Count <= "111"; -- Starts at 7
elsif falling_edge(i_Clk) then
-- Default Assignments
o_RX_DV <= '0';
if o_TX_Ready = '1' then -- Check if ready, if so reset count to default
r_RX_Bit_Count <= "111"; -- Starts at 7
elsif (r_Leading_Edge = '1' and w_CPHA = '0') or (r_Trailing_Edge = '1' and w_CPHA = '1') then
o_RX_Byte(to_integer(r_RX_Bit_Count)) <= i_SPI_MISO; -- Sample data
r_RX_Bit_Count <= r_RX_Bit_Count - 1;
if r_RX_Bit_Count = "000" then
o_RX_DV <= '1'; -- Byte done, pulse Data Valid
end if;
end if;
end if;
end process MISO_Data;
-- Purpose: Add clock delay to signals for alignment.
SPI_Clock : process (i_Clk, i_Rst_L)
begin
if i_Rst_L = '0' then
o_SPI_Clk <= w_CPOL;
elsif falling_edge(i_Clk) then
o_SPI_Clk <= r_SPI_Clk;
end if;
end process SPI_Clock;
end architecture RTL;
SPI_master_withCS.vhd
-------------------------------------------------------------------------------
-- Description: SPI (Serial Peripheral Interface) Master
-- With single chip-select (AKA Slave Select) capability
--
-- Supports arbitrary length byte transfers.
--
-- Instantiates a SPI Master and adds single CS.
-- If multiple CS signals are needed, will need to use different
-- module, OR multiplex the CS from this at a higher level.
--
-- Note: i_Clk must be at least 2x faster than i_SPI_Clk
--
-- Parameters: SPI_MODE, can be 0, 1, 2, or 3. See above.
-- Can be configured in one of 4 modes:
-- Mode | Clock Polarity (CPOL/CKP) | Clock Phase (CPHA)
-- 0 | 0 | 0
-- 1 | 0 | 1
-- 2 | 1 | 0
-- 3 | 1 | 1
--
-- CLKS_PER_HALF_BIT - Sets frequency of o_SPI_Clk. o_SPI_Clk is
-- derived from i_Clk. Set to integer number of clocks for each
-- half-bit of SPI data. E.g. 100 MHz i_Clk, CLKS_PER_HALF_BIT = 2
-- would create o_SPI_CLK of 25 MHz. Must be >= 2
--
-- MAX_BYTES_PER_CS - Set to the maximum number of bytes that
-- will be sent during a single CS-low pulse.
--
-- CS_INACTIVE_CLKS - Sets the amount of time in clock cycles to
-- hold the state of Chip-Selct high (inactive) before next
-- command is allowed on the line. Useful if chip requires some
-- time when CS is high between trasnfers.
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity spi_master_cs is
generic (
SPI_MODE : integer := 2;
CLKS_PER_HALF_BIT : integer := 5;
MAX_BYTES_PER_CS : integer := 2;
CS_INACTIVE_CLKS : integer := 1
);
port (
-- Control/Data Signals,
i_Rst_L : in std_logic; -- FPGA Reset
i_Clk : in std_logic; -- FPGA Clock
-- TX (MOSI) Signals
i_TX_Count : in std_logic_vector; -- # bytes per CS low
i_TX_Byte : in std_logic_vector(11 downto 0); -- Byte to transmit on MOSI
i_TX_DV : in std_logic; -- Data Valid Pulse with i_TX_Byte
o_TX_Ready : out std_logic; -- Transmit Ready for next byte
-- RX (MISO) Signals
o_RX_Count : inout std_logic_vector(11 downto 0); -- Index RX byte
o_RX_DV : inout std_logic; -- Data Valid pulse (1 clock cycle)
o_RX_Byte : out std_logic_vector(11 downto 0); -- Byte received on MISO
-- SPI Interface
o_SPI_Clk : out std_logic;
i_SPI_MISO : in std_logic;
o_SPI_CS_n : out std_logic
);
end entity spi_master_cs;
architecture RTL of spi_master_cs is
type t_SM_CS is (IDLE, TRANSFER, CS_INACTIVE);
signal r_SM_CS : t_SM_CS;
signal r_CS_n : std_logic;
signal r_CS_Inactive_Count : integer range 0 to CS_INACTIVE_CLKS;
signal r_TX_Count : integer range 0 to MAX_BYTES_PER_CS + 1;
signal w_Master_Ready : std_logic;
begin
-- Instantiate Master
SPI_Master_1 : entity work.spi_master
generic map (
SPI_MODE => SPI_MODE,
CLKS_PER_HALF_BIT => CLKS_PER_HALF_BIT)
port map (
-- Control/Data Signals,
i_Rst_L => i_Rst_L, -- FPGA Reset
i_Clk => i_Clk, -- FPGA Clock
-- TX (MOSI) Signals
i_TX_Byte => i_TX_Byte, -- Byte to transmit
i_TX_DV => i_TX_DV, -- Data Valid pulse
o_TX_Ready => w_Master_Ready, -- Transmit Ready for Byte
-- RX (MISO) Signals
o_RX_DV => o_RX_DV, -- Data Valid pulse
o_RX_Byte => o_RX_Byte, -- Byte received on MISO
-- SPI Interface
o_SPI_Clk => o_SPI_Clk,
i_SPI_MISO => i_SPI_MISO
);
-- Purpose: Control CS line using State Machine
SM_CS :process(i_Clk, i_Rst_L)
begin
if i_Rst_L = '0' then
r_SM_CS <= IDLE;
r_CS_n <= '1'; -- Resets to high
r_TX_Count <= 0;
r_CS_Inactive_Count <= CS_INACTIVE_CLKS;
elsif falling_edge(i_Clk) then
case r_SM_CS is
when IDLE =>
if r_CS_n = '1' and i_TX_DV = '1' then -- Start of transmission
r_TX_Count <= to_integer(unsigned(i_TX_Count) - 1); -- Register TX Count
r_CS_n <= '0'; -- Drive CS low
r_SM_CS <= TRANSFER; -- Transfer bytes
end if;
when TRANSFER =>
-- Wait until SPI is done transferring do next thing
if w_Master_Ready = '1' then
if r_TX_Count > 0 then
if i_TX_DV = '1' then
r_TX_Count <= r_TX_Count - 1;
end if;
else
r_CS_n <= '1'; -- we done, so set CS high
r_CS_Inactive_Count <= CS_INACTIVE_CLKS;
r_SM_CS <= CS_INACTIVE;
end if;
end if;
when CS_INACTIVE =>
if r_CS_Inactive_Count > 0 then
r_CS_Inactive_Count <= r_CS_Inactive_Count - 1;
else
r_SM_CS <= IDLE;
end if;
when others =>
r_CS_n <= '1'; -- we done, so set CS high
r_SM_CS <= IDLE;
end case;
end if;
end process SM_CS;
-- Purpose: Keep track of RX_Count
RX_COUNT : process (i_Clk)
begin
if falling_edge(i_Clk) then
if r_CS_n = '1' then
o_RX_Count <= std_logic_vector(to_unsigned(0, o_RX_Count'length));
elsif o_RX_DV = '1' then
o_RX_Count <= std_logic_vector(unsigned(o_RX_Count) + 1);
end if;
end if;
end process RX_COUNT;
o_SPI_CS_n <= r_CS_n;
o_TX_Ready <= '1' when i_TX_DV /= '1' and ((r_SM_CS = IDLE) or (r_SM_CS = TRANSFER and w_Master_Ready = '1' and r_TX_Count > 0)) else '0';
end architecture RTL;
mic3.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity mic3 is
port (
-- Control/Data Signals,
i_Switch_1 : in std_logic; -- Used as FPGA Reset
i_Clk : in std_logic; -- FPGA Clock
-- PMOD SPI Interface
o_SPI_Clk : out std_logic;
i_SPI_MISO : in std_logic;
o_SPI_CS_n : out std_logic;
-- Output
r_ADC_Word : out std_logic_vector(11 downto 0)
);
end entity mic3;
architecture RTL of mic3 is
-- CPOL = 1 and CPHA = 1, based on ADC part ADC08S021
constant SPI_MODE : integer := 2;
-- Go Board operates at 25 MHz, so divide by 10 to get to 2.5 MHz.
-- This is clocks per half bit, so divide 10 by 2 to get 5.
constant CLKS_PER_HALF_BIT : integer := 5;
-- Number of clock cycles to leave CS high after transaction is done
constant CS_INACTIVE_CLKS : integer := 100;
signal w_Rst_L : std_logic;
-- SPI Signals
signal w_Master_RX_Count : std_logic_vector(11 downto 0);
signal w_Master_RX_DV : std_logic;
signal w_Master_RX_Byte : std_logic_vector(11 downto 0);
signal w_Master_TX_Ready : std_logic;
signal r_Master_TX_DV : std_logic;
signal w_LED_Enable : std_logic;
signal w_Ambient_Val : std_logic_vector(11 downto 0);
begin -- architecture RTL
w_Rst_L <= not i_Switch_1; -- switch defaults to low.
SPI_Master_With_Single_CS_1: entity work.spi_master_cs
generic map (
SPI_MODE => SPI_MODE,
CLKS_PER_HALF_BIT => CLKS_PER_HALF_BIT,
MAX_BYTES_PER_CS => 2, -- always 2 bytes per CS
CS_INACTIVE_CLKS => CS_INACTIVE_CLKS)
port map (
i_Rst_L => w_Rst_L, -- FPGA Reset (push button)
i_Clk => i_Clk, -- FPGA Clock
-- TX (MOSI Signals)
i_TX_Count => x"10", -- Always 2 bytes per CS
i_TX_Byte => x"000", -- Can stuff with zeros
i_TX_DV => r_Master_TX_DV, -- Data valid pulse with i_TX_Byte
o_TX_Ready => w_Master_TX_Ready, -- Transmit ready for byte
-- RX (MISO) Signals
o_RX_Count => w_Master_RX_Count, -- Index of RX'd byte
o_RX_DV => w_Master_RX_DV, -- Data valid pulse (1 clock cycle)
o_RX_Byte => w_Master_RX_Byte, -- Byte received on MISO
-- SPI Interface
o_SPI_Clk => o_SPI_Clk,
i_SPI_MISO => i_SPI_MISO,
o_SPI_CS_n => o_SPI_CS_n
);
-- Purpose: Handle read requests from ADC.
-- Just request data as often as possible
-- Also creates simple counter to count continuously.
process (i_Clk) is
begin
if falling_edge(i_Clk) then
r_Master_TX_DV <= w_Master_TX_Ready;
end if;
end process;
-- Purpose: Handle data being read back from SPI
-- Pack up response into a single 8-bit value for ambient light.
process (i_Clk) is
begin
if falling_edge(i_Clk) then
if w_Master_RX_DV = '1' then
if w_Master_RX_Count = "00" then
r_ADC_Word <= w_Master_RX_Byte;
end if;
end if;
end if;
end process;
end architecture RTL;
Link to comment
Share on other sites
0 answers to this question
Recommended Posts
Archived
This topic is now archived and is closed to further replies.