293 lines
11 KiB
VHDL
293 lines
11 KiB
VHDL
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;
|