264 lines
7.3 KiB
VHDL
264 lines
7.3 KiB
VHDL
-- -------------------------------------------------------------------------- --
|
|
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
|
|
-- -------------------------------------------------------------------------- --
|
|
-- TODO
|
|
-- -------------------------------------------------------------------------- --
|
|
-- Author : Markus Koch <markus@notsyncing.net>
|
|
-- Contributors : None
|
|
-- License : Mozilla Public License (MPL) Version 2
|
|
-- -------------------------------------------------------------------------- --
|
|
|
|
-- TODO: By default, some (random) value is placed in cached_address, which may corrupt data if this happens to be the
|
|
-- first address accessesd after reset.
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
|
|
library generics;
|
|
use generics.wishbone_pkg.all;
|
|
|
|
entity aps6404l_wb is
|
|
port(
|
|
clk : in std_logic;
|
|
rst : in std_logic; --
|
|
|
|
-- Instruction Wishbone IF (32b read access only, caches one instruction ahead)
|
|
wbi_o : out wishbone_slave_out; -- Instruction Wishbone bus (out)
|
|
wbi_i : in wishbone_slave_in; -- Instruction Wishbone bus (in)
|
|
|
|
-- Wishbone IF
|
|
wb_o : out wishbone_slave_out; -- Wishbone bus (out)
|
|
wb_i : in wishbone_slave_in; -- Wishbone bus (in)
|
|
|
|
-- PSRAM IF
|
|
psram_ce_n : out std_logic;
|
|
psram_sclk : out std_logic;
|
|
psram_sio : inout std_logic_vector(3 downto 0) --
|
|
);
|
|
end entity aps6404l_wb;
|
|
|
|
architecture rtl of aps6404l_wb is
|
|
type apsc_in is record
|
|
addr : std_logic_vector(23 downto 0);
|
|
data_in : std_logic_vector(7 downto 0);
|
|
write_en : std_logic;
|
|
read_en : std_logic;
|
|
end record apsc_in;
|
|
|
|
type apsc_out is record
|
|
data_in_next : std_logic;
|
|
data_out : std_logic_vector(7 downto 0);
|
|
data_out_valid : std_logic;
|
|
end record apsc_out;
|
|
|
|
signal apsc_i : apsc_in;
|
|
signal apsc_o : apsc_out;
|
|
|
|
signal cached_address : std_logic_vector(apsc_i.addr'range);
|
|
signal cached_data : wishbone_data;
|
|
signal api_i : apsc_in;
|
|
signal api_o : apsc_out;
|
|
signal api_request_count : integer range 0 to 2;
|
|
signal api_bit_cnt : integer range 0 to 3;
|
|
|
|
signal apd_i : apsc_in;
|
|
signal apd_o : apsc_out;
|
|
signal bit_cnt : integer range 0 to 4;
|
|
signal data : std_logic_vector(wb_i.dat'range);
|
|
|
|
signal ap_sel : integer range 0 to 1;
|
|
|
|
begin
|
|
aps6404l_controller_inst : entity work.aps6404l_controller
|
|
port map(
|
|
clk => clk,
|
|
rst => rst,
|
|
addr => apsc_i.addr,
|
|
data_in => apsc_i.data_in,
|
|
data_in_next => apsc_o.data_in_next,
|
|
data_out => apsc_o.data_out,
|
|
data_out_valid => apsc_o.data_out_valid,
|
|
write_en => apsc_i.write_en,
|
|
read_en => apsc_i.read_en,
|
|
psram_ce_n => psram_ce_n,
|
|
psram_sclk => psram_sclk,
|
|
psram_sio => psram_sio
|
|
);
|
|
|
|
ap_arb : process(clk, rst) is
|
|
begin
|
|
if rst then
|
|
ap_sel <= 0;
|
|
|
|
elsif rising_edge(clk) then
|
|
if ap_sel = 0 then
|
|
if not (api_i.read_en or api_i.read_en) and (apd_i.read_en or apd_i.write_en) then
|
|
ap_sel <= 1;
|
|
end if;
|
|
else
|
|
if (apd_i.read_en nor apd_i.write_en) then -- lock until APD releases
|
|
ap_sel <= 0;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process ap_arb;
|
|
|
|
ap_mux : process(all) is
|
|
constant APSC_IN_DEFAULT : apsc_in := (
|
|
addr => (others => '-'),
|
|
data_in => (others => '-'),
|
|
write_en => '0',
|
|
read_en => '0'
|
|
);
|
|
constant APSC_OUT_DEFAULT : apsc_out := (
|
|
data_in_next => '0',
|
|
data_out => (others => '-'),
|
|
data_out_valid => '0'
|
|
);
|
|
begin
|
|
apsc_i <= APSC_IN_DEFAULT;
|
|
api_o <= APSC_OUT_DEFAULT;
|
|
apd_o <= APSC_OUT_DEFAULT;
|
|
|
|
if ap_sel = 0 then
|
|
api_o <= apsc_o;
|
|
apsc_i <= api_i;
|
|
else
|
|
apd_o <= apsc_o;
|
|
apsc_i <= apd_i;
|
|
end if;
|
|
end process ap_mux;
|
|
|
|
instruction_memory_p : process(clk, rst) is
|
|
begin
|
|
if rst then
|
|
wbi_o.ack <= '0';
|
|
cached_address <= (others => '1'); -- TODO: Set cached_address to 0xFFFFFF
|
|
api_request_count <= 0;
|
|
|
|
elsif rising_edge(clk) then
|
|
wbi_o.ack <= '0';
|
|
|
|
if wbi_i.cyc and wbi_i.stb then
|
|
if wbi_i.adr(cached_address'range) = cached_address then -- cache hit, return current immediately, retrieve next in meantime
|
|
wbi_o.dat <= cached_data;
|
|
wbi_o.ack <= '1';
|
|
|
|
api_i.addr <= std_logic_vector(unsigned(wbi_i.adr(apsc_i.addr'range)) + 4);
|
|
if not api_i.read_en then -- If we haven't yet triggered the retrieve from memory, start it now
|
|
api_request_count <= 1;
|
|
end if;
|
|
else -- cache miss, retrieve current (return asap) + next
|
|
if not api_i.read_en then -- If we haven't yet triggered the retrieve from memory, start it now
|
|
api_i.addr <= wbi_i.adr(apsc_i.addr'range);
|
|
api_request_count <= 2;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
if api_o.data_out_valid then
|
|
cached_data <= api_o.data_out & cached_data(cached_data'high downto 8); -- TODO: this assumes the CPU quickly processes the ACK (within one SPI byte)
|
|
|
|
if api_bit_cnt = 3 then
|
|
cached_address <= api_i.addr;
|
|
if api_request_count > 0 then
|
|
api_request_count <= api_request_count - 1;
|
|
end if;
|
|
api_bit_cnt <= 0;
|
|
else
|
|
api_bit_cnt <= api_bit_cnt + 1;
|
|
end if;
|
|
end if;
|
|
if not api_i.read_en then
|
|
api_bit_cnt <= 0;
|
|
end if;
|
|
end if;
|
|
end process instruction_memory_p;
|
|
api_i.read_en <= '1' when api_request_count > 0 else '0';
|
|
api_i.write_en <= '0';
|
|
wbi_o.err <= '0';
|
|
wbi_o.rty <= '0';
|
|
wbi_o.stall <= not wbi_o.ack;
|
|
|
|
main : process(clk, rst) is
|
|
variable start_addr : integer range 0 to 3;
|
|
variable stop_addr : integer range 0 to 3;
|
|
variable success : std_logic;
|
|
variable we : std_logic;
|
|
begin
|
|
if rst then
|
|
wb_o.ack <= '0';
|
|
bit_cnt <= 0;
|
|
apd_i.read_en <= '0';
|
|
apd_i.write_en <= '0';
|
|
|
|
elsif rising_edge(clk) then
|
|
wb_o.ack <= '0';
|
|
|
|
if bit_cnt = 0 then
|
|
apd_i.read_en <= '0';
|
|
apd_i.write_en <= '0';
|
|
|
|
start_addr := 0;
|
|
for i in 0 to 3 loop
|
|
if wb_i.sel(i) = '1' then
|
|
start_addr := i;
|
|
exit;
|
|
end if;
|
|
end loop;
|
|
stop_addr := 0;
|
|
for i in 3 downto 0 loop
|
|
if wb_i.sel(i) = '1' then
|
|
stop_addr := i;
|
|
exit;
|
|
end if;
|
|
end loop;
|
|
|
|
apd_i.addr <= wb_i.adr(apsc_i.addr'length - 1 downto 2) & std_logic_vector(to_unsigned(start_addr, 2));
|
|
data <= wb_i.dat;
|
|
|
|
we := wb_i.we;
|
|
if not wb_o.ack and wb_i.stb and wb_i.cyc then
|
|
bit_cnt <= 4;
|
|
if wb_i.we then
|
|
wb_o.ack <= '1'; -- We can ack a write immediately
|
|
end if;
|
|
end if;
|
|
else
|
|
success := '0';
|
|
|
|
if bit_cnt >= (4 - stop_addr) and bit_cnt <= (4 - start_addr) then
|
|
apd_i.read_en <= not we;
|
|
apd_i.write_en <= we;
|
|
else
|
|
apd_i.read_en <= '0';
|
|
apd_i.write_en <= '0';
|
|
success := '1';
|
|
end if;
|
|
|
|
if we and apd_o.data_in_next then
|
|
success := '1';
|
|
elsif not we and apd_o.data_out_valid then
|
|
success := '1';
|
|
end if;
|
|
|
|
if success then
|
|
bit_cnt <= bit_cnt - 1;
|
|
wb_o.dat <= apd_o.data_out & wb_o.dat(wb_o.dat'high downto 8);
|
|
data <= x"--" & data(data'high downto 8);
|
|
if (bit_cnt = 1) and (we = '0') then -- We only need to ack reads, writes were already acked in bit_cnt = 0
|
|
wb_o.ack <= '1';
|
|
end if;
|
|
end if;
|
|
end if;
|
|
|
|
end if;
|
|
end process main;
|
|
apd_i.data_in <= data(apsc_i.data_in'range);
|
|
|
|
wb_o.err <= '0';
|
|
wb_o.rty <= '0';
|
|
wb_o.stall <= not wb_o.ack;
|
|
|
|
end architecture rtl;
|