exjobb-public/src/socbridge_driver.vhd

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;