-- 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;