--=====================================================
-- Проект 18. Ведущий SPI
-- Выдаёт тактовый сигнал, сдвигает байт наружу
-- и одновременно принимает байт от ведомого.
--=====================================================
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity spi_master is
    generic (
        CLK_DIV : integer := 4                          -- делитель тактовой частоты
    );
    port (
        clk      : in  std_logic;
        rst      : in  std_logic;
        start    : in  std_logic;
        data_in  : in  std_logic_vector(7 downto 0);    -- байт на передачу
        data_out : out std_logic_vector(7 downto 0);    -- принятый байт
        done     : out std_logic;
        sclk     : out std_logic;
        mosi     : out std_logic;
        miso     : in  std_logic;
        cs_n     : out std_logic                        -- выбор ведомого
    );
end entity spi_master;

architecture rtl of spi_master is
    type state_t is (IDLE, TRANSFER);
    signal state    : state_t := IDLE;
    signal divider  : integer range 0 to CLK_DIV := 0;
    signal bit_cnt  : integer range 0 to 8 := 0;
    signal sclk_reg : std_logic := '0';
    signal tx_reg   : std_logic_vector(7 downto 0) := (others => '0');
    signal rx_reg   : std_logic_vector(7 downto 0) := (others => '0');
begin
    process (clk, rst)
    begin
        if rst = '1' then
            state    <= IDLE;
            sclk_reg <= '0';
            done     <= '0';
        elsif rising_edge(clk) then
            done <= '0';

            case state is
                when IDLE =>
                    sclk_reg <= '0';
                    divider  <= 0;
                    bit_cnt  <= 0;
                    if start = '1' then
                        tx_reg <= data_in;
                        state  <= TRANSFER;
                    end if;

                when TRANSFER =>
                    if divider = CLK_DIV - 1 then
                        divider  <= 0;
                        sclk_reg <= not sclk_reg;

                        if sclk_reg = '0' then
                            -- нарастающий фронт: принимаем бит
                            rx_reg <= rx_reg(6 downto 0) & miso;
                        else
                            -- спад: выдвигаем следующий бит
                            tx_reg <= tx_reg(6 downto 0) & '0';
                            if bit_cnt = 7 then
                                state <= IDLE;
                                done  <= '1';
                            else
                                bit_cnt <= bit_cnt + 1;
                            end if;
                        end if;
                    else
                        divider <= divider + 1;
                    end if;
            end case;
        end if;
    end process;

    sclk     <= sclk_reg;
    mosi     <= tx_reg(7);                              -- старшим битом вперёд
    cs_n     <= '1' when state = IDLE else '0';
    data_out <= rx_reg;
end architecture rtl;
