123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- --+-----------------------------------+-------------------------------------+--
- --| ___ ___ | (c) 2013-2014 William R Sowerbutts |--
- --| ___ ___ ___ ___( _ ) / _ \ | will@sowerbutts.com |--
- --| / __|/ _ \ / __|_ / _ \| | | | | |--
- --| \__ \ (_) | (__ / / (_) | |_| | | A Z80 FPGA computer, just for fun |--
- --| |___/\___/ \___/___\___/ \___/ | |--
- --| | http://sowerbutts.com/ |--
- --+-----------------------------------+-------------------------------------+--
- --| A rudimentary SPI master peripheral |--
- --+-------------------------------------------------------------------------+--
- library IEEE;
- use IEEE.STD_LOGIC_1164.ALL;
- use IEEE.NUMERIC_STD.ALL;
- entity spimaster is
- port ( clk : in std_logic;
- reset : in std_logic;
- cpu_address : in std_logic_vector(2 downto 0);
- cpu_wait : out std_logic;
- data_in : in std_logic_vector(7 downto 0);
- data_out : out std_logic_vector(7 downto 0);
- enable : in std_logic;
- req_read : in std_logic;
- req_write : in std_logic;
- slave_cs : out std_logic;
- slave_clk : out std_logic;
- slave_mosi : out std_logic;
- slave_miso : in std_logic
- );
- end spimaster;
- -- registers:
- -- base+0 -- chip select control; bit 0 is slave_cs
- -- base+1 -- status register; bit 0 indicates "transmitter busy"
- -- base+2 -- transmitter: write a byte here, starts SPI bus transaction
- -- base+3 -- receiver: last byte received (updated on each transation)
- -- base+4 -- clock divider: clk counts from 0 to whatever is in this register before proceeding
- --
- -- Note that if an SPI transfer is underway already the CPU will be
- -- forced to wait until it completes before any register can be
- -- read or written. This is very convenient as it means you can
- -- just read or write bytes without checking the status register.
- architecture Behavioral of spimaster is
- -- start up in idle state
- signal slave_cs_register : std_logic := '1';
- signal slave_clk_register : std_logic := '1';
- signal slave_mosi_register: std_logic := '0';
- signal data_out_sr : std_logic_vector(7 downto 0) := (others => '0'); -- shifted left ie MSB <- LSB
- signal data_in_sr : std_logic_vector(7 downto 0) := (others => '0'); -- shifted left ie MSB <- LSB
- signal busy_sr : std_logic_vector(7 downto 0) := (others => '0'); -- shifted left ie MSB <- LSB
- signal clk_divide_target : unsigned(7 downto 0) := (others => '0');
- signal clk_divide_value : unsigned(7 downto 0) := (others => '0');
- signal cpu_was_idle : std_logic := '1';
- -- cpu visible registers
- signal chip_select_out : std_logic_vector(7 downto 0);
- signal status_data_out : std_logic_vector(7 downto 0);
- begin
- chip_select_out <= "0000000" & slave_cs_register;
- status_data_out <= "0000000" & busy_sr(7);
- cpu_wait <= busy_sr(7);
- with cpu_address select
- data_out <=
- chip_select_out when "000",
- status_data_out when "001",
- data_out_sr when "010",
- data_in_sr when "011",
- std_logic_vector(clk_divide_target) when "100",
- status_data_out when others;
- slave_cs <= slave_cs_register;
- slave_clk <= slave_clk_register;
- slave_mosi <= slave_mosi_register;
- spimaster_proc: process(clk)
- begin
- if rising_edge(clk) then
- if reset = '1' then
- slave_cs_register <= '1';
- slave_clk_register <= '1';
- slave_mosi_register <= '0';
- data_out_sr <= (others => '0');
- data_in_sr <= (others => '0');
- busy_sr <= (others => '0');
- clk_divide_target <= (others => '0');
- clk_divide_value <= (others => '0');
- cpu_was_idle <= '1';
- else
- -- divide down input clk to get 2 * spi clk
- clk_divide_value <= clk_divide_value + 1;
- if clk_divide_value = clk_divide_target then
- clk_divide_value <= to_unsigned(0, 8);
- end if;
- if busy_sr(7) = '1' then
- if clk_divide_value = clk_divide_target then
- -- we're in the midst of a transaction! whoo!
- if slave_clk_register = '1' then
- -- clk is high; next cycle will be falling edge of clk
- slave_clk_register <= '0';
- slave_mosi_register <= data_out_sr(7);
- -- shift data out
- data_out_sr <= data_out_sr(6 downto 0) & '0';
- else
- -- clk is low; next cycle will be rising edge of clk
- slave_clk_register <= '1';
- -- shift busy
- busy_sr <= busy_sr(6 downto 0) & '0';
- -- latch data in
- data_in_sr <= data_in_sr(6 downto 0) & slave_miso;
- end if;
- end if;
- end if;
- if enable = '1' and req_write = '1' then
- if busy_sr(7) = '0' and cpu_was_idle = '1' then
- cpu_was_idle <= '0';
- case cpu_address is
- when "000" =>
- slave_cs_register <= data_in(0);
- when "010" =>
- -- only allow writes when transmitter is idle
- data_out_sr <= data_in;
- busy_sr <= (others => '1');
- when "100" =>
- clk_divide_target <= unsigned(data_in);
- when others => -- no change
- end case;
- else
- cpu_was_idle <= cpu_was_idle;
- end if;
- else
- cpu_was_idle <= '1';
- end if;
- end if;
- end if;
- end process;
- end Behavioral;
|