- Started implementing SPI controller

This commit is contained in:
2013-06-05 15:40:06 +02:00
parent 1807fb99b5
commit fd4e8df556

270
src/spi.vhd Normal file
View File

@@ -0,0 +1,270 @@
-------------------------------------------------------------------------------
-- Title : SPI Controller
-- Project :
-------------------------------------------------------------------------------
-- File : spi.vhd
-- Author : Matthias Blankertz <matthias@blankertz.org>
-- Company :
-- Created : 2013-06-05
-- Last update: 2013-06-05
-- Platform :
-- Standard : VHDL'93/02
-------------------------------------------------------------------------------
-- Description:
-------------------------------------------------------------------------------
-- Copyright (c) 2013
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2013-06-05 1.0 matthias Created
-------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
use IEEE.NUMERIC_STD.ALL;
-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
library UNISIM;
use UNISIM.VComponents.all;
use work.intercon_package.all;
-- Register map
-- 0 : data in/out register
-- 1 : status register
-- 2 : config1 register
-- 7..6: reserved
-- 5..0: output clock divider
entity spi is
generic (
dontcare : std_logic := '-';
clock_pol : std_logic := '1';
clock_phase : std_logic := '0'
);
port (
clk_i : in std_logic;
rst_i : in std_logic;
-- Wishbone slave port
dat_i : in std_logic_vector(7 downto 0);
we_i : in std_logic;
cyc_i : in std_logic;
stb_i : in std_logic;
adr_i : in std_logic_vector(1 downto 0);
dat_o : out std_logic_vector(7 downto 0);
ack_o : out std_logic;
-- SPI Master
miso : in std_logic;
mosi : out std_logic;
sclk : out std_logic;
ss : out std_logic
);
end spi;
architecture Behavioral of spi is
constant sclk_obuf_init : std_logic := not clock_pol;
signal spi_clk, spi_clk_dly : std_logic := '0';
signal spi_clk_inv : std_logic;
type spi_states is (S_IDLE, S_XFER_START, S_XFER, S_WAITFALLING, S_WAITINFIFO);
signal spi_state : spi_states := S_IDLE;
signal clk_divider, clk_ctr : unsigned(5 downto 0) := to_unsigned(0, 6);
begin
spi_clk_gen : process(clk_i)
begin
if rising_edge(clk_i) then
if spi_clk_rst = '1' then
clk_ctr <= to_unsigned(0, 6);
spi_clk <= '0';
elsif clk_ctr >= clk_divider then
clk_ctr <= to_unsigned(0, 6);
spi_clk <= not spi_clk;
else
clk_ctr <= clk_ctr + 1;
end if;
spi_clk_dly <= spi_clk;
end if;
end process spi_clk_gen;
spi_clk_inv <= not spi_clk_dly;
sclk_obuf : ODDR2
generic map (
DDR_ALIGNMENT => "C0",
INIT => sclk_obuf_init
)
port map (
Q => sclk,
C0 => spi_clk_dly,
C1 => spi_clk_inv,
CE => sclk_o_en,
D0 => sclk_o_d0,
D1 => sclk_o_d1,
R => '0',
S => '0'
);
sclk_o_d0 <= not clock_pol;
sclk_o_d1 <= clock_pol;
spi_sreg : process(clk_i)
begin
if rising_edge(clk_i) then
infifo_wr <= '0';
infifo_din <= (others => dontcare);
outfifo_rd <= '0';
case spi_state is
when S_IDLE =>
if outfifo_empty = '0' then
sreg(8 downto 1) <= outfifo_dout;
outfifo_rd <= '1';
if clock_phase = '0' then
mosi <= outfifo_dout(7);
end if;
spi_state <= S_XFER_START;
end if;
when S_XFER_START =>
if spi_clk_dly = '1' and spi_clk = '0' then
sclk_o_en <= '1';
spi_state <= S_XFER;
end if;
when S_XFER =>
if ((spi_clk_dly = '0' and spi_clk = '1' and clock_phase = '0') or
(spi_clk_dly = '1' and spi_clk = '0' and clock_phase = '1')) then
-- Data in
sreg(0) <= miso;
if spi_ctr = 7 then
-- 8 bits latched, i.e. transfer complete
if outfifo_empty = '0' and infifo_full = '0' then
-- data for new transfer available
-- save received data
infifo_din <= sreg(7 downto 1) & miso;
infifo_wr <= '1';
-- load new data
sreg(8 downto 1) <= outfifo_dout;
outfifo_rd <= '1';
spi_ctr <= 0;
else
-- stop transfer
if infifo_full = '0' then
-- save received data
infifo_din <= sreg(7 downto 1) & miso;
infifo_wr <= '1';
end if;
if clock_phase = '1' then
sclk_o_en <= '0';
if infifo_full = '1' then
spi_state <= S_WAITINFIFO;
else
spi_state <= S_IDLE;
end if;
else
indata_pending <= infifo_full;
spi_state <= S_WAITFALLING;
end if;
end if;
end if;
elsif (spi_clk_dly = '1' and spi_clk = '0' and clock_phase = '0') then
-- Data out
mosi <= sreg(8);
sreg <= sreg(7 downto 0) & "0";
elsif (spi_clk_dly = '0' and spi_clk = '1' and clock_phase = '1') then
-- Data out
mosi <= sreg(8);
sreg <= sreg(7 downto 0) & "0";
end if;
when S_WAITFALLING =>
if spi_clk_dly = '1' and spi_clk = '0' then
sclk_o_en <= '0';
if indata_pending = '1' then
if infifo_full = '0' then
-- save received data
infifo_din <= sreg(7 downto 0);
infifo_wr <= '1';
spi_state <= S_IDLE;
else
spi_state <= S_WAITINFIFO;
end if;
else
spi_state <= S_IDLE;
end if;
end if;
when S_WAITINFIFO =>
if infifo_full = '0' then
-- save received data
infifo_din <= sreg(7 downto 0);
infifo_wr <= '1';
spi_state <= S_IDLE;
end if;
end case;
end if;
end process spi_sreg;
spi_idle <= '1' when spi_state = S_IDLE and outfifo_empty = '1' else
'0';
wb : process(clk_i)
begin
if rising_edge(clk_i) then
outfifo_wr <= '0';
infifo_rd <= '0';
outfifo_din <= (others => dontcare);
if rst_i = '1' then
wbs_o.ack_o <= '0';
wb_in_cyc <= '0';
wbs_o.dat_o <= (others => dontcare);
else
if wb_in_cyc = '1' and wbs_i.stb_i = '0' then
wbs_o.dat_o <= (others => dontcare);
wb_in_cyc <= '0';
elsif wb_in_cyc = '0' and wbs_i.stb_i = '1' and wbs_i.cyc_i = '1' then
wb_in_cyc <= '1';
wbs_o.ack_o <= '1';
if wbs_i.we_i = '0' then
if wbs_i.adr_i = "00" then
-- read data i/o reg
wbs_o.dat_o <= infifo_dout;
if infifo_empty = '0' then
infifo_rd <= '1';
end if;
elsif wbs_i.adr_i = "01" then
-- read status reg
wbs_o.dat_o(7 downto 3) <= (others => dontcare)
wbs_o.dat_o(2 downto 0) <= infifo_empty & outfifo_full & spi_idle;
elsif wbs_i.adr_i = "10" then
-- read config reg
wbs_o.dat_o(7 downto 6) <= (others => dontcare);
wbs_o.dat_o(5 downto 0) <= std_logic_vector(clk_divider);
else
wbs_o.dat_o <= (others => dontcare);
end if;
else
wbs_o.dat_o <= (others => dontcare);
if wbs_i.adr_i = "00" then
-- write data i/o reg
outfifo_din <= wbs_i.dat_i;
if outfifo_full = '0' then
outfifo_wr <= '1';
end if;
elsif wbs_i.adr_i = "10" then
-- write config reg
clk_divider <= unsigned(wbs_i.dat_i(5 downto 0));
end if;
end if;
end if;
end if;
end if;
end process wb;
end Behavioral;