-- See the file "LICENSE" for the full license governing this code. -- library ieee; use std.textio.all; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use work.lt16x32_global.all; -- the memory handles all memory transactions from the processor -- it could be extended to have bus accesses to communicate to the outside world. -- this is currently possible via port out_byte, which can be written to at address 0x04000000. -- also, artificial latency can be inserted for simulation purposes. entity memory is generic( -- name of file for initialization, formatted as lines of 32 ones or zeroes, each describing one word. filename : in string := "program.ram"; -- size in words (32bit) size : in integer := 256; -- latency of the instruction memory interface imem_latency : in time := 5 ns; -- latency of the data memory interface dmem_latency : in time := 5 ns ); port( -- clock signal clk : in std_logic; -- reset signal, active high, synchronous rst : in std_logic; -- dmem signals from the processor in_dmem : in core_dmem; -- dmem signals to the processor out_dmem : out dmem_core; -- imem signals from the processor in_imem : in core_imem; -- imem signals to the processor out_imem : out imem_core; -- fault signal, active high fault : out std_logic; -- out_byte port to access "outer world" out_byte : out std_logic_vector(7 downto 0)); end entity memory; architecture RTL of memory is constant width : integer := 32; -- construct to have in_simulation and in_synthesis for generates in this module constant in_simulation : boolean := false -- synthesis translate_off or true -- synthesis translate_on ; constant in_synthesis : boolean := not in_simulation; -- type for the memory signal type memory_type is array (0 to size - 1) of std_logic_vector(width - 1 downto 0); -- this function initializes an array of memory_type with the contents of a given file -- function from http://myfpgablog.blogspot.de/2011/12/memory-initialization-methods.html -- and from http://www.stefanvhdl.com/vhdl/html/file_read.html -- and from http://www.ee.sunysb.edu/~jochoa/vhd_writefile_tutorial.htm impure function init_mem(mif_file_name : in string) return memory_type is -- input file file mif_file : text open read_mode is mif_file_name; -- input file read line variable mif_line : line; -- temporary bit vector for data read from file variable temp_bv : bit_vector(width - 1 downto 0); -- temporary memory array variable temp_mem : memory_type; -- read function success value variable good : boolean; begin for i in memory_type'range loop if not endfile(mif_file) then -- if no end of input file, read next line readline(mif_file, mif_line); -- match line into bit vector read(mif_line, temp_bv, good); -- synthesis translate_off assert good report ("Non-good word in memory location " & integer'image(i)) severity warning; --! give a warning when readline is no good -TF 2014-05-20 -- synthesis translate_on -- copy temporary bit vector into temporary memory array temp_mem(i) := to_stdlogicvector(temp_bv); else -- EOF but memory not yet full, fill up with zeros temp_mem(i) := (others => '0'); end if; end loop; -- check if program fit into memory if not endfile(mif_file) then assert false report "memory not large enough for loaded program." severity failure; end if; -- give back filled array return temp_mem; end function; -- memory array signal memory : memory_type := init_mem(filename); -- internal data signals for use with ready signals in simulation signal imem_data : std_logic_vector(width - 1 downto 0); signal dmem_data : std_logic_vector(width - 1 downto 0); -- fault signal for dmem read fault signal dmem_read_fault : std_logic; -- fault signal for dmem write fault signal dmem_write_fault : std_logic; -- fault signal for imem read faults signal imem_read_fault : std_logic; begin -- fault logic fault <= dmem_read_fault or dmem_write_fault or imem_read_fault; -- imem read imem_read : process(clk) is -- calculated word address variable wordaddress : integer; begin if (rising_edge(clk)) then if (rst = '1') then -- in reset zero output and no fault imem_data <= (others => '0'); imem_read_fault <= '0'; elsif (in_imem.read_en = '1') then -- if read enabled -- standard output imem_read_fault <= '0'; -- word address calculation wordaddress := to_integer(unsigned(in_imem.read_addr(in_imem.read_addr'high downto 2))); -- always 32bit aligned -- read data if (wordaddress < size) then -- always return full word imem_data <= memory(wordaddress)(width - 1 downto 0); else -- memory access out of bounds imem_read_fault <= '1'; imem_data <= (others => 'X'); end if; end if; end if; end process imem_read; -- dmem read dmem_read : process(clk) is -- calculated word address variable wordaddress : integer; -- calculated byte address inside of word variable byteaddress : std_logic_vector(1 downto 0); -- read full word variable word : std_logic_vector(width - 1 downto 0); begin if (rising_edge(clk)) then if (rst = '1') then -- in reset zero output and no fault dmem_data <= (others => '0'); dmem_read_fault <= '0'; else -- standard output dmem_read_fault <= '0'; if (in_dmem.read_en = '1') then -- if read enabled, otherwise keep up old data -- calculate word address and address of byte inside of word wordaddress := to_integer(unsigned(in_dmem.read_addr(in_dmem.read_addr'high downto 2))); byteaddress := in_dmem.read_addr(1 downto 0); if (wordaddress = 0) then dmem_data <= (others => '0'); elsif (wordaddress < size) then -- in memory range -- read word from memory array word := memory(wordaddress); -- get correct bits from full word case in_dmem.read_size is when "00" => -- byte access -- clear non-byte bits dmem_data(width - 1 downto 8) <= (others => '0'); -- fill last byte of output word with read data case byteaddress is when "00" => dmem_data(7 downto 0) <= word(7 downto 0); when "01" => dmem_data(7 downto 0) <= word(15 downto 8); when "10" => dmem_data(7 downto 0) <= word(23 downto 16); when "11" => dmem_data(7 downto 0) <= word(31 downto 24); when others => -- will not happen in synthesis, but might in simulation dmem_data(7 downto 0) <= (others => 'X'); end case; when "01" => -- halfword access -- clear non-halfword bits dmem_data(width - 1 downto 16) <= (others => '0'); -- fill last halfword of output word with read data case byteaddress is when "00" => dmem_data(15 downto 0) <= word(15 downto 0); when "01" => dmem_data(15 downto 0) <= word(23 downto 8); when "10" => dmem_data(15 downto 0) <= word(31 downto 16); when others => -- memory access exceeds word boundaries dmem_read_fault <= '1'; -- synthesis translate_off assert false report "memory access exceeds word boundaries (16bit dmem read at " & integer'image(to_integer(unsigned(in_dmem.read_addr))) & ")" severity error; -- synthesis translate_on end case; when "10" => -- word access -- fill all bits of output word with read data if (byteaddress = "00") then dmem_data <= word; else -- memory access exceeds word boundaries dmem_read_fault <= '1'; -- synthesis translate_off assert false report "memory access exceeds word boundaries (32bit dmem read at " & integer'image(to_integer(unsigned(in_dmem.read_addr))) & ")" severity error; -- synthesis translate_on end if; when others => -- memory size not implemented dmem_read_fault <= '1'; -- synthesis translate_off assert false report "memory size not implemented" severity error; -- synthesis translate_on end case; else -- (wordaddress >= size) -- memory access out of bounds dmem_read_fault <= '1'; dmem_data <= (others => 'X'); -- synthesis translate_off assert false report "memory access out of bounds (dmem read at " & integer'image(to_integer(unsigned(in_dmem.read_addr))) & ")" severity error; -- synthesis translate_on end if; end if; end if; end if; end process dmem_read; -- dmem write dmem_write : process(clk) is -- calculated word address variable wordaddress : integer; -- calculated byte address inside of word variable byteaddress : std_logic_vector(1 downto 0); -- read full word variable word : std_logic_vector(width - 1 downto 0); begin if rising_edge(clk) then if rst = '1' then dmem_write_fault <= '0'; out_byte <= (others => '0'); elsif (in_dmem.write_en = '1') then dmem_write_fault <= '0'; wordaddress := to_integer(unsigned(in_dmem.write_addr(in_dmem.write_addr'high downto 2))); byteaddress := in_dmem.write_addr(1 downto 0); if (wordaddress = 0) then out_byte <= in_dmem.write_data(7 downto 0); elsif (wordaddress < size) then -- in memory range and no special word -- read old word word := memory(wordaddress)(31 downto 0); -- modify word case in_dmem.write_size is when "00" => -- byte access case byteaddress is when "00" => word(7 downto 0) := in_dmem.write_data(7 downto 0); when "01" => --word(15 downto 8) := in_dmem.write_data(7 downto 0); word(15 downto 8) := in_dmem.write_data(15 downto 8); when "10" => --word(23 downto 16) := in_dmem.write_data(7 downto 0); word(23 downto 16) := in_dmem.write_data(23 downto 16); when "11" => --word(31 downto 24) := in_dmem.write_data(7 downto 0); word(31 downto 24) := in_dmem.write_data(31 downto 24); when others => -- will not happen in synthesis, but might in simulation word(7 downto 0) := (others => 'X'); end case; when "01" => -- halfword access case byteaddress is when "00" => word(15 downto 0) := in_dmem.write_data(15 downto 0); when "01" => --word(23 downto 8) := in_dmem.write_data(15 downto 0); word(23 downto 8) := in_dmem.write_data(23 downto 8); when "10" => --word(31 downto 16) := in_dmem.write_data(15 downto 0); word(31 downto 16) := in_dmem.write_data(31 downto 16); when others => -- memory access exceeds word boundaries dmem_write_fault <= '1'; -- synthesis translate_off assert false report "memory access exceeds word boundaries (16bit dmem write at " & integer'image(to_integer(unsigned(in_dmem.write_addr))) & ")" severity error; -- synthesis translate_on end case; when "10" => if (byteaddress = "00") then word := in_dmem.write_data; else -- memory access exceeds word boundaries dmem_write_fault <= '1'; -- synthesis translate_off assert false report "memory access exceeds word boundaries (32bit dmem write at " & integer'image(to_integer(unsigned(in_dmem.write_addr))) & ")" severity error; -- synthesis translate_on end if; when others => -- memory size not implemented dmem_write_fault <= '1'; -- synthesis translate_off assert false report "memory size not implemented" severity error; -- synthesis translate_on end case; memory(wordaddress) <= word; else dmem_write_fault <= '1'; -- synthesis translate_off assert false report "memory access out of bounds (dmem write at " & integer'image(to_integer(unsigned(in_dmem.write_addr))) & ")" severity error; -- synthesis translate_on end if; end if; end if; end process dmem_write; -- in synthesis, data is always valid in next clock cycle synthesis_only : if (in_synthesis) generate out_imem.read_data <= imem_data; out_imem.ready <= '1'; out_dmem.read_data <= dmem_data; out_dmem.ready <= '1'; end generate synthesis_only; -- add latency in simulation only -- synthesis translate_off simulation_only : if (in_simulation) generate -- instruction memory latency imem_delay : process is begin wait until imem_data'event; -- wait artificial delay if (imem_latency > 0 ns) then -- show not ready-yet data as XX out_imem.ready <= '0'; out_imem.read_data <= (others => 'X'); wait for imem_latency; end if; -- output data out_imem.read_data <= imem_data; -- now we're ready out_imem.ready <= '1'; end process imem_delay; -- data memory latency dmem_delay : process is begin wait until dmem_data'event; -- wait artificial delay if (dmem_latency > 0 ns) then -- show not ready-yet data as XX out_dmem.ready <= '0'; out_dmem.read_data <= (others => 'X'); wait for dmem_latency; end if; -- output data out_dmem.read_data <= dmem_data; -- now we're ready out_dmem.ready <= '1'; end process dmem_delay; end generate simulation_only; -- synthesis translate_on end architecture RTL;