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.write_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;
 |