-- -------------------------------------------------------------------------- -- -- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs -- -- -------------------------------------------------------------------------- -- -- TODO -- -------------------------------------------------------------------------- -- -- Author : Markus Koch -- 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;