123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- library ieee;
- use ieee.std_logic_1164.all;
- use ieee.numeric_std.all;
- library work;
- use work.wishbone_types.all;
- entity spi_rxtx is
- generic (
- DATA_LINES : positive := 1; -- Number of data lines
- -- 1=MISO/MOSI, otherwise 2 or 4
- INPUT_DELAY : natural range 0 to 1 := 1 -- Delay latching of SPI input:
- -- 0=no delay, 1=clk/2
- );
- port (
- clk : in std_ulogic;
- rst : in std_ulogic;
- --
- -- Clock divider
- -- SCK = CLK/((CLK_DIV+1)*2) : 0=CLK/2, 1=CLK/4, 2=CLK/6....
- --
- -- This need to be changed before a command.
- -- XX TODO add handshake
- clk_div_i : in natural range 0 to 255;
- --
- -- Command port (includes write data)
- --
- -- Valid & ready: command sampled when valid=1 and ready=1
- cmd_valid_i : in std_ulogic;
- cmd_ready_o : out std_ulogic;
- -- Command modes:
- -- 000 : Single bit read+write
- -- 010 : Single bit read
- -- 011 : Single bit write
- -- 100 : Dual read
- -- 101 : Dual write
- -- 110 : Quad read
- -- 111 : Quad write
- cmd_mode_i : in std_ulogic_vector(2 downto 0);
- -- # clocks-1 in a command (#bits-1)
- cmd_clks_i : in std_ulogic_vector(2 downto 0);
- -- Write data (sampled with command)
- cmd_txd_i : in std_ulogic_vector(7 downto 0);
- --
- -- Read data port. Data valid when d_ack=1, no ready
- -- signal, receiver must be ready
- --
- d_rxd_o : out std_ulogic_vector(7 downto 0);
- d_ack_o : out std_ulogic := '0';
- -- Set when all commands are done. Needed for callers to know when
- -- to release CS#
- bus_idle_o : out std_ulogic;
- --
- -- SPI port. These might need to go into special IOBUFs or STARTUPE2 on
- -- Xilinx.
- --
- -- Data lines are organized as follow:
- --
- -- DATA_LINES = 1
- --
- -- sdat_o(0) is MOSI (master output slave input)
- -- sdat_i(0) is MISO (master input slave output)
- --
- -- DATA_LINES > 1
- --
- -- sdat_o(0..n) are DQ(0..n)
- -- sdat_i(0..n) are DQ(0..n)
- --
- -- as such, beware that:
- --
- -- sdat_o(0) is MOSI (master output slave input)
- -- sdat_i(1) is MISO (master input slave output)
- --
- -- In order to leave dealing with the details of how to wire the tristate
- -- and bidirectional pins to the system specific toplevel, we separate
- -- the input and output signals, and provide a "sdat_oe" signal which
- -- is the "output enable" of each line.
- --
- sck : out std_ulogic;
- sdat_o : out std_ulogic_vector(DATA_LINES-1 downto 0);
- sdat_oe : out std_ulogic_vector(DATA_LINES-1 downto 0);
- sdat_i : in std_ulogic_vector(DATA_LINES-1 downto 0)
- );
- end entity spi_rxtx;
- architecture rtl of spi_rxtx is
- -- Internal clock signal. Output is gated by sck_en_int
- signal sck_0 : std_ulogic;
- signal sck_1 : std_ulogic;
- -- Clock divider latch
- signal clk_div : natural range 0 to 255;
- -- 1 clk pulses indicating when to send and when to latch
- --
- -- Typically for CPOL=CPHA
- -- sck_send is sck falling edge
- -- sck_recv is sck rising edge
- --
- -- Those pulses are generated "ahead" of the corresponding
- -- edge so then are "seen" at the rising sysclk edge matching
- -- the corresponding sck edgeg.
- signal sck_send : std_ulogic;
- signal sck_recv : std_ulogic;
- -- Command mode latch
- signal cmd_mode : std_ulogic_vector(2 downto 0);
-
- -- Output shift register (use fifo ?)
- signal oreg : std_ulogic_vector(7 downto 0);
- -- Input latch
- signal dat_i_l : std_ulogic_vector(DATA_LINES-1 downto 0);
- -- Data ack latch
- signal dat_ack_l : std_ulogic;
- -- Delayed recv signal for the read machine
- signal sck_recv_d : std_ulogic := '0';
- -- Input shift register (use fifo ?)
- signal ireg : std_ulogic_vector(7 downto 0) := (others => '0');
- -- Bit counter
- signal bit_count : std_ulogic_vector(2 downto 0);
- -- Next/start/stop command signals. Set when counter goes negative
- signal next_cmd : std_ulogic;
- signal start_cmd : std_ulogic;
- signal end_cmd : std_ulogic;
- function data_single(mode : std_ulogic_vector(2 downto 0)) return boolean is
- begin
- return mode(2) = '0';
- end;
- function data_dual(mode : std_ulogic_vector(2 downto 0)) return boolean is
- begin
- return mode(2 downto 1) = "10";
- end;
- function data_quad(mode : std_ulogic_vector(2 downto 0)) return boolean is
- begin
- return mode(2 downto 1) = "11";
- end;
- function data_write(mode : std_ulogic_vector(2 downto 0)) return boolean is
- begin
- return mode(0) = '1';
- end;
- type state_t is (STANDBY, DATA);
- signal state : state_t := STANDBY;
- begin
- -- We don't support multiple data lines at this point
- assert DATA_LINES = 1 or DATA_LINES = 2 or DATA_LINES = 4
- report "Unsupported DATA_LINES configuration !" severity failure;
- -- Clock generation
- --
- -- XX HARD WIRE CPOL=1 CPHA=1 for now
- sck_gen: process(clk)
- variable counter : integer range 0 to 255;
- begin
- if rising_edge(clk) then
- if rst = '1' then
- sck_0 <= '1';
- sck_1 <= '1';
- sck_send <= '0';
- sck_recv <= '0';
- clk_div <= 0;
- counter := 0;
- elsif counter = clk_div then
- counter := 0;
- -- Latch new divider
- clk_div <= clk_div_i;
- -- Internal version of the clock
- sck_0 <= not sck_0;
- -- Generate send/receive pulses to run out state machine
- sck_recv <= not sck_0;
- sck_send <= sck_0;
- else
- counter := counter + 1;
- sck_recv <= '0';
- sck_send <= '0';
- end if;
- -- Delayed version of the clock to line up with
- -- the up/down signals
- --
- -- XXX Figure out a better way
- if (state = DATA and end_cmd = '0') or (next_cmd = '1' and cmd_valid_i = '1') then
- sck_1 <= sck_0;
- else
- sck_1 <= '1';
- end if;
- end if;
- end process;
- -- SPI clock
- sck <= sck_1;
- -- Ready to start the next command. This is set on the clock down
- -- after the counter goes negative.
- -- Note: in addition to latching a new command, this will cause
- -- the counter to be reloaded.
- next_cmd <= '1' when sck_send = '1' and bit_count = "111" else '0';
- -- We start a command when we have a valid request at that time.
- start_cmd <= next_cmd and cmd_valid_i;
-
- -- We end commands if we get start_cmd and there's nothing to
- -- start. This sends up to standby holding CLK high
- end_cmd <= next_cmd and not cmd_valid_i;
- -- Generate cmd_ready. It will go up and down with sck, we could
- -- gate it with cmd_valid to make it look cleaner but that would
- -- add yet another combinational loop on the wishbone that I'm
- -- to avoid.
- cmd_ready_o <= next_cmd;
- -- Generate bus_idle_o
- bus_idle_o <= '1' when state = STANDBY else '0';
- -- Main state machine. Also generates cmd and data ACKs
- machine: process(clk)
- begin
- if rising_edge(clk) then
- if rst = '1' then
- state <= STANDBY;
- cmd_mode <= "000";
- else
- -- First clk down of a new cycle. Latch a request if any
- -- or get out.
- if start_cmd = '1' then
- state <= DATA;
- cmd_mode <= cmd_mode_i;
- elsif end_cmd = '1' then
- state <= STANDBY;
- end if;
- end if;
- end if;
- end process;
- -- Run the bit counter in DATA state. It will update on rising
- -- SCK edges. It starts at d_clks on command latch
- count_bit: process(clk)
- begin
- if rising_edge(clk) then
- if rst = '1' then
- bit_count <= (others => '0');
- else
- if start_cmd = '1' then
- bit_count <= cmd_clks_i;
- elsif state /= DATA then
- bit_count <= (others => '1');
- elsif sck_recv = '1' then
- bit_count <= std_ulogic_vector(unsigned(bit_count) - 1);
- end if;
- end if;
- end if;
- end process;
- -- Shift output data
- shift_out: process(clk)
- begin
- if rising_edge(clk) then
- -- Starting a command
- if start_cmd = '1' then
- oreg <= cmd_txd_i(7 downto 0);
- elsif sck_send = '1' then
- -- Get shift amount
- if data_single(cmd_mode) then
- oreg <= oreg(6 downto 0) & '0';
- elsif data_dual(cmd_mode) then
- oreg <= oreg(5 downto 0) & "00";
- else
- oreg <= oreg(3 downto 0) & "0000";
- end if;
- end if;
- end if;
- end process;
- -- Data out
- sdat_o(0) <= oreg(7);
- dl2: if DATA_LINES > 1 generate
- sdat_o(1) <= oreg(6);
- end generate;
- dl4: if DATA_LINES > 2 generate
- sdat_o(2) <= oreg(5);
- sdat_o(3) <= oreg(4);
- end generate;
- -- Data lines direction
- dlines: process(all)
- begin
- for i in DATA_LINES-1 downto 0 loop
- sdat_oe(i) <= '0';
- if state = DATA then
- -- In single mode, we always enable MOSI, otherwise
- -- we control the output enable based on the direction
- -- of transfer.
- --
- if i = 0 and (data_single(cmd_mode) or data_write(cmd_mode)) then
- sdat_oe(i) <= '1';
- end if;
- if i = 1 and data_dual(cmd_mode) and data_write(cmd_mode) then
- sdat_oe(i) <= '1';
- end if;
- if i > 0 and data_quad(cmd_mode) and data_write(cmd_mode) then
- sdat_oe(i) <= '1';
- end if;
- end if;
- end loop;
- end process;
- -- Latch input data no delay
- input_delay_0: if INPUT_DELAY = 0 generate
- process(clk)
- begin
- if rising_edge(clk) then
- dat_i_l <= sdat_i;
- end if;
- end process;
- end generate;
- -- Latch input data half clock delay
- input_delay_1: if INPUT_DELAY = 1 generate
- process(clk)
- begin
- if falling_edge(clk) then
- dat_i_l <= sdat_i;
- end if;
- end process;
- end generate;
- -- Shift input data
- shift_in: process(clk)
- begin
- if rising_edge(clk) then
- -- Delay the receive signal to match the input latch
- if state = DATA then
- sck_recv_d <= sck_recv;
- else
- sck_recv_d <= '0';
- end if;
- -- Generate read data acks
- if bit_count = "000" and sck_recv = '1' then
- dat_ack_l <= not cmd_mode(0);
- else
- dat_ack_l <= '0';
- end if;
- -- And delay them as well
- d_ack_o <= dat_ack_l;
- -- Shift register on delayed data & receive signal
- if sck_recv_d = '1' then
- if DATA_LINES = 1 then
- ireg <= ireg(6 downto 0) & dat_i_l(0);
- else
- if data_dual(cmd_mode) then
- ireg <= ireg(5 downto 0) & dat_i_l(1) & dat_i_l(0);
- elsif data_quad(cmd_mode) then
- ireg <= ireg(3 downto 0) & dat_i_l(3) & dat_i_l(2) & dat_i_l(1) & dat_i_l(0);
- else
- assert(data_single(cmd_mode));
- ireg <= ireg(6 downto 0) & dat_i_l(1);
- end if;
- end if;
- end if;
- end if;
- end process;
- -- Data recieve register
- d_rxd_o <= ireg;
- end architecture;
|