library IEEE; use IEEE.std_logic_1164.all; use IEEE.NUMERIC_STD.all; library work; use work.io_types.all; use work.socbridge_driver_tb_pkg.all; entity socbridge_driver is port( clk : in std_logic; rst : in std_logic; cmd : in command_t; address : in std_logic_vector(31 downto 0); cmd_size: in positive; ext_in : in ext_socbridge_in_t; ext_out : out ext_socbridge_out_t; int_in : out int_socbridge_in_t; int_out : in int_socbridge_out_t ); end entity socbridge_driver; architecture rtl of socbridge_driver is signal next_parity_out : std_logic; signal ext_in_rec : ext_protocol_t; shared variable ext_out_data_cmd : std_logic_vector(interface_inst.socbridge.payload_width - 1 downto 0); signal test : std_logic_vector(interface_inst.socbridge.payload_width - 1 downto 0); signal next_state : state_t; signal curr_cmd_bits : std_logic_vector(4 downto 0); signal curr_response : response_t; signal curr_response_bits : std_logic_vector(4 downto 0); signal st : state_rec_t; begin --- DEBUG GLOBAL BINDINGS --- -- synthesis translate_off G_next_parity_out <= next_parity_out; G_ext_in_rec <= ext_in_rec; G_next_state <= next_state; G_ext_out_data_cmd <=test; G_curr_command_bits <= curr_cmd_bits; G_curr_response <= curr_response; G_curr_response_bits <= curr_response_bits; G_st <= st; -- synthesis translate_on ext_in_rec.data <= ext_in.payload; ext_in_rec.clk <= ext_in.control(1); ext_in_rec.parity <= ext_in.control(0); -- Helpful Bindings -- curr_response_bits <= ext_in.payload(7 downto 3); -- CANT USE EXT_IN_REC here for some reason, the assignment becomes stasteful -- Not sure that the two process method is helping here: if this was a normal -- signal assignment there would be no confusion. -- in the case ... <= ext_in_rec we get -- curr_resp | ext_in_rec | ext_in -- 00000 | 00000000 | 00001001 -- 00000 | 00001001 | 00001001 -- 00001 | 00001001 | 00001001 -- 00001 | 00001001 | 00001001 -- -- but in the case ... <= ext_in we get -- curr_resp | ext_in_rec | ext_in -- 00000 | 00000000 | 00001001 -- 00001 | 00001001 | 00001001 -- 00001 | 00001001 | 00001001 -- 00001 | 00001001 | 00001001 with curr_response_bits select curr_response <= WRITE_ACK when "00001", WRITE_ACK when "00101", READ_RESPONSE when "01000", READ_RESPONSE when "01100", NO_OP when others; comb_proc: process(ext_in, int_out, curr_response, st, cmd) begin -- Outputs ext_out <= create_io_type_out_from_ext_protocol(st.ext_out_reg); --- State Transition Diagram --- -- -- -- -- +-----+ -- | | -- \|/ /--+ -- IDLE<-------------------+ -- / \ | -- / \ | -- / \ | -- \|/ \|/ | -- TX_HEADER RX_HEADER | -- |\ / | | -- | \ / | | -- | ADDR1 | | -- | | | | -- | \|/ | | -- | ADDR2 | | -- | | | | -- | \|/ | | -- | ADDR3 | | -- | | | | -- | \|/ | | -- | ADDR4 | | -- | /\ | | -- | / \ | | -- |-+ +----| +---+ | -- \|/ \|/ \|/ | | -- TX_BODY RX_RESPONSE---+ | -- | | | -- | +--+ | | -- \|/\|/ | \|/ | -- TX_ACK--+ RX_BODY | -- | | | -- | | | -- +-----------+--------------+ -- --- Next State Assignment --- case st.curr_state is when IDLE => if cmd = WRITE or cmd = WRITE_ADD then next_state <= TX_HEADER; elsif cmd = READ or cmd = READ_ADD then next_state <= RX_HEADER; else next_state <= IDLE; end if; when TX_HEADER => -- The header only takes one word (cycle) to transmit. -- Continue to body or address directly afterwards. if st.cmd_reg = WRITE_ADD then next_state <= ADDR1; else next_state <= TX_BODY; end if; when TX_BODY => -- Here we want to stay in TX_BODY for the duration of a packet. if st.write_stage = 0 then next_state <= TX_ACK; else next_state <= TX_BODY; end if; when TX_ACK => -- Wait for write acknowledgement. if curr_response = WRITE_ACK then next_state <= IDLE; else next_state <= TX_ACK; end if; when RX_HEADER => -- The header only takes one word (cycle) to transmit. -- Continue to awaiting response directly afterwards. if st.cmd_reg = READ_ADD then next_state <= ADDR1; else next_state <= RX_RESPONSE; end if; when RX_RESPONSE => -- Wait for read response. if curr_response = READ_RESPONSE then next_state <= RX_BODY_NO_OUT; else next_state <= RX_RESPONSE; end if; when RX_BODY_NO_OUT => next_state <= RX_BODY; when RX_BODY => -- Here we want to stay in RX_BODY for the duration of a packet. if st.read_stage = 0 then next_state <= IDLE; else next_state <= RX_BODY; end if; when ADDR1 => -- Transmits the entire address and returns to the appropriate next_state <= ADDR2; when ADDR2 => next_state <= ADDR3; when ADDR3 => next_state <= ADDR4; when ADDR4 => if st.cmd_reg = WRITE or st.cmd_reg = WRITE_ADD then next_state <= TX_BODY; else next_state <= RX_RESPONSE; end if; end case; --- Combinatorial output based on current state --- ext_out_data_cmd := (others => '0'); int_in.is_full_out <= '1'; int_in.write_enable_in <= '0'; int_in.payload <= (others => '0'); case st.curr_state is when IDLE => if cmd = WRITE or cmd = WRITE_ADD then ext_out_data_cmd := get_cmd_bits(cmd) & get_size_bits(cmd_size); elsif cmd = READ or cmd = READ_ADD then ext_out_data_cmd := get_cmd_bits(cmd) & get_size_bits(cmd_size); end if; when TX_HEADER => if st.cmd_reg = WRITE_ADD then ext_out_data_cmd := st.addr_reg(7 downto 0); else ext_out_data_cmd := int_out.payload; int_in.is_full_out <= '0'; end if; when TX_BODY => if st.write_stage > 0 then int_in.is_full_out <= '0'; ext_out_data_cmd := int_out.payload; else ext_out_data_cmd := (others => '0'); end if; when TX_ACK => when RX_HEADER => if st.cmd_reg = READ_ADD then ext_out_data_cmd := st.addr_reg(7 downto 0); end if; when RX_RESPONSE => when RX_BODY_NO_OUT => when RX_BODY => int_in.payload <= st.ext_in_reg.data; int_in.write_enable_in <= '1'; when ADDR1 => ext_out_data_cmd := st.addr_reg(15 downto 8); when ADDR2 => ext_out_data_cmd := st.addr_reg(23 downto 16); when ADDR3 => ext_out_data_cmd := st.addr_reg(31 downto 24); when ADDR4 => if st.cmd_reg = WRITE_ADD then int_in.is_full_out <= '0'; ext_out_data_cmd := int_out.payload; end if; end case; next_parity_out <= calc_parity(ext_out_data_cmd); --- DEBUG GLOBAL BINDINGS --- -- synthesis translate_off test <= ext_out_data_cmd; -- synthesis translate_on end process comb_proc; -- Process updating internal registers based on primary clock seq_proc: process(ext_in_rec.clk, rst) begin if(rst = '1') then st.ext_in_reg.data <= (others => '0'); st.ext_out_reg.data <= (others => '0'); st.ext_out_reg.clk <= '0'; st.ext_out_reg.parity <= '1'; st.curr_state <= IDLE; st.write_stage <= 0; st.read_stage <= 0; st.cmd_reg <= NO_OP; st.addr_reg <= (others => '0'); elsif(rising_edge(ext_in_rec.clk)) then st.ext_in_reg.data <= ext_in_rec.data; st.ext_in_reg.clk <= ext_in_rec.clk; st.ext_in_reg.parity <= ext_in_rec.parity; st.ext_out_reg.data <= ext_out_data_cmd; st.ext_out_reg.clk <= not st.ext_out_reg.clk; st.ext_out_reg.parity <= next_parity_out; st.curr_state <= next_state; case st.curr_state is when IDLE => if cmd = WRITE or cmd = WRITE_ADD or cmd = READ or cmd = READ_ADD then st.addr_reg <= address; st.cmd_reg <= cmd; end if; when TX_HEADER => st.write_stage <= 2**(cmd_size - 1) - 1; when TX_BODY => if st.write_stage > 0 then st.write_stage <= st.write_stage - 1; end if; when RX_HEADER => st.read_stage <= 2**(cmd_size - 1) - 1; when RX_BODY => if st.read_stage > 0 then st.read_stage <= st.read_stage - 1; end if; when others => end case; end if; end process seq_proc; end architecture rtl;