-- this module transmit a given constant data when requested
-- and then signals done.
--
-- G_DATA has to contain also the address and the r/w bit
--
-- MSB of G_DATA will go first
-- P1DATA_P2DATA_P3DATA
--
-- version for multiple devices with different data

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity spi_master_transmit is
generic (
        G_DATA1 : std_logic_vector;
        G_DATA2 : std_logic_vector;
        G_NUM_BITS_PACKET : integer;
        G_NUM_PACKETS : integer;
        G_NUM_BITS_PAUSE : integer
);
port (
        
        i_clk : in std_logic;
        i_rst : in std_logic;
        
        o_done : out std_logic;
        
        -- selects which data to transfer. If '0', it transfers G_DATA1
        i_data_selector : in std_logic;
        
        -- SPI ports:
        o_n_ce : out std_logic_vector;
        o_dout : out std_logic;
        o_clk  : out std_logic
        
        );
                
end spi_master_transmit;

architecture behavioral of spi_master_transmit is

        subtype t_pause_counter is integer range 0 to G_NUM_BITS_PAUSE;
        signal s_pause_counter : t_pause_counter;
        subtype t_bit_counter is integer range 0 to G_NUM_BITS_PACKET - 1;
        signal s_bit_counter : t_bit_counter;
        subtype t_packet_counter is integer range 0 to G_NUM_PACKETS;
        signal s_packet_counter : t_packet_counter;
        subtype t_device_counter is integer range 0 to o_n_ce'length - 1;
        signal s_device_counter : t_device_counter;
        
        signal s_clk_inv : std_logic;
        
        --signal s_o_dout_d : std_logic;
        signal s_o_clk_en : std_logic;
        
        constant C_DATALEN_PER_DEVICE : integer := G_NUM_BITS_PACKET*G_NUM_PACKETS;
        
        signal s_cs_out : std_logic_vector( o_n_ce'range );
        
begin

        assert( G_DATA1'length = C_DATALEN_PER_DEVICE*o_n_ce'length ) report "The size of G_DATA1 does not match the number of devices and other generics." severity failure;
        assert( G_DATA2'length = C_DATALEN_PER_DEVICE*o_n_ce'length ) report "The size of G_DATA2 does not match the number of devices and other generics." severity failure;
        
        -- inverted clock:
        s_clk_inv <= not i_clk;
        
        -- output clock:
        o_clk <= i_clk and s_o_clk_en;
        
        -- done:
        o_done <= '1' when ( (i_rst = '0') and (s_packet_counter = 0) and (s_pause_counter = 0) and (s_bit_counter = 0 ) and (s_device_counter=0) ) else
                                                '0';
        
        transmitter_process : process( s_clk_inv )
        begin
                if( rising_edge( s_clk_inv ) ) then
                        if( i_rst = '1' ) then
                                s_bit_counter <= 0;
                                s_pause_counter <= t_pause_counter'high;
                                s_packet_counter <= t_packet_counter'high;
                                s_device_counter <= t_device_counter'high;
                                s_cs_out( s_cs_out'low ) <= '0';
                                s_cs_out( s_cs_out'high downto s_cs_out'low+1 ) <= ( others => '1' );
                                o_n_ce <= ( o_n_ce'range => '1' );
                                o_dout <= '0';
                                s_o_clk_en <= '0';
                        elsif( s_bit_counter > 0 ) then
                                o_n_ce <= s_cs_out;
                                if( i_data_selector = '0' ) then
                                        o_dout <= G_DATA1( s_bit_counter + s_packet_counter*G_NUM_BITS_PACKET - 1 + s_device_counter*C_DATALEN_PER_DEVICE ); -- here s_packet_counter points to the current packet and s_bit_counter is one bit behind, therefore the -1.
                                else
                                        o_dout <= G_DATA2( s_bit_counter + s_packet_counter*G_NUM_BITS_PACKET - 1 + s_device_counter*C_DATALEN_PER_DEVICE );
                                end if;
                                s_bit_counter <= s_bit_counter - 1;
                                s_o_clk_en <= '1';
                        elsif( s_pause_counter > 0 ) then
                                o_n_ce <= ( o_n_ce'range => '1' );
                                s_pause_counter <= s_pause_counter - 1;
                                o_dout <= '0';
                                s_o_clk_en <= '0';
                        elsif( s_packet_counter > 0 ) then
                                s_bit_counter <= t_bit_counter'high;
                                s_pause_counter <= t_pause_counter'high;
                                s_packet_counter <= s_packet_counter - 1;
                                o_n_ce <= s_cs_out;
                                if( i_data_selector = '0' ) then
                                        o_dout <= G_DATA1( s_bit_counter + s_packet_counter*G_NUM_BITS_PACKET - 1 + s_device_counter*C_DATALEN_PER_DEVICE ); -- here s_bit_counter = 0, s_packet_counter points to previous packet. Therefore -1 to get the msb of current packet.
                                else
                                        o_dout <= G_DATA2( s_bit_counter + s_packet_counter*G_NUM_BITS_PACKET - 1 + s_device_counter*C_DATALEN_PER_DEVICE );
                                end if;
                                s_o_clk_en <= '1';
                        elsif( s_device_counter > 0 ) then
                                s_cs_out <= s_cs_out( s_cs_out'high-1 downto s_cs_out'low ) & '1';
                                s_device_counter <= s_device_counter - 1;
                                
                                -- follows pause.
                                s_bit_counter <= 0;
                                s_pause_counter <= t_pause_counter'high;
                                s_packet_counter <= t_packet_counter'high;
                                o_n_ce <= ( o_n_ce'range => '1' );
                                o_dout <= '0';
                                s_o_clk_en <= '0';
                        
                        end if; 
                end if;
        end process;

        
end architecture;