lw35-upgrade/display/fpga/design/lmg6202.vhd

190 lines
4.5 KiB
VHDL

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library ip;
use ip.all;
entity lmg6202 is
generic(
F_CLK : natural;
LCD_F_CLP : natural := 2_000_000 -- Max 2.5 MHz atm (because fo t_lc)
);
port(
-- System signals
clk : in std_logic;
rst : in std_logic; --
-- Interface signals
addr_in : in std_logic_vector(12 downto 0);
data_in : in std_logic_vector(8 downto 0);
data_we : in std_logic;
data_out : out std_logic_vector(8 downto 0);
vsync_out : out std_logic; -- Strobed when resetting to the top left
vsync_in : in std_logic; -- Reset renderer to the top left corner
-- LCD hardware signals
lcd_data : out std_logic_vector(3 downto 0);
lcd_clp : out std_logic;
lcd_load : out std_logic;
lcd_frp : out std_logic;
lcd_frmb : out std_logic
);
end entity lmg6202;
architecture RTL of lmg6202 is
constant DISPLAY_WIDTH_PX : natural := 480;
constant DISPLAY_HEIGHT_PX : natural := 128;
constant DISPLAY_WIDTH : natural := DISPLAY_WIDTH_PX / 4;
constant DISPLAY_HEIGHT : natural := DISPLAY_HEIGHT_PX;
-- CLP Gen
constant CLP_CNT_MAX : integer := F_CLK / LCD_F_CLP / 2 - 1;
signal clp_cnt : integer range 0 to CLP_CNT_MAX;
signal clp_falling : std_logic;
signal row : integer range 0 to DISPLAY_HEIGHT - 1;
signal col : integer range 0 to DISPLAY_WIDTH - 1;
signal renderer_addr : unsigned(12 downto 0);
signal renderer_data : std_logic_vector(8 downto 0);
signal additional_nibble : std_logic_vector(3 downto 0);
signal cached_nibble : std_logic_vector(3 downto 0);
signal clp_rising : std_logic;
signal step_cnt : unsigned(3 downto 0);
begin
gram0_inst : entity ip.gram0
port map(
DataInA => data_in,
DataInB => (others => '0'),
AddressA => addr_in,
AddressB => std_logic_vector(renderer_addr),
ClockA => clk,
ClockB => clk,
ClockEnA => '1',
ClockEnB => '1',
WrA => data_we,
WrB => '0',
ResetA => rst,
ResetB => rst,
QA => data_out,
QB => renderer_data
);
renderer : process(clk, rst) is
procedure reset_renderer is
begin
row <= 0;
col <= 0;
renderer_addr <= (others => '0');
step_cnt <= (others => '0');
vsync_out <= '1';
end procedure reset_renderer;
begin
if (rst = '1') then
additional_nibble <= (others => '0');
cached_nibble <= (others => '0');
lcd_load <= '0';
lcd_frp <= '0';
lcd_frmb <= '0';
lcd_data <= (others => '0');
reset_renderer;
elsif (rising_edge(clk)) then
vsync_out <= '0';
if (clp_falling = '1') then
if (col = DISPLAY_WIDTH - 1) then
col <= 0;
lcd_load <= '1';
if (row = 0) then
lcd_frp <= '1';
lcd_frmb <= not lcd_frmb;
else
lcd_frp <= '0';
end if;
if (row = DISPLAY_HEIGHT - 1) then
reset_renderer;
else
row <= row + 1;
end if;
else
col <= col + 1;
end if;
end if;
if (clp_rising = '1') then
lcd_load <= '0';
step_cnt <= step_cnt + 1;
if (step_cnt = 8) then
step_cnt <= (others => '0');
lcd_data <= additional_nibble;
elsif (step_cnt(0) = '0') then
renderer_addr <= renderer_addr + 1;
lcd_data <= renderer_data(8 downto 5);
cached_nibble <= renderer_data(4 downto 1);
additional_nibble <= additional_nibble(2 downto 0) & renderer_data(0);
else
lcd_data <= cached_nibble;
end if;
-- DEBUG BEGIN
-- lcd_data <= (others => '0');
-- if (col = 0) then
-- lcd_data <= x"8";
-- end if;
-- if (col = DISPLAY_WIDTH - 1) then
-- lcd_data <= x"1";
-- end if;
-- if (row = 0 or row = DISPLAY_HEIGHT - 1) then
-- lcd_data <= (others => '1');
-- end if;
-- DEBUG END
end if;
if (vsync_in = '1') then
reset_renderer;
end if;
end if;
end process renderer;
cpgen : process(clk, rst) is
begin
if (rst = '1') then
clp_cnt <= CLP_CNT_MAX;
lcd_clp <= '0';
clp_falling <= '0';
clp_rising <= '0';
elsif (rising_edge(clk)) then
clp_falling <= '0';
clp_rising <= '0';
if (clp_cnt = 0) then
lcd_clp <= not lcd_clp;
clp_cnt <= CLP_CNT_MAX;
else
if (clp_cnt = 1) then
if (lcd_clp = '1') then
clp_falling <= '1';
else
clp_rising <= '1';
end if;
end if;
clp_cnt <= clp_cnt - 1;
end if;
end if;
end process cpgen;
end architecture RTL;