2022-04-07 18:46:57 +02:00

188 lines
5.4 KiB
VHDL

-- altera vhdl_input_version vhdl_2008
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
entity LCDDriver is
generic (
F_CLK : natural -- Board frequency in Hz
);
port (
clk : in std_logic;
rst_n : in std_logic;
data_in : in std_logic_vector(15 downto 0);
empty_in : in std_logic;
refresh_in : in std_logic;
cmd_en_in : in std_logic;
cmd_dcx_in : in std_logic;
cmd_data_in : in std_logic_vector(7 downto 0);
data_out : out std_logic_vector(15 downto 0);
rd_n : out std_logic;
wr_n : out std_logic;
rs : out std_logic;
cs_n : out std_logic;
lcd_rdreq : out std_logic;
busy : out std_logic
);
end LCDDriver;
architecture Behavioral of LCDDriver is
constant F_LCD : natural := natural(real(25)*10**(6.0));
constant F_LCD_MIN : natural := natural(real(12.5)*10**(6.0));
constant F_LCD_MAX : natural := natural(real(30)*10**(6.0));
constant lcd_clk_en : std_logic := '1' ;
signal lcd_clk : std_logic;
type GState is (tx, cmd, ready, std_wait,cmd_wait);
type SNDState is (init, wrx, tx, done);
signal snds_reg, snds_next : SNDState;
signal gs_reg, gs_next : GState;
signal data_reg : std_logic_vector(15 downto 0);
signal cmd_dcx_reg : std_logic := '1';
signal cmd_data_reg : std_logic_vector(7 downto 0);
signal rdreq_limiter : std_logic;
begin
busy <= '0' when gs_reg = ready else '1';
--Global State Process
process (all) begin
gs_next <= gs_reg;
snds_next <= snds_reg;
case gs_reg is
when ready =>
if cmd_en_in = '1' then
if lcd_clk = '1' then
gs_next <= cmd;
snds_next <= wrx;
else
gs_next <= cmd_wait;
end if;
elsif refresh_in = '1' then
gs_next <= std_wait;
end if;
when cmd_wait =>
if lcd_clk = '1' then
gs_next <= cmd;
snds_next <= wrx;
end if;
when std_wait =>
if empty_in = '0' then
if lcd_clk = '1' then
gs_next <= tx;
snds_next <= init;
end if;
end if;
when tx =>
if snds_reg = done then
gs_next <= ready;
end if;
when cmd =>
if snds_reg = done then
gs_next <= ready;
end if;
end case;
if (gs_reg = cmd or gs_reg = tx) then
case snds_reg is
when wrx =>
snds_next <= tx;
when tx =>
if gs_reg = cmd then
snds_next <= done;
elsif gs_reg = tx then
if empty_in = '1' then
snds_next <= done;
else
snds_next <= wrx;
end if;
end if;
when init =>
snds_next <= wrx;
when others =>
null;
end case;
end if;
end process;
rs <= cmd_dcx_reg when gs_reg = cmd else '1'; -- DCX when CMD else Keep high
cs_n <= not rst_n; -- Chip Select
wr_n <= '0' when (gs_reg = cmd or gs_reg = tx) and
snds_reg = wrx else '1'; -- Write data on rising edge
rd_n <= '1'; -- Unused, Read data on rising edge
data_out <= data_reg when gs_reg = tx else
"00000000" & cmd_data_reg when gs_reg = cmd else
(others => '0');
lcd_rdreq <= '1' when (gs_reg = ready and gs_next = std_wait) or
(snds_reg /= init and snds_next = init) or
(rdreq_limiter = '0' and gs_reg = tx and snds_reg = tx) else '0';
-- Clock Sync & State Handler
process (clk, rst_n) begin
if rst_n = '0' then
gs_reg <= ready;
snds_reg <= wrx;
data_reg <= (others => '0');
cmd_dcx_reg <= '0';
cmd_data_reg <= (others => '0');
elsif rising_edge(clk) then
gs_reg <= gs_next;
if lcd_clk = '1' then
snds_reg <= snds_next; -- We run the SND at 25Mhz to respect state timings
if (snds_reg = tx or snds_reg = init) then
data_reg <= data_in;
end if;
if (snds_reg = wrx) then
rdreq_limiter <= '0';
end if;
end if;
if lcd_rdreq = '1' then
rdreq_limiter <= '1';
end if;
if gs_reg = ready and (cmd_en_in = '1') then
cmd_dcx_reg <= cmd_dcx_in;
cmd_data_reg <= cmd_data_in;
end if;
end if;
end process;
-- WS output clock
clkgen_ent : entity work.ClkGen
generic map (
F_CLK => F_CLK,
F_OUT => integer(F_LCD),
F_MIN => integer(F_LCD_MIN),
F_MAX => integer(F_LCD_MAX)
)
port map (
clk => clk,
rst_n => rst_n,
clk_o => lcd_clk,
en => lcd_clk_en
);
end Behavioral;