git-svn-id: http://moon:8086/svn/vhdl/trunk@1277 cc03376c-175c-47c8-b038-4cd826a8556b
374 lines
9.0 KiB
VHDL
374 lines
9.0 KiB
VHDL
LIBRARY IEEE;
|
|
USE IEEE.STD_LOGIC_1164.ALL;
|
|
USE IEEE.NUMERIC_STD.ALL;
|
|
use std.textio.all; -- Imports the standard textio package.
|
|
|
|
use work.utils_pkg.all;
|
|
use work.spi_types.all;
|
|
|
|
ENTITY spi_tx IS
|
|
Generic
|
|
(
|
|
word_width : natural := 32;
|
|
CAT_FIFO_DEPTH : natural := 2;
|
|
DATA_FIFO_DEPTH : natural := 16
|
|
);
|
|
Port
|
|
(
|
|
rst : in STD_LOGIC;
|
|
clk : in STD_LOGIC;
|
|
cmd_vld : in STD_LOGIC;
|
|
cmd_rdy : out STD_LOGIC;
|
|
cmd : in cmd_t;
|
|
din_vld : in STD_LOGIC;
|
|
din_rdy : out STD_LOGIC;
|
|
din : in unsigned(word_width-1 downto 0);
|
|
dout : out unsigned(word_width-1 downto 0);
|
|
dout_vld : out STD_LOGIC;
|
|
dout_re : in STD_LOGIC;
|
|
shift_en : in STD_LOGIC;
|
|
ctrl : in ctrl_t;
|
|
status : out status_t;
|
|
mosi : out STD_LOGIC;
|
|
miso : in STD_LOGIC;
|
|
sclk : out STD_LOGIC;
|
|
mso : out STD_LOGIC
|
|
|
|
);
|
|
END spi_tx;
|
|
|
|
ARCHITECTURE behavior OF spi_tx IS
|
|
|
|
type state_t is (reset, idle, load_data, shift, finish);
|
|
|
|
signal xfer_count : unsigned(XFER_MAX_SIZE_BITS-1 downto 0);
|
|
signal data_count : unsigned(XFER_MAX_SIZE_BITS-1 downto 0);
|
|
signal cmd_reg : cmd_t;
|
|
signal state : state_t;
|
|
signal state_next : state_t;
|
|
signal xfer_count_en : std_logic;
|
|
signal xfer_count_busy : std_logic;
|
|
signal data_count_busy : std_logic;
|
|
signal spi_clk : std_logic := '0';
|
|
signal spi_clk_out : std_logic;
|
|
signal clk_en : std_logic;
|
|
signal spi_clk_data : std_logic;
|
|
signal spi_rst_count : unsigned(5 downto 0);
|
|
signal spi_rst : std_logic;
|
|
|
|
signal cat_fifo_din : unsigned(to_unsigned(cmd)'length-1 downto 0);
|
|
signal cat_fifo_dout : unsigned(to_unsigned(cmd)'length-1 downto 0);
|
|
signal cat_fifo_full : std_logic;
|
|
signal cat_fifo_empty : std_logic;
|
|
|
|
signal write_fifo_din : unsigned(word_width-1 downto 0);
|
|
signal write_fifo_dout : unsigned(word_width-1 downto 0);
|
|
signal write_fifo_full : std_logic;
|
|
signal write_fifo_empty : std_logic;
|
|
|
|
signal read_fifo_din : unsigned(word_width-1 downto 0);
|
|
signal read_fifo_dout : unsigned(word_width-1 downto 0);
|
|
signal read_fifo_full : std_logic;
|
|
signal read_fifo_empty : std_logic;
|
|
signal read_fifo_we : std_logic;
|
|
|
|
signal tx_shift_reg : unsigned(word_width-1 downto 0);
|
|
signal rx_shift_reg : unsigned(word_width-1 downto 0);
|
|
signal shift_cnt_pipe : unsigned(word_width-1 downto 0);
|
|
signal data_load_en : std_logic;
|
|
signal xfer_start_en : std_logic;
|
|
signal ctrl_reg : ctrl_t;
|
|
signal rx_enable : std_logic;
|
|
signal slv_enable : std_logic;
|
|
|
|
--------------------------------------------------------------------------
|
|
begin
|
|
|
|
sclk <= spi_clk_out;
|
|
cat_fifo_din <= to_unsigned(cmd);
|
|
write_fifo_din <= din;
|
|
cmd_reg <= to_cmd(cat_fifo_dout);
|
|
mosi <= tx_shift_reg(tx_shift_reg'left) when ctrl_reg.msb_first = '1' else tx_shift_reg(tx_shift_reg'right);
|
|
spi_clk_data <= spi_clk xor ctrl_reg.cpha;
|
|
|
|
dout_vld <= not read_fifo_empty;
|
|
status.cmd_fifo_full <= cat_fifo_full;
|
|
status.cmd_fifo_empty <= cat_fifo_empty;
|
|
status.write_fifo_full <= write_fifo_full;
|
|
status.write_fifo_empty <= write_fifo_empty;
|
|
status.read_fifo_full <= read_fifo_full;
|
|
status.read_fifo_empty <= read_fifo_empty;
|
|
status.rx_valid <= read_fifo_we;
|
|
mso <= slv_enable;
|
|
rx_enable <= '1' when cmd_reg.xfer_size > cmd_reg.data_size else '0';
|
|
|
|
--------------------------------------------------------------------------
|
|
-- Instantiate synchronous FIFO
|
|
inst_cat_fifo: entity work.fifo_async
|
|
GENERIC MAP
|
|
(
|
|
addr_width => NextExpBaseTwo(CAT_FIFO_DEPTH),
|
|
data_width => cmd_size
|
|
)
|
|
PORT MAP
|
|
(
|
|
rst => spi_rst,
|
|
clk_w => clk,
|
|
clk_r => spi_clk,
|
|
we => cmd_vld,
|
|
re => xfer_start_en,
|
|
fifo_full => cat_fifo_full,
|
|
fifo_empty => cat_fifo_empty,
|
|
fifo_afull => open,
|
|
fifo_aempty => open,
|
|
data_w => cat_fifo_din,
|
|
data_r => cat_fifo_dout
|
|
);
|
|
|
|
-- Instantiate synchronous FIFO
|
|
inst_write_fifo: entity work.fifo_async
|
|
GENERIC MAP
|
|
(
|
|
addr_width => NextExpBaseTwo(DATA_FIFO_DEPTH),
|
|
data_width => word_width
|
|
)
|
|
PORT MAP
|
|
(
|
|
rst => spi_rst,
|
|
clk_w => clk,
|
|
clk_r => spi_clk,
|
|
we => din_vld,
|
|
re => data_load_en,
|
|
fifo_full => write_fifo_full,
|
|
fifo_empty => write_fifo_empty,
|
|
fifo_afull => open,
|
|
fifo_aempty => open,
|
|
data_w => write_fifo_din,
|
|
data_r => write_fifo_dout
|
|
);
|
|
|
|
-- Instantiate synchronous FIFO
|
|
inst_read_fifo: entity work.fifo_async
|
|
GENERIC MAP
|
|
(
|
|
addr_width => NextExpBaseTwo(DATA_FIFO_DEPTH),
|
|
data_width => word_width
|
|
)
|
|
PORT MAP
|
|
(
|
|
rst => spi_rst,
|
|
clk_w => clk,
|
|
clk_r => spi_clk,
|
|
we => read_fifo_we,
|
|
re => dout_re,
|
|
fifo_full => read_fifo_full,
|
|
fifo_empty => read_fifo_empty,
|
|
fifo_afull => open,
|
|
fifo_aempty => open,
|
|
data_w => rx_shift_reg,
|
|
data_r => dout
|
|
);
|
|
|
|
--------------------------------------------------------------------------
|
|
proc_spi_clk_gen:
|
|
process(clk)
|
|
begin
|
|
if rising_edge(clk) then
|
|
if rst = '1' then
|
|
spi_rst <= '1';
|
|
spi_clk <= '0';
|
|
spi_rst_count <= (others => '1');
|
|
else
|
|
spi_clk <= not spi_clk;
|
|
if spi_rst_count /= to_unsigned(0, spi_rst_count'length) then
|
|
spi_rst_count <= spi_rst_count - 1;
|
|
else
|
|
spi_rst <= '0';
|
|
end if;
|
|
end if ;
|
|
end if;
|
|
end process;
|
|
|
|
proc_spi_clk_out_gen:
|
|
process(clk)
|
|
begin
|
|
if rising_edge(clk) then
|
|
if spi_rst = '1' then
|
|
spi_clk_out <= '0';
|
|
elsif clk_en = '0' then
|
|
spi_clk_out <= ctrl_reg.cpol;
|
|
else
|
|
spi_clk_out <= not spi_clk_out;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
proc_state_next:
|
|
process(spi_clk)
|
|
begin
|
|
if rising_edge(spi_clk) then
|
|
if spi_rst = '1' then
|
|
state <= reset;
|
|
else
|
|
state <= state_next;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
proc_ctrl_reg:
|
|
process(spi_clk)
|
|
begin
|
|
if rising_edge(spi_clk) then
|
|
if xfer_start_en = '1' or spi_rst = '1' then
|
|
ctrl_reg <= ctrl;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
proc_fsm:
|
|
process(state, rx_enable, cat_fifo_empty, cat_fifo_full, write_fifo_empty, write_fifo_full, xfer_count_busy, data_count_busy, shift_cnt_pipe, ctrl_reg)
|
|
begin
|
|
state_next <= state;
|
|
|
|
xfer_start_en <= '0';
|
|
xfer_count_en <= '0';
|
|
data_load_en <= '0';
|
|
clk_en <= '0';
|
|
cmd_rdy <= not cat_fifo_full;
|
|
din_rdy <= not write_fifo_full;
|
|
slv_enable <= '1';
|
|
read_fifo_we <= '0';
|
|
|
|
case state is
|
|
|
|
when reset =>
|
|
cmd_rdy <= '0';
|
|
din_rdy <= '0';
|
|
slv_enable <= '0';
|
|
state_next <= idle;
|
|
|
|
when idle =>
|
|
slv_enable <= '0';
|
|
if cat_fifo_empty = '0' then
|
|
state_next <= load_data;
|
|
xfer_start_en <= '1';
|
|
end if;
|
|
|
|
when load_data =>
|
|
if xfer_count_busy = '0' then
|
|
state_next <= idle;
|
|
elsif write_fifo_empty = '0' then
|
|
data_load_en <= '1';
|
|
state_next <= shift;
|
|
clk_en <= ctrl_reg.cpha;
|
|
end if;
|
|
|
|
when shift =>
|
|
clk_en <= '1';
|
|
xfer_count_en <= '1';
|
|
if xfer_count_busy = '0' then
|
|
state_next <= finish;
|
|
elsif shift_cnt_pipe(shift_cnt_pipe'left) = '1' and data_count_busy = '1' then
|
|
if write_fifo_empty = '0' then
|
|
data_load_en <= '1';
|
|
else
|
|
state_next <= load_data;
|
|
end if;
|
|
end if;
|
|
|
|
when finish =>
|
|
read_fifo_we <= rx_enable;
|
|
state_next <= idle;
|
|
|
|
when others =>
|
|
state_next <= idle;
|
|
|
|
end case;
|
|
end process;
|
|
|
|
PROC_XFER_COUNT:
|
|
process(spi_clk)
|
|
begin
|
|
if rising_edge(spi_clk) then
|
|
if xfer_start_en = '1' then
|
|
xfer_count <= cmd_reg.xfer_size;
|
|
xfer_count_busy <= '1';
|
|
elsif shift_en = '1' and xfer_count_en = '1' then
|
|
if xfer_count /= to_unsigned(2, xfer_count'length) then
|
|
xfer_count <= xfer_count - 1;
|
|
else
|
|
xfer_count_busy <= '0';
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
PROC_SHIFT_COUNT:
|
|
process(spi_clk)
|
|
begin
|
|
if rising_edge(spi_clk) then
|
|
if xfer_start_en = '1' then
|
|
shift_cnt_pipe <= (shift_cnt_pipe'left downto 1 => '0') & '1';
|
|
elsif shift_en = '1' and xfer_count_en = '1' then
|
|
if shift_cnt_pipe(shift_cnt_pipe'left) = '0' then
|
|
shift_cnt_pipe <= shift_cnt_pipe(shift_cnt_pipe'left-1 downto 0) & '0';
|
|
else
|
|
shift_cnt_pipe <= (shift_cnt_pipe'left downto 1 => '0') & '1';
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- Transmitter
|
|
PROC_DATA_COUNT:
|
|
process(spi_clk)
|
|
begin
|
|
if rising_edge(spi_clk) then
|
|
if xfer_start_en = '1' then
|
|
data_count <= cmd_reg.data_size;
|
|
data_count_busy <= '1';
|
|
elsif shift_en = '1' and xfer_count_en = '1' then
|
|
if data_count /= to_unsigned(2, data_count'length) then
|
|
data_count <= data_count - 1;
|
|
else
|
|
data_count_busy <= '0';
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
PROC_TX_SHIFT_REG:
|
|
process(spi_clk_data)
|
|
begin
|
|
if rising_edge(spi_clk_data) then
|
|
if data_load_en = '1' then
|
|
tx_shift_reg <= write_fifo_dout;
|
|
elsif shift_en = '1' and xfer_count_en = '1' then
|
|
if (ctrl_reg.msb_first = '1') then
|
|
tx_shift_reg <= tx_shift_reg(tx_shift_reg'left-1 downto 0) & '0';
|
|
else
|
|
tx_shift_reg <= '0' & tx_shift_reg(tx_shift_reg'left downto 1);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- Receiver
|
|
PROC_RX_SHIFT_REG:
|
|
process(spi_clk_out)
|
|
begin
|
|
if rising_edge(spi_clk_out) then
|
|
if shift_en = '1' and xfer_count_en = '1' then
|
|
if slv_enable = '1' then
|
|
if (ctrl_reg.msb_first = '1') then
|
|
rx_shift_reg <= rx_shift_reg(rx_shift_reg'left-1 downto 0) & miso;
|
|
else
|
|
rx_shift_reg <= miso & rx_shift_reg(rx_shift_reg'left downto 1);
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
end behavior;
|