--------------------------------------------------------------------------------
-- Company: 
-- Engineer:
--
-- Create Date:    11:25:45 10/15/05
-- Design Name:    
-- Module Name:    vga_backend - Behavioral
-- Project Name:   
-- Target Device:  
-- Tool versions:  
-- Description:
--
-- Dependencies:
-- 
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
-- 
--------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

use work.utils_pkg.all;
use work.vga_types.all;

entity vga_frontend is
	Generic
	(
		fifo_depth					: integer    		:= 2048;
		fifo_almost_full_thresh		: natural 			:= 2000;
		fifo_almost_empty_thresh	: natural 			:= 48;
		tsvga						: vga_timespec_t	:= ts_vga_800_600_72
	);
	Port
	(
		-- System signals
		RST_I			: in  STD_LOGIC;
		CLK_I			: in  STD_LOGIC;

		-- JBUS Master signals
		CYC_O			: out STD_LOGIC;
		STB_O			: out STD_LOGIC;
		WE_O			: out STD_LOGIC;
		SEL_O			: out unsigned(3 downto 0);
		ACK_I			: in  STD_LOGIC;
		SRDY_I			: in  STD_LOGIC;
		MRDY_O			: out STD_LOGIC;
		ADDR_O			: out unsigned(31 downto 0);
		MDAT_I			: in  unsigned(31 downto 0);
		MDAT_O			: out unsigned(31 downto 0);
		
		-- JBUS Slave signals
		CYC_I			: in  STD_LOGIC;
		STB_I			: in  STD_LOGIC;
		WE_I			: in  STD_LOGIC;
		SEL_I			: in  unsigned(3 downto 0);
		ACK_O			: out STD_LOGIC;
		SRDY_O			: out STD_LOGIC;
		MRDY_I			: in  STD_LOGIC;
		ADDR_I			: in  unsigned(31 downto 0);
		SDAT_I			: in  unsigned(31 downto 0);
		SDAT_O			: out unsigned(31 downto 0);
		
		-- VGA signals
		vga_clk_in		: in  std_logic;
		vga_clk_ce		: in  std_logic;
		vga_red			: out unsigned(7 downto 0);
		vga_green		: out unsigned(7 downto 0);
		vga_blue		: out unsigned(7 downto 0);
		vga_blank_n		: out std_logic;
		vga_sync_n		: out std_logic;
		vga_hsync		: out std_logic;
		vga_vsync		: out std_logic
	);
end vga_frontend;

architecture Behavioral of vga_frontend is

	signal vga_clk				: std_logic;

	-- Color FIFO signals
	signal fifo_full			: std_logic;
	signal fifo_empty			: std_logic;
	signal fifo_almost_empty	: std_logic;
	signal fifo_almost_full		: std_logic;
	signal fifo_color_re		: std_logic;
	signal fifo_color_we		: std_logic;
	signal fifo_color_out		: unsigned(31 downto 0);
	signal fifo_color_in		: unsigned(31 downto 0);

	--	Backend signals
	signal scan_rdy				: std_logic;
	signal ctrl_scan_en			: std_logic;
	signal color_in				: color_array_t;
	signal color_op				: color_op_t;
	signal color_en				: unsigned (VGA_NUM_CH-1 downto 0);

	signal stat_vga_rdy			: std_logic;
	signal stat_hready			: std_logic;
	signal stat_vready			: std_logic;
	signal stat_scan			: std_logic;
	signal stat_hscan			: std_logic;
	signal stat_vscan			: std_logic;
	signal stat_hsync			: std_logic;
	signal stat_vsync			: std_logic;
	signal stat_hpos			: natural range 0 to tsvga.ts_h.ncyc_scan-1;
	signal stat_vpos			: natural range 0 to tsvga.ts_v.ncyc_scan-1;

	--	CG signals
	signal cg_en				: std_logic;
	signal cg_rdy				: std_logic;
	signal cg_draw				: std_logic;
	signal cg_clr_screen		: std_logic;
	signal cg_clr_line			: std_logic;
	signal cg_din				: unsigned(7 downto 0);
	signal cg_dout				: unsigned(7 downto 0);
	signal cg_ascii_we			: std_logic;
	signal cg_posy_we			: std_logic;
	signal cg_posx_we			: std_logic;
	signal cg_color				: color_t;
	signal cg_csr_pos_x			: natural range 0 to 80-1;
	signal cg_csr_pos_y			: natural range 0 to 32-1;

	-- Bus master signals
	constant MAX_REQUEST_CNT	: natural := tsvga.ts_h.ncyc_scan*tsvga.ts_v.ncyc_scan;

	signal vga_mem_offset		: unsigned(31 downto 0);
	signal vga_scan_rdy			: std_logic;
	signal vga_master_en		: std_logic;
	signal request_cnt_rst		: std_logic;
	signal request_cnt_en		: std_logic;
	signal pixel_cnt_rst		: std_logic;
	signal read_bus_en			: std_logic;
	signal request_cnt			: natural range 0 to MAX_REQUEST_CNT-1;
	signal read_cnt				: natural range 0 to MAX_REQUEST_CNT-1;

	type vm_state_t is (init, idle, rdy, bus_request, bus_access, bus_data);
	signal s, sn : vm_state_t;

