library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library std;
use std.textio.all;

entity sram is
	generic (
		OUTPUT_DIR   : string  := "./"
	);
	port (
		-- SRAM interface
		dq     : inout std_logic_vector(15 downto 0) := (others => 'Z');
		addr   : in	std_logic_vector(19 downto 0);
		lb_n   : in	std_logic;
		ub_n   : in	std_logic;
		ce_n   : in	std_logic;
		oe_n   : in	std_logic;
		we_n   : in	std_logic;

		-- on rising_edge: dump frame to file
		write_file : in std_logic;
		base_address : natural := 0;
		width        : natural := 400;
		height       : natural := 240
	);
end entity;

architecture arch of sram is

	-- 2 MB data = 2 * 1024^2 B = 2 * 1024^6 * 8 bit = 16777216 bit
	-- = 16777216 / 16 * 16 bit-wide = 1048576 bit * 16 bit-wide
	-- = 2^log_2(1048576) bit * 16 bit-wide
	-- = 2^20 bit * 16 bit-wide
	type data_t is array (2**20 downto 0) of std_logic_vector(15 downto 0);

	signal data : data_t := (others => (others => '0'));

	file dump_file : text;
begin

	write_ram : process(we_n, ce_n, lb_n, ub_n)
	begin
		-- taken straight out of the datasheet truth table
		if we_n = '0' and ce_n = '0' and lb_n = '0' then
			-- lower bytes 0-7
			data(to_integer(unsigned(addr)))(7 downto 0) <= dq(7 downto 0);
		end if;
		if we_n = '0' and ce_n = '0' and ub_n = '0' then
			-- higher bytes 8-15
			data(to_integer(unsigned(addr)))(15 downto 8) <= dq(15 downto 8);
		end if;
	end process;

	read_ram : process(we_n, ce_n, oe_n, lb_n, ub_n)
	begin
		-- high impedance
		if we_n = '1' and ce_n = '0' then
			dq <= (others => 'Z');
		end if;
		-- taken straight out of the datasheet truth table
		if we_n = '1' and ce_n = '0' and oe_n = '0' and lb_n = '0' then
			-- lower bytes 0-7
			dq(7 downto 0) <= data(to_integer(unsigned(addr)))(7 downto 0);
		end if;
		if we_n = '1' and ce_n = '0' and oe_n = '0' and ub_n = '0' then
			-- lower bytes 8-15
			dq(15 downto 8) <= data(to_integer(unsigned(addr)))(15 downto 8);
		end if;
	end process;

	dump : process(write_file)
		variable l : line;
		variable count : integer := 0;
	begin
		if rising_edge(write_file) then
			-- init file IO
			file_open(dump_file, OUTPUT_DIR & "sram_dump_" & to_string(count) & ".ppm", write_mode);
			report "opened file @ " & OUTPUT_DIR & "sram_dump_" & to_string(count) & ".ppm";
			count := count + 1;

			-- header
			write(l, string'("P3"));
			writeline(dump_file, l);
			write(l, to_string(width) & " " & to_string(height));
			writeline(dump_file, l);
			-- 0 to 63 ~ 2^6
			write(l, string'("63"));
			writeline(dump_file, l);

			-- body
			for y in 0 to height - 1 loop
				for x in 0 to width - 1 loop
					-- because r and b have one bit less, they need to be scaled
					-- write r bits (5)
					write(l, 2 * to_integer(unsigned(data(base_address + y * width + x)(15 downto 11))));
					write(l, string'(" "));
					-- write g bits (6)
					write(l,     to_integer(unsigned(data(base_address + y * width + x)(10 downto 5))));
					write(l, string'(" "));
					-- write b bits (5)
					write(l, 2 * to_integer(unsigned(data(base_address + y * width + x)(4 downto 0))));
					write(l, string'(" "));
				end loop;
				writeline(dump_file, l);
			end loop;

			file_close(dump_file);
			report "closed file";
		end if;
	end process;

end architecture;
