library ieee;
use ieee.std_logic_1164.all;
use work.spi_pkg.all;
entity spi_peripheral is
generic (
SIZES : natural_vector := (8, 16);
SIZES_2LOG : natural := 1;
DIVISORS : natural_vector := (2, 4, 6, 8, 16, 32, 64, 128, 256);
DIVISORS_LOG2 : natural := 3;
CSN_PULSE_CYCLES : natural := 1
);
port (
waddress_i : in std_logic_vector(2 downto 0);
wdata_i : in std_logic_vector(31 downto 0);
raddress_i : in std_logic_vector(2 downto 0);
rdata_o : out std_logic_vector(31 downto 0);
write_i : in std_logic;
read_i : in std_logic;
-- IOs
sck_i : in std_logic;
miso_i : in std_logic;
mosi_i : in std_logic;
csn_i : in std_logic;
sck_o : out std_logic;
miso_o : out std_logic;
mosi_o : out std_logic;
csn_o : out std_logic;
sck_t : out std_logic;
miso_t : out std_logic;
mosi_t : out std_logic;
csn_t : out std_logic;
-- Control
clk_i : in std_logic;
rst_in : in std_logic;
interrupt_o : out std_logic
-- rx_valid_o : out std_logic;
-- tx_ready_o : out std_logic;
-- busy_o : out std_logic;
-- err_lost_rx_data_o : out std_logic
);
end entity spi_peripheral;
architecture a1 of spi_peripheral is
constant MAX_SIZE : natural := get_max_natural(SIZES);
constant CTRL_ADDRESS : std_logic_vector(2 downto 0) := "000";
constant INTMASK_ADDRESS : std_logic_vector(2 downto 0) := "001";
constant STATUS_ADDRESS : std_logic_vector(2 downto 0) := "010";
constant DATA_ADDRESS : std_logic_vector(2 downto 0) := "011";
constant CTRL_EN_BIT : natural := 0;
constant CTRL_MASTER_BIT : natural := 1;
constant CTRL_TX_EN_BIT : natural := 2;
constant CTRL_RX_EN_BIT : natural := 3;
constant CTRL_CLOCK_POLARITY_BIT : natural := 4;
constant CTRL_CLOCK_PHASE_BIT : natural := 5;
constant CTRL_PULSE_CSN_BIT : natural := 6;
constant CTRL_LSBFIRST_BIT : natural := 7;
-- constant CTRL_RX_BLOCK_ON_FULL_BIT : natural := 8;
constant CTRL_SIZE_SEL_BIT : natural := 10;
constant CTRL_DIV_SEL_BIT : natural := 20;
constant INTMASK_RX_BUFFER_FULL : natural := 0;
constant INTMASK_TX_BUFFER_EMPTY : natural := 1;
constant INTMASK_LOST_RX_DATA : natural := 4;
constant CTRL_DEFAULT : std_logic_vector(31 downto 0)
:= (CTRL_TX_EN_BIT => '1',
CTRL_RX_EN_BIT => '1',
others => '0');
constant INTMASK_DEFAULT : std_logic_vector(31 downto 0) := (others => '0');
signal rx_buffer : std_logic_vector(MAX_SIZE - 1 downto 0);
signal rx_buffer_full : std_logic;
signal tx_buffer : std_logic_vector(MAX_SIZE - 1 downto 0);
signal tx_buffer_full : std_logic;
signal fill_tx_buffer : std_logic;
signal reg_control : std_logic_vector(31 downto 0);
signal reg_status : std_logic_vector(31 downto 0);
signal reg_intmask : std_logic_vector(31 downto 0);
signal clear_rx_buffer_full : std_logic;
signal en : std_logic;
signal master : std_logic;
signal clock_polarity : std_logic;
signal clock_phase : std_logic;
signal size_sel : std_logic_vector(SIZES_2LOG - 1 downto 0);
signal div_sel : std_logic_vector(DIVISORS_LOG2 - 1 downto 0);
signal pulse_csn : std_logic;
signal rx_block_on_full : std_logic;
signal lsbfirst : std_logic;
-- Rx
signal rx_en : std_logic;
signal rx_data : std_logic_vector(MAX_SIZE - 1 downto 0);
signal rx_valid : std_logic;
signal rx_ready : std_logic;
-- Tx
signal tx_en : std_logic;
signal tx_data : std_logic_vector(MAX_SIZE - 1 downto 0);
signal tx_valid : std_logic;
signal tx_ready : std_logic;
-- State
signal busy : std_logic;
signal err_lost_rx_data : std_logic;
signal clear_lost_rx_data : std_logic;
begin -- architecture a1
interrupt_o <= (rx_buffer_full and reg_intmask(INTMASK_RX_BUFFER_FULL)) or
(not tx_buffer_full and reg_intmask(INTMASK_TX_BUFFER_EMPTY)) or
(err_lost_rx_data and reg_intmask(INTMASK_LOST_RX_DATA));
en <= reg_control(CTRL_EN_BIT);
master <= reg_control(CTRL_MASTER_BIT);
clock_polarity <= reg_control(CTRL_CLOCK_POLARITY_BIT);
clock_phase <= reg_control(CTRL_CLOCK_PHASE_BIT);
pulse_csn <= reg_control(CTRL_PULSE_CSN_BIT);
size_sel <= reg_control(CTRL_SIZE_SEL_BIT + SIZES_2LOG - 1 downto CTRL_SIZE_SEL_BIT);
div_sel <= reg_control(CTRL_DIV_SEL_BIT + DIVISORS_LOG2 - 1 downto CTRL_DIV_SEL_BIT);
lsbfirst <= reg_control(CTRL_LSBFIRST_BIT);
tx_en <= reg_control(CTRL_TX_EN_BIT);
rx_en <= reg_control(CTRL_RX_EN_BIT);
rx_block_on_full <= '0';
masterslave : entity work.spi_masterslave
generic map (
DIVISORS => DIVISORS,
DIVISORS_LOG2 => DIVISORS_LOG2,
SIZES => SIZES,
SIZES_2LOG => SIZES_2LOG,
CSN_PULSE_CYCLES => CSN_PULSE_CYCLES)
port map (
-- IOs
sck_o => sck_o,
miso_o => miso_o,
mosi_o => mosi_o,
csn_o => csn_o,
sck_i => sck_i,
miso_i => miso_i,
mosi_i => mosi_i,
csn_i => csn_i,
sck_t => sck_t,
miso_t => miso_t,
mosi_t => mosi_t,
csn_t => csn_t,
-- Control
clk_i => clk_i,
rst_in => rst_in,
en_i => en,
master_i => master,
clock_polarity_i => clock_polarity,
clock_phase_i => clock_phase,
size_sel_i => size_sel,
div_sel_i => div_sel,
pulse_csn_i => pulse_csn,
rx_block_on_full_i => rx_block_on_full,
lsbfirst_i => lsbfirst,
-- Data
-- Rx
rx_en_i => rx_en,
rx_data_o => rx_data,
rx_valid_o => rx_valid,
rx_ready_i => rx_ready,
-- Tx
tx_en_i => tx_en,
tx_data_i => tx_data,
tx_valid_i => tx_valid,
tx_ready_o => tx_ready,
-- State
busy_o => busy,
err_lost_rx_data_o => err_lost_rx_data,
clear_lost_rx_data_i => clear_lost_rx_data);
writer: process (clk_i) is
begin -- process writer
if rising_edge(clk_i) then -- rising clock edge
if rst_in = '0' then -- synchronous reset (active low)
reg_control <= CTRL_DEFAULT;
reg_intmask <= INTMASK_DEFAULT;
fill_tx_buffer <= '0';
elsif write_i = '1' then
case waddress_i is
when CTRL_ADDRESS => reg_control <= wdata_i;
when INTMASK_ADDRESS => reg_intmask <= wdata_i;
when DATA_ADDRESS => fill_tx_buffer <= '1';
when STATUS_ADDRESS =>
clear_lost_rx_data <= wdata_i(4);
when others => null;
end case;
else
fill_tx_buffer <= '0';
clear_lost_rx_data <= '0';
end if;
end if;
end process writer;
reader: process (clk_i) is
begin -- process reader
if rising_edge(clk_i) then -- rising clock edge
if rst_in = '0' then -- synchronous reset (active low)
clear_rx_buffer_full <= '0';
elsif read_i = '1' then
case raddress_i is
when DATA_ADDRESS => clear_rx_buffer_full <= '1';
when others => null;
end case;
else
clear_rx_buffer_full <= '0';
end if;
end if;
end process reader;
output_register: process (all) is
begin -- process output_register
case raddress_i is
when CTRL_ADDRESS => rdata_o <= reg_control;
when STATUS_ADDRESS => rdata_o <= reg_status;
when DATA_ADDRESS => rdata_o <= (15 downto 0 => rx_buffer, others => '0');
when INTMASK_ADDRESS => rdata_o <= reg_intmask;
when others => rdata_o <= (others => '0');
end case;
end process output_register;
tx_buffer_writer: process (clk_i) is
begin -- process tx_buffer_writer
if rising_edge(clk_i) then -- rising clock edge
if rst_in = '0' then -- synchronous reset (active low)
tx_buffer <= (others => '0');
tx_buffer_full <= '0';
else
if fill_tx_buffer = '1' and tx_buffer_full = '0' then
tx_buffer_full <= '1';
tx_buffer <= wdata_i(tx_buffer'range);
tx_buffer_full <= '1';
elsif tx_ready = '1' and tx_buffer_full = '1' then
tx_buffer_full <= '0';
end if;
end if;
end if;
end process tx_buffer_writer;
rx_buffer_reader: process (clk_i) is
begin -- process rx_buffer_full
if rising_edge(clk_i) then -- rising clock edge
if rst_in = '0' then -- synchronous reset (active low)
rx_buffer_full <= '0';
else
if clear_rx_buffer_full = '1' then
rx_buffer_full <= '0';
end if;
if rx_valid = '1' then
rx_buffer_full <= '1';
rx_buffer <= rx_data;
end if;
end if;
end if;
end process rx_buffer_reader;
reg_status <= "000000000000000000000000000" &
err_lost_rx_data &
busy &
"0" &
(not tx_buffer_full) &
rx_buffer_full;
tx_data <= tx_buffer;
rx_ready <= not rx_buffer_full;
tx_valid <= tx_buffer_full;
end architecture a1;