VHDL implementation of the 6845

From CPCWiki - THE Amstrad CPC encyclopedia!
Revision as of 16:36, 27 January 2010 by MacDeath (Talk | contribs)

Jump to: navigation, search

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;