VHDL implementation of the 6845
From CPCWiki - THE Amstrad CPC encyclopedia!
This code can be used to implement an CRTC6845 in a CPLD or a FPGA. It consists of three files (config.vhd, crtc6845.vhd, cursor.vhd). You can also directly download the source files.
config.vhd
--===========================================================================-- -- -- -- S Y N T H E S I Z A B L E CRTC6845 C O R E -- -- -- -- www.opencores.org - January 2000 -- -- This IP core adheres to the GNU public license. -- -- -- -- VHDL model of MC6845 compatible CRTC -- -- -- -- This model doesn't implement interlace mode. Everything else is -- -- (probably) according to original MC6845 data sheet (except VTOTADJ). -- -- -- -- Implementation in Xilinx Virtex XCV50-6 runs at 50 MHz (character clock).-- -- With external pixel generator this CRTC could handle 450MHz pixel rate -- -- (see MC6845 datasheet for typical application). -- -- -- -- Author: Damjan Lampret, lampret@opencores.org -- -- -- -- TO DO: -- -- -- -- - fix REG_INIT and remove non standard signals at topl level entity. -- -- Allow fixed registers values (now set with REG_INIT). Anyway cleanup -- -- required. -- -- -- -- - split design in four units (horizontal sync, vertical sync, bus -- -- interface and the rest) -- -- -- -- - synthesis with Synplify pending (there are some problems with -- -- UNSIGNED and BIT_LOGIC_VECTOR types in some units !) -- -- -- -- - testbench -- -- -- -- - interlace mode support, extend VSYNC for V.Total Adjust value (R5) -- -- -- -- - verification in a real application -- -- -- --===========================================================================-- library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_arith.all; use IEEE.math_real.all; package CONFIG is -- Don't change these unless you know what you are doing. constant MA_WIDTH : INTEGER := 14; constant RA_WIDTH : INTEGER := 5; constant DB_WIDTH : INTEGER := 8; constant AR_WIDTH : INTEGER := 5; constant INDEX_HT : STD_LOGIC_VECTOR (4 downto 0) := '0' & x"0"; constant INDEX_HD : STD_LOGIC_VECTOR (4 downto 0) := '0' & x"1"; constant INDEX_HSP : STD_LOGIC_VECTOR (4 downto 0) := '0' & x"2"; constant INDEX_HSW : STD_LOGIC_VECTOR (4 downto 0) := '0' & x"3"; constant INDEX_VT : STD_LOGIC_VECTOR (4 downto 0) := '0' & x"4"; constant INDEX_ADJ : STD_LOGIC_VECTOR (4 downto 0) := '0' & x"5"; constant INDEX_VD : STD_LOGIC_VECTOR (4 downto 0) := '0' & x"6"; constant INDEX_VSP : STD_LOGIC_VECTOR (4 downto 0) := '0' & x"7"; constant INDEX_IM : STD_LOGIC_VECTOR (4 downto 0) := '0' & x"8"; constant INDEX_SL : STD_LOGIC_VECTOR (4 downto 0) := '0' & x"9"; constant INDEX_CURST : STD_LOGIC_VECTOR (4 downto 0) := '0' & x"a"; constant INDEX_CUREND : STD_LOGIC_VECTOR (4 downto 0) := '0' & x"b"; constant INDEX_SA_H : STD_LOGIC_VECTOR (4 downto 0) := '0' & x"c"; constant INDEX_SA_L : STD_LOGIC_VECTOR (4 downto 0) := '0' & x"d"; constant INDEX_CUR_H : STD_LOGIC_VECTOR (4 downto 0) := '0' & x"e"; constant INDEX_CUR_L : STD_LOGIC_VECTOR (4 downto 0) := '0' & x"f"; constant INDEX_LP_H : STD_LOGIC_VECTOR (4 downto 0) := '1' & x"0"; constant INDEX_LP_L : STD_LOGIC_VECTOR (4 downto 0) := '1' & x"1"; -- Convert UNSIGNED type to STD_LOGIC_VECTOR type function MAKE_BINARY(A : UNSIGNED) return STD_LOGIC_VECTOR; -- Convert STD_LOGIC_VECTOR type to UNSIGNED type function MAKE_UNSIGNED(A : STD_LOGIC_VECTOR) return UNSIGNED; end CONFIG; package body CONFIG is -- synopsys synthesis_off type tbl_type is array (STD_ULOGIC) of STD_ULOGIC; constant tbl_BINARY : tbl_type := ('X', 'X', '0', '1', 'X', 'X', '0', '1', 'X'); -- synopsys synthesis_on function MAKE_BINARY(A : UNSIGNED) return STD_LOGIC_VECTOR is -- synopsys built_in SYN_FEED_THRU variable one_bit : STD_ULOGIC; variable result : STD_LOGIC_VECTOR (A'range); begin -- synopsys synthesis_off for i in A'range loop result(i) := tbl_BINARY(A(i)); end loop; return result; -- synopsys synthesis_on end; function MAKE_UNSIGNED(A : STD_LOGIC_VECTOR) return UNSIGNED is -- synopsys built_in SYN_FEED_THRU variable one_bit : STD_ULOGIC; variable result : UNSIGNED (A'range); begin -- synopsys synthesis_off for i in A'range loop result(i) := tbl_BINARY(A(i)); end loop; return result; -- synopsys synthesis_on end; end CONFIG;
crtc6845.vhd
--===========================================================================-- -- -- -- S Y N T H E S I Z A B L E CRTC6845 C O R E -- -- -- -- www.opencores.org - January 2000 -- -- This IP core adheres to the GNU public license. -- -- -- -- VHDL model of MC6845 compatible CRTC -- -- -- -- This model doesn't implement interlace mode. Everything else is -- -- (probably) according to original MC6845 data sheet (except VTOTADJ). -- -- -- -- Implementation in Xilinx Virtex XCV50-6 runs at 50 MHz (character clock).-- -- With external pixel generator this CRTC could handle 450MHz pixel rate -- -- (see MC6845 datasheet for typical application). -- -- -- -- Author: Damjan Lampret, lampret@opencores.org -- -- -- -- TO DO: -- -- -- -- - fix REG_INIT and remove non standard signals at topl level entity. -- -- Allow fixed registers values (now set with REG_INIT). Anyway cleanup -- -- required. -- -- -- -- - split design in four units (horizontal sync, vertical sync, bus -- -- interface and the rest) -- -- -- -- - synthesis with Synplify pending (there are some problems with -- -- UNSIGNED and BIT_LOGIC_VECTOR types in some units !) -- -- -- -- - testbench -- -- -- -- - interlace mode support, extend VSYNC for V.Total Adjust value (R5) -- -- -- -- - verification in a real application -- -- -- --===========================================================================-- library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_arith.all; use work.config.all; entity crtc6845 is port ( MA : out STD_LOGIC_VECTOR (MA_WIDTH-1 downto 0); RA : out STD_LOGIC_VECTOR (RA_WIDTH-1 downto 0); HSYNC : out STD_LOGIC; VSYNC : out STD_LOGIC; DE : out STD_LOGIC; CURSOR : out STD_LOGIC; LPSTBn : in STD_LOGIC; E : in STD_LOGIC; RS : in STD_LOGIC; CSn : in STD_LOGIC; RW : in STD_LOGIC; D : inout STD_LOGIC_VECTOR (DB_WIDTH-1 downto 0); RESETn : in STD_LOGIC; CLK : in STD_LOGIC; -- not standard REG_INIT: in STD_LOGIC; -- Hend: inout STD_LOGIC; HS: inout STD_LOGIC; CHROW_CLK: inout STD_LOGIC; Vend: inout STD_LOGIC; SLadj: inout STD_LOGIC; H: inout STD_LOGIC; V: inout STD_LOGIC; CURSOR_ACTIVE: inout STD_LOGIC; VERT_RST: inout STD_LOGIC ); end crtc6845; architecture crtc6845_behav of crtc6845 is -- components component cursor_ctrl port ( RESETn : in STD_LOGIC; CLK : in STD_LOGIC; RA : in STD_LOGIC_VECTOR (RA_WIDTH-1 downto 0); CURSOR : out STD_LOGIC; ACTIVE : in STD_LOGIC; CURST : in STD_LOGIC_VECTOR (6 downto 0); CUREND : in STD_LOGIC_VECTOR (4 downto 0) ); end component; -- 6845 registers R0-R17 signal REG_HT : STD_LOGIC_VECTOR (7 downto 0); signal REG_HD : STD_LOGIC_VECTOR (7 downto 0); signal REG_HSP : STD_LOGIC_VECTOR (7 downto 0); signal REG_HSW : STD_LOGIC_VECTOR (3 downto 0); signal REG_VT : STD_LOGIC_VECTOR (6 downto 0); signal REG_ADJ : STD_LOGIC_VECTOR (4 downto 0); signal REG_VD : STD_LOGIC_VECTOR (6 downto 0); signal REG_VSP : STD_LOGIC_VECTOR (6 downto 0); signal REG_IM : STD_LOGIC_VECTOR (1 downto 0); signal REG_SL : STD_LOGIC_VECTOR (4 downto 0); signal REG_CURST : STD_LOGIC_VECTOR (6 downto 0); signal REG_CUREND : STD_LOGIC_VECTOR (4 downto 0); signal REG_SA_H : STD_LOGIC_VECTOR (5 downto 0); signal REG_SA_L : STD_LOGIC_VECTOR (7 downto 0); signal REG_CUR_H : STD_LOGIC_VECTOR (5 downto 0); signal REG_CUR_L : STD_LOGIC_VECTOR (7 downto 0); signal REG_LP_H : STD_LOGIC_VECTOR (5 downto 0); signal REG_LP_L : STD_LOGIC_VECTOR (7 downto 0); -- Counters signal CTR_HORIZ : UNSIGNED (7 downto 0); signal CTR_HSW : UNSIGNED (3 downto 0); signal CTR_SL : UNSIGNED (4 downto 0); signal CTR_VERT : UNSIGNED (6 downto 0); signal CTR_VSW : UNSIGNED (4 downto 0); signal CTR_LAG : UNSIGNED (13 downto 0); -- I/O address register signal REGIO_AR : STD_LOGIC_VECTOR (AR_WIDTH-1 downto 0); -- Interconnect signals (as in MC6845 datasheet) --pragma translate_off signal Hend: STD_LOGIC; signal HS: STD_LOGIC; signal CHROW_CLK: STD_LOGIC; signal Vend: STD_LOGIC; signal SLadj: STD_LOGIC; signal H: STD_LOGIC; signal V: STD_LOGIC; signal CURSOR_ACTIVE: STD_LOGIC; signal VERT_RST: STD_LOGIC; --pragma translate_on -- Shared Variables signal Hdisp: STD_LOGIC; signal Vdisp: STD_LOGIC; shared variable ROWaddr: UNSIGNED (13 downto 0); begin ext_read: process(E) begin if rising_edge(E) then if CSn = '0' and RW = '1' and RS = '1' then case REGIO_AR is when INDEX_CUR_H => D(5 downto 0) <= REG_CUR_H; D(7 downto 6) <= "00"; when INDEX_CUR_L => D <= REG_CUR_L; when INDEX_LP_H => D(5 downto 0) <= REG_LP_H; D(7 downto 6) <= "00"; when INDEX_LP_L => D <= REG_LP_L; when others => D <= (others => '0'); end case; else D <= (others => 'Z'); end if; end if; end process; ext_write: process(E,REG_INIT) begin if falling_edge(E) then if REG_INIT = '1' then REGIO_AR <= b"0" & x"0"; REG_HT <= x"65"; REG_HD <= x"50"; REG_HSP <= x"56"; REG_HSW <= x"9"; REG_SL <= '0' & x"b"; REG_VT <= b"001" & x"8"; --18 REG_ADJ <= b"0" & x"a"; REG_VD <= b"001" & x"8"; --18 REG_VSP <= b"001" & x"8";--18 REG_CURST <= b"000" & x"0"; REG_CUREND <= b"0" & x"B"; REG_SA_H <= b"00" & x"0"; REG_SA_L <= x"80"; REG_CUR_H <= b"00" & x"0"; REG_CUR_L <= x"80"; end if; end if; --pragma translate_off if CSn = '0' and RW = '0' then if RS = '0' then REGIO_AR <= D (AR_WIDTH-1 downto 0); else case REGIO_AR is when INDEX_HT => REG_HT <= D; when INDEX_HD => REG_HD <= D; when INDEX_HSP => REG_HSP <= D; when INDEX_HSW => REG_HSW <= D(3 downto 0); when INDEX_SL => REG_SL <= D(4 downto 0); when INDEX_VT => REG_VT <= D(6 downto 0); when INDEX_ADJ => REG_ADJ <= D(4 downto 0); when INDEX_VD => REG_VD <= D(6 downto 0); when INDEX_VSP => REG_VSP <= D(6 downto 0); when INDEX_CURST => REG_CURST <= D(6 downto 0); when INDEX_CUREND => REG_CUREND <= D(4 downto 0); when INDEX_SA_H => REG_SA_H <= D(5 downto 0); when INDEX_SA_L => REG_SA_L <= D; when INDEX_CUR_H => REG_CUR_H <= D(5 downto 0); when INDEX_CUR_L => REG_CUR_L <= D; when others => null; end case; end if; end if; end if; --pragma translate_on end process; -------------------------------------------- -- Horizontal Sync -- -------------------------------------------- H_p: process(CLK, RESETn) begin if RESETn = '0' then CTR_HORIZ <= (others => '0'); elsif rising_edge(CLK) then if MAKE_BINARY(CTR_HORIZ) = REG_HT then H <= '1'; CTR_HORIZ <= (others => '0'); else H <= '0'; CTR_HORIZ <= CTR_HORIZ + 1; end if; end if; end process; Hend_p: process(CTR_HORIZ, REG_HD) begin if MAKE_BINARY(CTR_HORIZ) = REG_HD then Hend <= '1'; else Hend <= '0'; end if; end process; CTR_HSW_p: process(CLK, RESETn) begin if RESETn = '0' then CTR_HSW <= (others => '0'); elsif rising_edge(CLK) then if HS = '1' then CTR_HSW <= CTR_HSW + 1; else CTR_HSW <= (others => '0'); end if; end if; end process; HS_p: process(CTR_HORIZ, CTR_HSW, REG_HSP, REG_HSW, HS) begin if MAKE_BINARY(CTR_HORIZ) = REG_HSP then HS <= '1'; elsif MAKE_BINARY(CTR_HSW) = REG_HSW then HS <= '0'; else HS <= HS; end if; HSYNC <= HS; end process; -------------------------------------------- -- Display Enable -- -------------------------------------------- DE_p: process(H, Hend, V, Vend, Hdisp, Vdisp) begin if H = '1' and Hend = '0' then Hdisp <= '1'; elsif H = '0' and Hend = '1' then Hdisp <= '0'; else Hdisp <= Hdisp; end if; if V = '1' and Vend = '0' then Vdisp <= '1'; elsif V = '0' and Vend = '1' then Vdisp <= '0'; else Vdisp <= Vdisp; end if; end process; DE <= Hdisp and Vdisp; -------------------------------------------- -- Scan Line Counter -- -------------------------------------------- CTR_SL_p: process(H, RESETn) begin if RESETn = '0' then CTR_SL <= (others => '0'); elsif rising_edge(H) then if MAKE_BINARY(CTR_SL) = REG_SL then CHROW_CLK <= '1'; CTR_SL <= (others => '0'); else CHROW_CLK <= '0'; CTR_SL <= CTR_SL + 1; end if; end if; end process; RA <= MAKE_BINARY(CTR_SL); SLadj_p: process(CTR_SL, REG_ADJ) begin if MAKE_BINARY(CTR_SL) = REG_ADJ then SLadj <= '1'; else SLadj <= '0'; end if; end process; -------------------------------------------- -- Vertical Sync (Character Row CTR) -- -------------------------------------------- V_p: process(CHROW_CLK, RESETn) begin if RESETn = '0' then CTR_VERT <= (others => '0'); elsif rising_edge(CHROW_CLK) then if MAKE_BINARY(CTR_VERT) = REG_VT then V <= '1'; -- if SLadj = '1' then VERT_RST <= '1'; CTR_VERT <= (others => '0'); -- end if; else VERT_RST <= '0'; V <= '0'; CTR_VERT <= CTR_VERT + 1; end if; end if; end process; Vend_p: process(CTR_VERT, REG_VD) begin if MAKE_BINARY(CTR_VERT) = REG_VD then Vend <= '1'; else Vend <= '0'; end if; end process; CTR_VSW_p: process(H, RESETn, CTR_VERT, REG_VSP, CTR_VSW) begin if RESETn = '0' then CTR_VSW <= (others => '0'); elsif rising_edge(H) then if MAKE_BINARY(CTR_VERT) = REG_VSP then CTR_VSW <= (others => '0'); end if; if CTR_VSW /= 16 then CTR_VSW <= CTR_VSW + 1; VSYNC <= '1'; else VSYNC <= '0'; end if; end if; end process; -------------------------------------------- -- Linear Address Generator -- -------------------------------------------- ROWaddr_p: process(RESETn, CHROW_CLK, VERT_RST, REG_SA_H, REG_SA_L) begin if RESETn = '0' then ROWaddr := MAKE_UNSIGNED(REG_SA_H & REG_SA_L); elsif rising_edge(CHROW_CLK) then ROWaddr := ROWaddr + MAKE_UNSIGNED(REG_HD); if VERT_RST = '1' then ROWaddr := MAKE_UNSIGNED(REG_SA_H & REG_SA_L); end if; end if; end process; LAG_p: process(CLK, RESETn, H, REG_SA_H, REG_SA_L) begin if RESETn = '0' then CTR_LAG <= MAKE_UNSIGNED(REG_SA_H & REG_SA_L); elsif rising_edge(CLK) then if H = '1' then CTR_LAG <= ROWaddr; end if; CTR_LAG <= CTR_LAG + 1; end if; end process; MA <= MAKE_BINARY(CTR_LAG); -------------------------------------------- -- Light Pen Capture -- -------------------------------------------- LP_p: process(CLK, LPSTBn) begin if rising_edge(CLK) then if LPSTBn = '0' then REG_LP_H <= MAKE_BINARY(CTR_LAG(13 downto 8)); REG_LP_L <= MAKE_BINARY(CTR_LAG(7 downto 0)); end if; end if; end process; -------------------------------------------- -- Cursor Control Unit Instantiation -- -------------------------------------------- CURSOR_p: process(CTR_LAG, REG_CUR_H, REG_CUR_L) begin if CTR_LAG = MAKE_UNSIGNED(REG_CUR_H & REG_CUR_L) then CURSOR_ACTIVE <= '1'; else CURSOR_ACTIVE <= '0'; end if; end process; cursor_ctrl_inst: cursor_ctrl port map ( RESETn => RESETn, CLK => V, RA => MAKE_BINARY(CTR_SL), CURSOR => CURSOR, ACTIVE => CURSOR_ACTIVE, CURST => REG_CURST, CUREND => REG_CUREND ); end crtc6845_behav;
cursor.vhd
--===========================================================================-- -- -- -- S Y N T H E S I Z A B L E CRTC6845 C O R E -- -- -- -- www.opencores.org - January 2000 -- -- This IP core adheres to the GNU public license. -- -- -- -- VHDL model of MC6845 compatible CRTC -- -- -- -- This model doesn't implement interlace mode. Everything else is -- -- (probably) according to original MC6845 data sheet (except VTOTADJ). -- -- -- -- Implementation in Xilinx Virtex XCV50-6 runs at 50 MHz (character clock).-- -- With external pixel generator this CRTC could handle 450MHz pixel rate -- -- (see MC6845 datasheet for typical application). -- -- -- -- Author: Damjan Lampret, lampret@opencores.org -- -- -- -- TO DO: -- -- -- -- - fix REG_INIT and remove non standard signals at topl level entity. -- -- Allow fixed registers values (now set with REG_INIT). Anyway cleanup -- -- required. -- -- -- -- - split design in four units (horizontal sync, vertical sync, bus -- -- interface and the rest) -- -- -- -- - synthesis with Synplify pending (there are some problems with -- -- UNSIGNED and BIT_LOGIC_VECTOR types in some units !) -- -- -- -- - testbench -- -- -- -- - interlace mode support, extend VSYNC for V.Total Adjust value (R5) -- -- -- -- - verification in a real application -- -- -- --===========================================================================-- library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_arith.all; use work.config.all; entity cursor_ctrl is port ( RESETn : in STD_LOGIC; CLK : in STD_LOGIC; RA : in STD_LOGIC_VECTOR (RA_WIDTH-1 downto 0); CURSOR : out STD_LOGIC; ACTIVE : in STD_LOGIC; CURST : in STD_LOGIC_VECTOR (6 downto 0); CUREND : in STD_LOGIC_VECTOR (4 downto 0) ); end cursor_ctrl; architecture cursor_ctrl_behav of cursor_ctrl is signal CTR_BLINK : UNSIGNED (4 downto 0); begin blink_ctr_p: process (CLK, RESETn) begin if RESETn = '0' then CTR_BLINK <= (others => '0'); elsif rising_edge(CLK) then CTR_BLINK <= CTR_BLINK + 1; end if; end process; cursor_p: process (ACTIVE, CURST, CUREND, RA, CTR_BLINK) begin if RA >= CURST(4 downto 0) and RA <= CUREND and ACTIVE = '1' then case CURST(6 downto 5) is when "00" => CURSOR <= '1'; when "10" => CURSOR <= CTR_BLINK(3); when "11" => CURSOR <= CTR_BLINK(4); when others => CURSOR <= '0'; end case; else CURSOR <= '0'; end if; end process; end cursor_ctrl_behav;