begin

inst_vga_backend : entity work.vga_backend
	GENERIC MAP
	(
		tsvga						=> tsvga
	)
	PORT MAP
	(
		-- System signals
		sys_rst			=> RST_I,
		sys_clk			=> CLK_I,
		
		-- Color channels
		color_in		=> color_in,
		color_op		=> color_op,
		color_en		=> color_en,

		-- Control signals
		ctrl_scan_en	=> ctrl_scan_en,

		-- Status signals
		stat_vga_rdy	=> stat_vga_rdy,
		stat_scan		=> stat_scan,
		stat_hsync		=> stat_hsync,
		stat_vsync		=> stat_vsync,
		stat_hready		=> stat_hready,
		stat_vready		=> stat_vready,
		stat_hscan		=> stat_hscan,
		stat_vscan		=> stat_vscan,
		stat_hpos		=> stat_hpos,
		stat_vpos		=> stat_vpos,
		
		-- VGA domain signals
		vga_clk_in		=> vga_clk_in,
		vga_clk_ce		=> vga_clk_ce,
		vga_red			=> vga_red,
		vga_green		=> vga_green,
		vga_blue		=> vga_blue,
		vga_blank_n		=> vga_blank_n,
		vga_sync_n		=> vga_sync_n,
		vga_hsync		=> vga_hsync,
		vga_vsync		=> vga_vsync
	);

	ctrl_scan_en	<= '1';
	color_en(0)		<= not fifo_empty;
	color_en(1)		<= cg_draw;
	color_in(0)		<= to_color_t(fifo_color_out);
	color_in(1)		<= cg_color; --(X"FF", X"FF", X"FF", X"FF");	-- white opaque
	color_op		<= op_over;
	vga_scan_rdy	<= stat_hready and stat_vready;
	
inst_linefifo: entity work.fifo_async
	GENERIC MAP 
	(
		addr_width			=> NextExpBaseTwo(fifo_depth),
		data_width			=> 32,
		almost_full_thresh	=> fifo_almost_full_thresh,
		almost_empty_thresh	=> fifo_almost_empty_thresh
	)
	PORT MAP
	(
		rst				=> RST_I,
		clk_w			=> CLK_I,
		clk_r			=> vga_clk_in,
		we				=> fifo_color_we,
		re				=> fifo_color_re,
		data_w			=> fifo_color_in,
		data_r			=> fifo_color_out,
		fifo_full		=> fifo_full,
		fifo_empty		=> fifo_empty,
		fifo_afull		=> fifo_almost_full,
		fifo_aempty		=> fifo_almost_empty
		
	);

	fifo_color_in		<= MDAT_I;
	fifo_color_re		<= stat_scan;
	fifo_color_we		<= ACK_I and read_bus_en;		-- TODO:
	MRDY_O				<= not fifo_full;
	SEL_O				<= (others => '0');
	WE_O				<= '0';
	
vga_master_next:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			if RST_I = '1' then
				s	<= init;
			else
				s	<= sn;
			end if;
		end if;
	end process;	

