trashernet-soc/fpga/hdl/design/aps6404l_wb.vhd

264 lines
7.3 KiB
VHDL
Raw Normal View History

2024-06-12 20:03:04 +02:00
-- -------------------------------------------------------------------------- --
-- 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.
2024-06-12 20:03:04 +02:00
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)
2024-06-12 20:03:04 +02:00
-- 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;
2024-06-12 20:03:04 +02:00
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;
2024-06-12 20:03:04 +02:00
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;
2024-06-12 20:03:04 +02:00
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,
2024-06-12 20:03:04 +02:00
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;
2024-06-12 20:03:04 +02:00
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;
2024-06-12 20:03:04 +02:00
begin
if rst then
wb_o.ack <= '0';
bit_cnt <= 0;
apd_i.read_en <= '0';
apd_i.write_en <= '0';
2024-06-12 20:03:04 +02:00
elsif rising_edge(clk) then
wb_o.ack <= '0';
if bit_cnt = 0 then
apd_i.read_en <= '0';
apd_i.write_en <= '0';
2024-06-12 20:03:04 +02:00
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;
2024-06-12 20:03:04 +02:00
we := wb_i.we;
2024-06-12 20:03:04 +02:00
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;
2024-06-12 20:03:04 +02:00
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;
2024-06-12 20:03:04 +02:00
else
apd_i.read_en <= '0';
apd_i.write_en <= '0';
success := '1';
2024-06-12 20:03:04 +02:00
end if;
if we and apd_o.data_in_next then
2024-06-12 20:03:04 +02:00
success := '1';
elsif not we and apd_o.data_out_valid then
2024-06-12 20:03:04 +02:00
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);
2024-06-12 20:03:04 +02:00
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
2024-06-12 20:03:04 +02:00
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);
2024-06-12 20:03:04 +02:00
wb_o.err <= '0';
wb_o.rty <= '0';
wb_o.stall <= not wb_o.ack;
end architecture rtl;