URL
https://opencores.org/ocsvn/open8_urisc/open8_urisc/trunk
Subversion Repositories open8_urisc
[/] [open8_urisc/] [trunk/] [VHDL/] [o8_hd44780_if.vhd] - Rev 322
Compare with Previous | Blame | View Log
-- Copyright (c)2013, 2020 Jeremy Seth Henry -- All rights reserved. -- -- Redistribution and use in source and binary forms, with or without -- modification, are permitted provided that the following conditions are met: -- * Redistributions of source code must retain the above copyright -- notice, this list of conditions and the following disclaimer. -- * Redistributions in binary form must reproduce the above copyright -- notice, this list of conditions and the following disclaimer in the -- documentation and/or other materials provided with the distribution, -- where applicable (as part of a user interface, debugging port, etc.) -- -- THIS SOFTWARE IS PROVIDED BY JEREMY SETH HENRY ``AS IS'' AND ANY -- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -- DISCLAIMED. IN NO EVENT SHALL JEREMY SETH HENRY BE LIABLE FOR ANY -- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- -- VHDL Entity: o8_hd44780_if -- Description: Provides low-level access to "standard" character LCDs using -- the ST/HD44780(U) control ASIC wired in either 8-bit or -- reduced (4-bit) mode. -- All low-level timing of the control signals are handled by this -- module, allowing client firmware to use a simple register -- interface to program the LCD panel. -- Note that this module assumes that the R/Wn line has been tied LOW -- -- Register Map -- Address Function -- Offset Bitfield Description Read/Write -- 0x0 AAAAAAAA LCD Register Write (Read-Write*) -- 0x1 AAAAAAAA LCD Data Write (Read-Write*) -- 0x2 AAAAAAAA LCD Rearm Init Timer (Read-Write*) -- 0x3 AAAAAAAA LCD Backlight (Read-Write) -- -- Note: Reading 0x0, 0x1 or 0x2 will report whether the panel is ready or not -- in the MSB (bit 7). 0x00 = NOT READY / 0x80 = READY -- -------------------------------------------------------------------------------- -- LCD Controller -------------------------------------------------------------------------------- -- -- LCD Instruction Set (Hitachi Compatible) -- Instruction RS RW D7 D6 D5 D4 D3 D2 D1 D0 Time ------------------------------------------------------------------------ -- Clear Display | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1.52mS -- Return Home | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | x | 1.52mS -- Entry Mode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | ID| S | 37uS -- Display Pwr | 0 | 0 | 0 | 0 | 0 | 0 | 1 | D | C | B | 37uS -- Cursor/Display Shift | 0 | 0 | 0 | 0 | 0 | 1 | SC| RL| x | x | 37uS -- Function Set | 0 | 0 | 0 | 0 | 1 | DL| N | F | x | x | 37uS -- Set CGRAM Address | 0 | 0 | 0 | 1 | A | A | A | A | A | A | 37uS -- Set DDRAM Address | 0 | 0 | 1 | A | A | A | A | A | A | A | 37uS -- LCD Instruction Set (New Haven) -- Instruction RS RW D7 D6 D5 D4 D3 D2 D1 D0 Time ------------------------------------------------------------------------ -- Clear Display | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 2.00mS -- Return Home | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | x | 600uS -- Entry Mode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | ID| S | 600uS -- Display Pwr | 0 | 0 | 0 | 0 | 0 | 0 | 1 | D | C | B | 600uS -- Cursor/Display Shift | 0 | 0 | 0 | 0 | 0 | 1 | SC| RL| x | x | 600uS -- Function Set | 0 | 0 | 0 | 0 | 1 | DL| N | F | T | T | 600uS -- Set CGRAM Address | 0 | 0 | 0 | 1 | A | A | A | A | A | A | 600uS -- Set DDRAM Address | 0 | 0 | 1 | A | A | A | A | A | A | A | 600uS -- -- Notes: -- ID = Increment/Decrement DDRAM Address (1 = increment, 0 = decrement) -- S = Shift Enable (1 = Shift display according to ID, 0 = Don't shift) -- D = Display On/Off (1 = on, 0 = off) -- C = Cursor On/Off (1 = on, 0 = off) -- B = Cursor Blink (1 = block cursor, 0 = underline cursor) -- SC / RL = Shift Cursor/Display Right/Left (see data sheet - not needed for init) -- F = Font (0 = 5x8, 1 = 5x11) Ignored on 2-line displays (N = 1) -- N = Number of Lines (0 = 1 lines, 1 = 2 lines) -- DL = Data Length (0 = 4-bit bus, 1 = 8-bit bus) This is fixed at 1 in this module -- A = Address (see data sheet for usage) -- T = New Haven Only - Changes the character set (see data sheet) -- -- Revision History -- Author Date Change ------------------ -------- --------------------------------------------------- -- Seth Henry 04/12/21 Design Start library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; use ieee.std_logic_arith.all; library work; use work.open8_pkg.all; entity o8_hd44780_if is generic( Use_4Bit_IF : boolean := FALSE; -- LCD bus IF timing Tas : integer := 20; -- ns Tpwe : integer := 450; -- nS Tcyce : integer := 1000; -- nS -- LCD command timing Tpwrdly : integer := 40000; -- uS Tcldsp : integer := 2000; -- uS Tbusy : integer := 50; -- uS -- Backlight Use_Backlight : boolean := FALSE; Default_Brightness : std_logic_vector(7 downto 0) := x"00"; -- System clock & address Clock_Frequency : real; Address : ADDRESS_TYPE ); port( Open8_Bus : in OPEN8_BUS_TYPE; Write_Qual : in std_logic := '1'; Rd_Data : out DATA_TYPE; Interrupt : out std_logic; -- LCD_E : out std_logic; LCD_RS : out std_logic; LCD_DQ : out std_logic_vector(7 downto 0); LCD_BL : out std_logic ); end entity; architecture behave of o8_hd44780_if is alias Clock is Open8_Bus.Clock; alias Reset is Open8_Bus.Reset; alias uSec_Tick is Open8_Bus.uSec_Tick; constant User_Addr : std_logic_vector(15 downto 2) := Address(15 downto 2); alias Comp_Addr is Open8_Bus.Address(15 downto 2); signal Addr_Match : std_logic; alias Reg_Sel_d is Open8_Bus.Address(1 downto 0); signal Reg_Sel_q : std_logic_vector(1 downto 0) := "00"; signal Wr_En_d : std_logic := '0'; signal Wr_En_q : std_logic := '0'; alias Wr_Data_d is Open8_Bus.Wr_Data; signal Wr_Data_q : DATA_TYPE := x"00"; signal Rd_En_d : std_logic := '0'; signal Rd_En_q : std_logic := '0'; signal Rearm_Init : std_logic := '0'; signal Reg_Valid : std_logic := '0'; signal Reg_Sel : std_logic := '0'; signal Reg_Data : std_logic_vector(7 downto 0) := x"00"; signal Tx_Ready : std_logic := '0'; constant HW_TMR_BITS : integer := ceil_log2(Tpwrdly); constant TPWR_DELAY : std_logic_vector(HW_TMR_BITS-1 downto 0) := conv_std_logic_vector(Tpwrdly,HW_TMR_BITS); constant CLDSP_DELAY : std_logic_vector(HW_TMR_BITS-1 downto 0) := conv_std_logic_vector(Tcldsp,HW_TMR_BITS); constant BUSY_DELAY : std_logic_vector(HW_TMR_BITS-1 downto 0) := conv_std_logic_vector(Tbusy, HW_TMR_BITS); signal hw_timer : std_logic_vector(HW_TMR_BITS-1 downto 0); type CTRL_STATES is (INIT, PWR_WAIT, IDLE, PREP_WR, ISSUE_WR, WR_WAIT, BUSY_WAIT, ISSUE_INT ); signal ctrl_state : CTRL_STATES := INIT; signal Wr_Fnset : std_logic := '0'; signal Wr_Data : DATA_TYPE := x"00"; signal Wr_Reg : std_logic := '0'; signal Wr_En : std_logic := '0'; signal IO_Done : std_logic := '0'; signal LCD_Data : DATA_TYPE := x"00"; signal LCD_Addr : std_logic := '0'; -------------------------------------------------------------------------------- -- Backlight signals -------------------------------------------------------------------------------- signal LCD_Bright : DATA_TYPE := x"00"; begin -------------------------------------------------------------------------------- -- Open8 Register interface -------------------------------------------------------------------------------- Addr_Match <= '1' when Comp_Addr = User_Addr else '0'; Wr_En_d <= Addr_Match and Open8_Bus.Wr_En and Write_Qual; Rd_En_d <= Addr_Match and Open8_Bus.Rd_En; io_reg: process( Clock, Reset ) begin if( Reset = Reset_Level )then Reg_Sel_q <= "00"; Wr_En_q <= '0'; Wr_Data_q <= x"00"; Rd_En_q <= '0'; Rd_Data <= OPEN8_NULLBUS; Rearm_Init <= '0'; Reg_Valid <= '0'; Reg_Sel <= '0'; Reg_Data <= x"00"; LCD_Bright <= Default_Brightness; elsif( rising_edge( Clock ) )then Reg_Sel_q <= Reg_Sel_d; Rearm_Init <= '0'; Wr_En_q <= Wr_En_d; Wr_Data_q <= Wr_Data_d; Reg_Valid <= '0'; if( Wr_En_q = '1' )then case( Reg_Sel_q )is when "00" | "01" => Reg_Valid <= '1'; Reg_Sel <= Reg_Sel_q(0); Reg_Data <= Wr_Data_q; when "10" => Rearm_Init <= '1'; when "11" => LCD_Bright <= Wr_Data_q; when others => null; end case; end if; Rd_En_q <= Rd_En_d; Rd_Data <= OPEN8_NULLBUS; if( Rd_En_q = '1' )then case( Reg_Sel_q )is when "00" | "01" | "10" => Rd_Data(7) <= Tx_Ready; when "11" => Rd_Data <= LCD_Bright; when others => null; end case; end if; end if; end process; -------------------------------------------------------------------------------- -- LCD and Register logic -------------------------------------------------------------------------------- LCD_Ctrl_proc: process( Clock, Reset ) begin if( Reset = Reset_Level )then ctrl_state <= INIT; hw_timer <= (others => '0'); Wr_Fnset <= '0'; Wr_Data <= x"00"; Wr_Reg <= '0'; Wr_En <= '0'; Tx_Ready <= '0'; Interrupt <= '0'; elsif( rising_edge(Clock) )then Wr_En <= '0'; Tx_Ready <= '0'; Interrupt <= '0'; hw_timer <= hw_timer - uSec_Tick; case( ctrl_state )is when INIT => hw_timer <= TPWR_DELAY; ctrl_state <= PWR_WAIT; when PWR_WAIT => if( hw_timer = 0 )then ctrl_state <= IDLE; end if; when IDLE => Tx_Ready <= '1'; if( Rearm_Init = '1' )then ctrl_state <= INIT; elsif( Reg_Valid = '1' )then Wr_Reg <= Reg_Sel; Wr_Data <= Reg_Data; ctrl_state <= PREP_WR; end if; when PREP_WR => Wr_Fnset <= '0'; -- Trap on Function Set if we are in 4-bit mode, so that we can issue -- the first nibble twice. if( Use_4Bit_IF and Wr_Reg = '0' and Wr_Data(7 downto 4) = "0010" )then Wr_Fnset <= '1'; end if; ctrl_state <= ISSUE_WR; when ISSUE_WR => Wr_En <= '1'; hw_timer <= BUSY_DELAY; if( Wr_Reg = '0' and Wr_Data < 4 )then hw_timer <= CLDSP_DELAY; end if; ctrl_state <= WR_WAIT; when WR_WAIT => if( IO_Done = '1' )then ctrl_state <= BUSY_WAIT; end if; when BUSY_WAIT => if( hw_timer = 0 )then ctrl_state <= ISSUE_INT; end if; when ISSUE_INT => Interrupt <= '1'; ctrl_state <= IDLE; when others => null; end case; end if; end process; -------------------------------------------------------------------------------- -- Low-level I/O drivers -------------------------------------------------------------------------------- IF_Type_4bit: if( Use_4Bit_IF )generate U_IO : entity work.hd44780_4b generic map( Tas => Tas, Tpwe => Tpwe, Tcyce => Tcyce, Clock_Frequency => Clock_Frequency, Reset_Level => Reset_Level ) port map( Clock => Clock, Reset => Reset, -- Wr_Fnset => Wr_Fnset, Wr_Data => Wr_Data, Wr_Reg => Wr_Reg, Wr_En => Wr_En, -- IO_Done => IO_Done, -- LCD_RS => LCD_RS, LCD_E => LCD_E, LCD_DQ => LCD_DQ ); end generate; IF_Type_8bit: if( not Use_4Bit_IF )generate U_IO : entity work.hd44780_8b generic map( Tas => Tas, Tpwe => Tpwe, Tcyce => Tcyce, Clock_Frequency => Clock_Frequency, Reset_Level => Reset_Level ) port map( Clock => Clock, Reset => Reset, -- Wr_Data => Wr_Data, Wr_Reg => Wr_Reg, Wr_En => Wr_En, -- IO_Done => IO_Done, -- LCD_RS => LCD_RS, LCD_E => LCD_E, LCD_DQ => LCD_DQ ); end generate; -------------------------------------------------------------------------------- -- Backlight control logic (optional) -------------------------------------------------------------------------------- Backlight_Disabled: if( not Use_Backlight )generate LCD_BL <= '0'; end generate; Backlight_Enabled: if( Use_Backlight )generate U_BL : entity work.vdsm8 generic map( Reset_Level => Reset_Level ) port map( Clock => Clock, Reset => Reset, DACin => LCD_Bright, DACout => LCD_BL ); end generate; end architecture;