vga_master:
	process(s, stat_vga_rdy, vga_master_en, vga_scan_rdy, fifo_almost_empty, fifo_almost_full, SRDY_I, ACK_I, read_cnt, request_cnt)
	begin
	
	CYC_O			<= '0';
	STB_O			<= '0';
	pixel_cnt_rst	<= '0';
	request_cnt_rst	<= '0';
	request_cnt_en	<= '0';
	read_bus_en		<= '0';
	sn				<= s;
	
	case s is
		when init =>
		if stat_vga_rdy = '1' then
			sn	<= idle;
		end if;
		
		when idle =>
		pixel_cnt_rst	<= '1';
		if vga_master_en = '1' and vga_scan_rdy = '1' then
			sn	<= rdy;
		end if;
		
		when rdy =>
--		pixel_cnt_rst	<= '1';
		if fifo_almost_empty = '1' then
			sn	<= bus_request;
		end if;
		
		when bus_request =>
			CYC_O			<= '1';
			request_cnt_rst	<= '1';
			if SRDY_I = '1' then
				sn	<= bus_access;
			end if;
			
		when bus_access =>
			CYC_O			<= '1';
			STB_O			<= '1';
			request_cnt_en	<= '1';
			read_bus_en		<= '1';
			if fifo_almost_full = '1' then
				sn <= bus_data;
			end if;	
		when bus_data =>
			CYC_O		<= '1';
			read_bus_en	<= '1';
			if read_cnt = request_cnt then
				sn	<= rdy;
				if vga_master_en = '0' then
					sn	<= idle;
				end if;	
			end if;	
		when others =>
			sn	<= idle;
		
		end case;
	end process;
	
request_counter:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			if request_cnt_rst = '1' then
				request_cnt	<= 0;
			elsif request_cnt_en = '1' and SRDY_I = '1' then
				if request_cnt /= MAX_REQUEST_CNT-1 then
					request_cnt <= request_cnt + 1;
				end if;
			end if;	
		end if;
	end process;	
		
read_counter:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			if request_cnt_rst = '1' then
				read_cnt	<= 0;
			elsif read_bus_en = '1' and ACK_I = '1' then
				if read_cnt /= MAX_REQUEST_CNT-1 then
					read_cnt <= read_cnt + 1;
				end if;
			end if;	
		end if;
	end process;	

pixel_counter:
	process(CLK_I)
	variable pixel_cnt	: natural range 0 to MAX_REQUEST_CNT-1;
	begin
		if rising_edge(CLK_I) then
			if pixel_cnt_rst = '1' then
				pixel_cnt	:= 0;
			elsif request_cnt_en = '1' and SRDY_I = '1' then
				if pixel_cnt /= MAX_REQUEST_CNT-1 then
					pixel_cnt 	:= pixel_cnt + 1;
				else
					pixel_cnt	:= 0;
				end if;	
 			end if;	
 			ADDR_O	<= vga_mem_offset + (to_unsigned(pixel_cnt, 30) & "00");
		end if;
	end process;	
	
inst_char_gen : entity work.char_gen
	GENERIC MAP
	(
		tsvga				=> tsvga,
		CHARACTER_SCALE		=> 1,
		CHAR_ROM_SIZE_X		=> 8,	-- Pixels
		CHAR_ROM_SIZE_Y		=> 16,	-- Pixels
		CHAR_ROM_DEPTH		=> 256,	-- Chars
		MAX_CHAR_PER_LINE	=> 80,	-- Chars
		MAX_LINE_PER_FRAME	=> 32,	-- Chars
		CHAR_SPACING_X		=> 0, 	-- Pixels
		CHAR_SPACING_Y		=> 0,	-- Pixels
		SCREEN_OFFSET_X		=> 16,	-- Pixels
		SCREEN_OFFSET_Y		=> 16	-- Pixels
	)		
	PORT MAP
	(
	    rst				=> RST_I,
	    sys_clk			=> CLK_I,
	    vga_clk			=> vga_clk_in,
	    ce				=> cg_en,
	    frame_pulse		=> scan_rdy,
	    clr_screen		=> cg_clr_screen,
	    clr_line		=> cg_clr_line,
	    din				=> cg_din,
	    dout			=> cg_dout,
	    ascii_we		=> cg_ascii_we,
	    posy_we			=> cg_posy_we,
	    posx_we			=> cg_posx_we,
	    ready			=> cg_rdy,
	    scan_pos_x_in	=> stat_hpos,
	    scan_pos_y_in	=> stat_vpos,
	   	csr_pos_x_out	=> cg_csr_pos_x,
	   	csr_pos_y_out	=> cg_csr_pos_y,
	    char_draw_en	=> cg_draw
    );
	SRDY_O				<= CYC_I; -- and cg_rdy;
--	SDAT_O				<= X"0000000" & "000" & cg_rdy;
	MDAT_O				<= (others => '-');
	scan_rdy			<= stat_hready and stat_vready;
	cg_en				<= stat_scan;
	

registers_read: 
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			ACK_O			<= '0';
			if (STB_I and CYC_I) = '1' then
				ACK_O		<= not WE_I;
				SDAT_O		<= (others => '0');
				case ADDR_I(5 downto 2) is

				-- sys_vga_ctrl		0
				when "0000" =>
					SDAT_O(0)			<= cg_rdy;
					SDAT_O(1)			<= vga_master_en;
					
				-- sys_vga_ascii	4
				when "0001" =>
					SDAT_O(7 downto 0)	<= cg_dout;
					
				-- sys_vga_posx		8
				when "0010" =>
					SDAT_O(7 downto 0)	<= to_unsigned(cg_csr_pos_x, 8);

				-- sys_vga_posy		12
				when "0011" =>
					SDAT_O(7 downto 0)	<= to_unsigned(cg_csr_pos_y, 8);

				-- sys_vga_cgcol	16
				when "0100" =>
					SDAT_O				<= to_unsigned(cg_color);

				-- sys_vga_moffs	20
				when "0101" =>
					SDAT_O				<= vga_mem_offset;

				-- sys_vga_resx		24
				when "0110" =>
					SDAT_O(15 downto 0)	<= to_unsigned(tsvga.ts_h.ncyc_scan, 16);

				-- sys_vga_resy		28
				when "0111" =>
					SDAT_O(15 downto 0)	<= to_unsigned(tsvga.ts_v.ncyc_scan, 16);

				-- sys_vga_fps		32
				when "1000" =>
					SDAT_O(15 downto 0)	<= to_unsigned(tsvga.nfps, 16);

				when others => null;
				end case;
			end if;
		end if;	
	end process;


registers_write:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			cg_ascii_we	<= '0';
			cg_posy_we	<= '0';
			cg_posx_we	<= '0';
			cg_clr_screen		<= '0';
			cg_clr_line			<= '0';
			if RST_I = '1' then
				cg_din			<= (others => '0');
				vga_master_en	<= '0';
				vga_mem_offset	<= X"40000000";
				cg_color		<= (X"FF", X"FF", X"FF", X"FF");
			elsif (STB_I and CYC_I and WE_I) = '1' then
				case ADDR_I(5 downto 2) is
				
				-- sys_vga_ctrl		0
				when "0000" =>
					vga_master_en	<= SDAT_I(1);
					cg_clr_screen	<= SDAT_I(4);
					cg_clr_line		<= SDAT_I(5);
					
				-- sys_vga_ascii	4
				when "0001" =>
					cg_ascii_we		<= '1';
					cg_din			<= SDAT_I(7 downto 0);
					
				-- sys_vga_posx		8
				when "0010" =>
					cg_posx_we		<= '1';
					cg_din			<= SDAT_I(7 downto 0);

				-- sys_vga_posy		12
				when "0011" =>
					cg_posy_we		<= '1';
					cg_din			<= SDAT_I(7 downto 0);

				-- sys_vga_cgcol	16
				when "0100" =>
					cg_color		<= to_color_t(SDAT_I(31 downto 0));

				-- sys_vga_moffs	20
				when "0101" =>
					vga_mem_offset	<= SDAT_I(31 downto 2) & "00";
					
				when others => null;
				end case;

			end if;
		end if;
	end process;
	
	---------------------------------------------------------------------------------------------

end Behavioral;
