aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Stirling <opensource@mikestirling.co.uk>2011-08-07 19:58:44 +0100
committerMike Stirling <opensource@mikestirling.co.uk>2011-08-07 19:58:44 +0100
commitb9547b5b46ccd0546c3fc312b57dd4fd07f08a64 (patch)
treee16bac979a37aa7222b9cc71ce8baaeca3606b16
parenteaf58b095e333967182edee679ebd0989f6ca9ed (diff)
downloadfpga-bbc-b9547b5b46ccd0546c3fc312b57dd4fd07f08a64.tar.gz
fpga-bbc-b9547b5b46ccd0546c3fc312b57dd4fd07f08a64.tar.bz2
fpga-bbc-b9547b5b46ccd0546c3fc312b57dd4fd07f08a64.zip
Fixed cursor generation in VIDPROC. Added SAA5050 for MODE 7 support. Added tools for generating SAA5050 character ROM.
-rw-r--r--bbc_micro_de1.qsf1
-rw-r--r--bbc_micro_de1.vhd92
-rw-r--r--saa5050.vhd378
-rw-r--r--saa5050_rom.qip3
-rw-r--r--saa5050_rom.vhd168
-rw-r--r--vidproc.vhd22
6 files changed, 649 insertions, 15 deletions
diff --git a/bbc_micro_de1.qsf b/bbc_micro_de1.qsf
index e5324f6..fa4583d 100644
--- a/bbc_micro_de1.qsf
+++ b/bbc_micro_de1.qsf
@@ -537,4 +537,5 @@ set_global_assignment -name QIP_FILE ehbasic.qip
set_global_assignment -name VHDL_FILE m6522_tb.vhd
set_global_assignment -name VHDL_FILE keyboard.vhd
set_global_assignment -name VHDL_FILE debugger.vhd
+set_global_assignment -name QIP_FILE saa5050_rom.qip
set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top \ No newline at end of file
diff --git a/bbc_micro_de1.vhd b/bbc_micro_de1.vhd
index 11ac86d..4240e5d 100644
--- a/bbc_micro_de1.vhd
+++ b/bbc_micro_de1.vhd
@@ -246,6 +246,42 @@ port (
);
end component;
+--------------------------------
+-- SAA5050 Teletext Generator
+--------------------------------
+
+component saa5050 is
+port (
+ CLOCK : in std_logic;
+ -- 6 MHz dot clock enable
+ CLKEN : in std_logic;
+ -- Async reset
+ nRESET : in std_logic;
+
+ -- Character data input (in the bus clock domain)
+ DI_CLOCK : in std_logic;
+ DI_CLKEN : in std_logic;
+ DI : in std_logic_vector(6 downto 0);
+
+ -- Timing inputs
+ -- General line reset (not used)
+ GLR : in std_logic; -- /HSYNC
+ -- Data entry window - high during VSYNC.
+ -- Resets ROM row counter and drives 'flash' signal
+ DEW : in std_logic; -- VSYNC
+ -- Character rounding select - high during even field
+ CRS : in std_logic; -- FIELD
+ -- Load output shift register enable - high during active video
+ LOSE : in std_logic; -- DE
+
+ -- Video out
+ R : out std_logic;
+ G : out std_logic;
+ B : out std_logic;
+ Y : out std_logic
+ );
+end component;
+
-------------
-- MOS ROM
-------------
@@ -514,6 +550,9 @@ signal vid_clken : std_logic; -- 16 MHz video cycles
signal mhz4_clken : std_logic; -- Used by 6522
signal mhz2_clken : std_logic; -- Used for latching CPU address for clock stretch
signal mhz1_clken : std_logic; -- 1 MHz bus and associated peripherals, 6522 phase 2
+-- SAA5050 needs a 6 MHz clock enable relative to a 24 MHz clock
+signal ttxt_clken_counter : unsigned(1 downto 0);
+signal ttxt_clken : std_logic;
-- Testing
signal test_uart_do : std_logic_vector(7 downto 0);
@@ -567,6 +606,16 @@ signal r_out : std_logic;
signal g_out : std_logic;
signal b_out : std_logic;
+-- SAA5050 signals
+signal ttxt_glr : std_logic;
+signal ttxt_dew : std_logic;
+signal ttxt_crs : std_logic;
+signal ttxt_lose : std_logic;
+signal ttxt_r : std_logic;
+signal ttxt_g : std_logic;
+signal ttxt_b : std_logic;
+signal ttxt_y : std_logic;
+
-- MOS ROM signals
signal mos_d : std_logic_vector(7 downto 0);
@@ -764,6 +813,20 @@ begin
r_out, g_out, b_out
);
+ teletext : saa5050 port map (
+ CLOCK_24(0), -- This runs at 6 MHz, which we can't derive from the 32 MHz clock
+ ttxt_clken,
+ reset_n,
+ clock, -- Data input is synchronised from the bus clock domain
+ vid_clken,
+ SRAM_DQ(6 downto 0),
+ ttxt_glr,
+ ttxt_dew,
+ ttxt_crs,
+ ttxt_lose,
+ ttxt_r, ttxt_g, ttxt_b, ttxt_y
+ );
+
-- MOS ROM
mos : os12 port map (
cpu_a(13 downto 0),
@@ -921,6 +984,18 @@ begin
end if;
end if;
end process;
+
+ ttxt_clk_gen: process(CLOCK_24(0),reset_n)
+ begin
+ if reset_n = '0' then
+ ttxt_clken_counter <= (others => '0');
+ elsif rising_edge(CLOCK_24(0)) then
+ ttxt_clken_counter <= ttxt_clken_counter + 1;
+ end if;
+ end process;
+
+ -- 6 MHz clock enable for SAA5050
+ ttxt_clken <= '1' when ttxt_clken_counter = 0 else '0';
-- CPU configuration and fixed signals
cpu_mode <= "00"; -- 6502
@@ -1118,9 +1193,15 @@ begin
-- VIDPROC
vidproc_invert_n <= '1';
vidproc_disen <= crtc_de and not crtc_ra(3); -- DISEN is masked off by RA(3) for MODEs 3 and 6
- r_in <= '0'; -- FIXME: From SAA5050
- g_in <= '0';
- b_in <= '0';
+ r_in <= ttxt_r;
+ g_in <= ttxt_g;
+ b_in <= ttxt_b;
+
+ -- SAA5050
+ ttxt_glr <= not crtc_hsync;
+ ttxt_dew <= crtc_vsync;
+ ttxt_crs <= not crtc_ra(0);
+ ttxt_lose <= crtc_de;
-- CRTC drives video out (CSYNC on HSYNC output, VSYNC high)
VGA_HS <= not (crtc_hsync xor crtc_vsync);
@@ -1199,8 +1280,7 @@ begin
-- DEBUG STUFF
-----------------
- GPIO_0(0) <= user_via_pb_out(1); --clk
- GPIO_0(1) <= user_via_pb_out(0); --do
- GPIO_0(2) <= SD_MISO; -- di
+ GPIO_0(0) <= not (crtc_hsync xor crtc_vsync);
+ GPIO_0(1) <= crtc_de;
end architecture;
diff --git a/saa5050.vhd b/saa5050.vhd
new file mode 100644
index 0000000..734890c
--- /dev/null
+++ b/saa5050.vhd
@@ -0,0 +1,378 @@
+-- SAA5050 teletext generator
+--
+-- Synchronous implementation for FPGA. Certain TV-specific functions are
+-- not implemented. e.g.
+--
+-- No /SI pin - 'TEXT' mode is permanently enabled
+-- No remote control features (/DATA, DLIM)
+-- No large character support
+-- No support for box overlay (BLAN, PO, DE)
+-- No character rounding, although this may be added
+--
+-- FIXME: Hold graphics not supported - this needs to be added
+--
+-- (C) 2011 Mike Stirling
+--
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+use IEEE.NUMERIC_STD.ALL;
+
+entity saa5050 is
+port (
+ CLOCK : in std_logic;
+ -- 6 MHz dot clock enable
+ CLKEN : in std_logic;
+ -- Async reset
+ nRESET : in std_logic;
+
+ -- Character data input (in the bus clock domain)
+ DI_CLOCK : in std_logic;
+ DI_CLKEN : in std_logic;
+ DI : in std_logic_vector(6 downto 0);
+
+ -- Timing inputs
+ -- General line reset (not used)
+ GLR : in std_logic; -- /HSYNC
+ -- Data entry window - high during VSYNC.
+ -- Resets ROM row counter and drives 'flash' signal
+ DEW : in std_logic; -- VSYNC
+ -- Character rounding select - high during even field
+ CRS : in std_logic; -- FIELD
+ -- Load output shift register enable - high during active video
+ LOSE : in std_logic; -- DE
+
+ -- Video out
+ R : out std_logic;
+ G : out std_logic;
+ B : out std_logic;
+ Y : out std_logic
+ );
+end entity;
+
+architecture rtl of saa5050 is
+
+component saa5050_rom IS
+ PORT
+ (
+ address : IN STD_LOGIC_VECTOR (11 DOWNTO 0);
+ clock : IN STD_LOGIC ;
+ q : OUT STD_LOGIC_VECTOR (7 DOWNTO 0)
+ );
+end component;
+
+-- Data input registered in the bus clock domain
+signal di_r : std_logic_vector(6 downto 0);
+-- Data input registered in the pixel clock domain
+signal code : std_logic_vector(6 downto 0);
+signal line_addr : unsigned(3 downto 0);
+signal rom_address : std_logic_vector(11 downto 0);
+signal rom_data : std_logic_vector(7 downto 0);
+
+-- Latched timing signals for detection of falling edges
+signal dew_r : std_logic;
+signal lose_r : std_logic;
+-- Delayed display enable derived from LOSE by delaying for one character
+signal disp_enable : std_logic;
+signal disp_enable_r : std_logic;
+
+-- Row and column addressing is handled externally. We just need to
+-- keep track of which of the 10 lines we are on within the character...
+signal line_counter : unsigned(3 downto 0);
+-- ... and which of the 6 pixels we are on within each line
+signal pixel_counter : unsigned(2 downto 0);
+-- We also need to count frames to implement the flash feature.
+-- The datasheet says this is 0.75 Hz with a 3:1 on/off ratio, so it
+-- is probably a /64 counter, which gives us 0.78 Hz
+signal flash_counter : unsigned(5 downto 0);
+-- Output shift register
+signal shift_reg : std_logic_vector(5 downto 0);
+
+-- Flash mask
+signal flash : std_logic;
+
+-- Current display state
+-- Foreground colour (B2, G1, R0)
+signal fg : std_logic_vector(2 downto 0);
+-- Background colour (B2, G1, R0)
+signal bg : std_logic_vector(2 downto 0);
+signal conceal : std_logic;
+signal gfx : std_logic;
+signal gfx_sep : std_logic;
+signal gfx_hold : std_logic;
+signal is_flash : std_logic;
+signal double_high : std_logic;
+-- Set in first row of double height
+signal double_high1 : std_logic;
+-- Set in second row of double height
+signal double_high2 : std_logic;
+
+begin
+ char_rom: saa5050_rom port map (
+ rom_address,
+ CLOCK,
+ rom_data
+ );
+
+ -- Generate flash signal for 3:1 ratio
+ flash <= flash_counter(5) and flash_counter(4);
+
+ -- Sync data input
+ process(DI_CLOCK,nRESET)
+ begin
+ if nRESET = '0' then
+ di_r <= (others => '0');
+ elsif rising_edge(DI_CLOCK) and DI_CLKEN = '1' then
+ di_r <= DI;
+ end if;
+ end process;
+
+ -- Register data into pixel clock domain
+ process(CLOCK,nRESET)
+ begin
+ if nRESET = '0' then
+ code <= (others => '0');
+ elsif rising_edge(CLOCK) and CLKEN = '1' then
+ code <= di_r;
+ end if;
+ end process;
+
+ -- Generate character rom address in pixel clock domain
+ -- This is done combinatorially since all the inputs are already
+ -- registered and the address is re-registered by the ROM
+ line_addr <= line_counter when double_high = '0' else
+ ("0" & line_counter(3 downto 1)) when double_high2 = '0' else
+ ("0" & line_counter(3 downto 1)) + 5;
+ rom_address <= (others => '0') when (double_high = '0' and double_high2 = '1') else
+ gfx & code & std_logic_vector(line_addr);
+
+-- process(CLOCK,nRESET)
+-- variable line_addr : unsigned(3 downto 0);
+-- variable c : std_logic_vector(6 downto 0);
+-- begin
+-- -- Derive line address taking double height into account
+-- if double_high = '1' then
+-- line_addr := "0" & line_counter(3 downto 1);
+-- if double_high2 = '1' then
+-- line_addr := line_addr + 5;
+-- end if;
+-- else
+-- line_addr := line_counter;
+-- end if;
+--
+-- -- If this is the second row of a double height pair and double_high
+-- -- is not asserted then we fetch a blank instead (0)
+-- if double_high = '0' and double_high2 = '1' then
+-- c := (others => '0');
+-- else
+-- c := di_r;
+-- end if;
+--
+-- if nRESET = '0' then
+-- rom_address <= (others => '0');
+-- elsif rising_edge(CLOCK) and CLKEN = '1' then
+-- rom_address <= gfx & c & std_logic_vector(line_addr);
+-- end if;
+-- end process;
+
+ -- Character row and pixel counters
+ process(CLOCK,nRESET)
+ begin
+ if nRESET = '0' then
+ dew_r <= '0';
+ lose_r <= '0';
+ disp_enable <= '0';
+ disp_enable_r <= '0';
+ double_high1 <= '0';
+ double_high2 <= '0';
+ line_counter <= (others => '0');
+ pixel_counter <= (others => '0');
+ flash_counter <= (others => '0');
+ elsif rising_edge(CLOCK) and CLKEN = '1' then
+ -- Register syncs for edge detection
+ dew_r <= DEW;
+ lose_r <= LOSE;
+ disp_enable_r <= disp_enable;
+
+ -- When first entering double-height mode start on top row
+ if double_high = '1' and double_high1 = '0' and double_high2 = '0' then
+ double_high1 <= '1';
+ end if;
+
+ -- Rising edge of LOSE is the start of the active line
+ if LOSE = '1' and lose_r = '0' then
+ -- Reset pixel counter - small offset to make the output
+ -- line up with the cursor from the video ULA
+ pixel_counter <= "010";
+ else
+ -- Count pixels between 0 and 5
+ if pixel_counter = 5 then
+ -- Start of next character and delayed display enable
+ pixel_counter <= (others => '0');
+ disp_enable <= lose_r;
+ else
+ pixel_counter <= pixel_counter + 1;
+ end if;
+ end if;
+
+ if DEW = '1' then
+ -- Reset line counter and double height state during VSYNC
+ line_counter <= (others => '0');
+ double_high1 <= '0';
+ double_high2 <= '0';
+ else
+ -- Count frames on end of VSYNC (falling edge of DEW)
+ if DEW = '0' and dew_r = '1' then
+ flash_counter <= flash_counter + 1;
+ end if;
+
+ -- Count lines on end of active video (falling edge of disp_enable)
+ if disp_enable = '0' and disp_enable_r = '1' then
+ if line_counter = 9 then
+ line_counter <= (others => '0');
+
+ -- Keep track of which row we are on for double-height
+ -- The double_high flag can be cleared before the end of a row, but if
+ -- double height characters are used anywhere on a row then the double_high1
+ -- flag will be set and remain set until the next row. This is used
+ -- to determine that the bottom half of the characters should be shown if
+ -- double_high is set once again on the row below.
+ double_high1 <= '0';
+ double_high2 <= double_high1;
+ else
+ line_counter <= line_counter + 1;
+ end if;
+ end if;
+ end if;
+ end if;
+ end process;
+
+ -- Shift register
+ process(CLOCK,nRESET)
+ begin
+ if nRESET = '0' then
+ shift_reg <= (others => '0');
+ elsif rising_edge(CLOCK) and CLKEN = '1' then
+ if disp_enable = '1' and pixel_counter = 0 then
+ -- Load the shift register with the ROM bit pattern
+ -- at the start of each character while disp_enable is asserted.
+ shift_reg <= rom_data(5 downto 0);
+
+ -- If bit 7 of the ROM data is set then this is a graphics
+ -- character and separated/hold graphics modes apply.
+ -- We don't just assume this to be the case if gfx=1 because
+ -- these modes don't apply to caps even in graphics mode
+ if rom_data(7) = '1' then
+ -- Apply a mask for separated graphics mode
+ if gfx_sep = '1' then
+ shift_reg(5) <= '0';
+ shift_reg(2) <= '0';
+ if line_counter = 2 or line_counter = 6 or line_counter = 9 then
+ shift_reg <= (others => '0');
+ end if;
+ end if;
+ end if;
+ else
+ -- Pump the shift register
+ shift_reg <= shift_reg(4 downto 0) & "0";
+ end if;
+ end if;
+ end process;
+
+ -- Control character handling
+ process(CLOCK,nRESET)
+ begin
+ if nRESET = '0' then
+ fg <= (others => '1');
+ bg <= (others => '0');
+ conceal <= '0';
+ gfx <= '0';
+ gfx_sep <= '0';
+ gfx_hold <= '0';
+ is_flash <= '0';
+ double_high <= '0';
+ elsif rising_edge(CLOCK) and CLKEN = '1' then
+ if disp_enable = '0' then
+ -- Reset to start of line defaults
+ fg <= (others => '1');
+ bg <= (others => '0');
+ conceal <= '0';
+ gfx <= '0';
+ gfx_sep <= '0';
+ gfx_hold <= '0';
+ is_flash <= '0';
+ double_high <= '0';
+ elsif pixel_counter = 0 then
+ -- Latch new control codes at the start of each character
+ if code(6 downto 5) = "00" then
+ if code(3) = '0' then
+ -- Colour and graphics setting clears conceal mode
+ conceal <= '0';
+
+ -- Select graphics or alpha mode
+ gfx <= code(4);
+
+ -- 0 would be black but is not allowed so has no effect,
+ -- otherwise set the colour
+ if code(2 downto 0) /= "000" then
+ fg <= code(2 downto 0);
+ end if;
+ else
+ case code(4 downto 0) is
+ -- FLASH
+ when "01000" => is_flash <= '1';
+ -- STEADY
+ when "01001" => is_flash <= '0';
+ -- NORMAL HEIGHT
+ when "01100" => double_high <= '0';
+ -- DOUBLE HEIGHT
+ when "01101" => double_high <= '1';
+ -- CONCEAL
+ when "11000" => conceal <= '1';
+ -- CONTIGUOUS GFX
+ when "11001" => gfx_sep <= '0';
+ -- SEPARATED GFX
+ when "11010" => gfx_sep <= '1';
+ -- BLACK BACKGROUND
+ when "11100" => bg <= (others => '0');
+ -- NEW BACKGROUND
+ when "11101" => bg <= fg;
+ -- HOLD GFX
+ when "11110" => gfx_hold <= '1';
+ -- RELEASE GFX
+ when "11111" => gfx_hold <= '0';
+
+ when others => null;
+ end case;
+ end if;
+ end if;
+ end if;
+ end if;
+ end process;
+
+ -- Output
+ process(CLOCK,nRESET)
+ variable pixel : std_logic;
+ begin
+ pixel := shift_reg(5) and not ((flash and is_flash) or conceal);
+
+ if nRESET = '0' then
+ R <= '0';
+ G <= '0';
+ B <= '0';
+ elsif rising_edge(CLOCK) and CLKEN = '1' then
+ -- Generate mono output
+ Y <= pixel;
+
+ -- Generate colour output
+ if pixel = '1' then
+ R <= fg(0);
+ G <= fg(1);
+ B <= fg(2);
+ else
+ R <= bg(0);
+ G <= bg(1);
+ B <= bg(2);
+ end if;
+ end if;
+ end process;
+end architecture;
diff --git a/saa5050_rom.qip b/saa5050_rom.qip
new file mode 100644
index 0000000..e9513f7
--- /dev/null
+++ b/saa5050_rom.qip
@@ -0,0 +1,3 @@
+set_global_assignment -name IP_TOOL_NAME "ROM: 1-PORT"
+set_global_assignment -name IP_TOOL_VERSION "9.1"
+set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) "saa5050_rom.vhd"]
diff --git a/saa5050_rom.vhd b/saa5050_rom.vhd
new file mode 100644
index 0000000..7541c0b
--- /dev/null
+++ b/saa5050_rom.vhd
@@ -0,0 +1,168 @@
+-- megafunction wizard: %ROM: 1-PORT%
+-- GENERATION: STANDARD
+-- VERSION: WM1.0
+-- MODULE: altsyncram
+
+-- ============================================================
+-- File Name: saa5050_rom.vhd
+-- Megafunction Name(s):
+-- altsyncram
+--
+-- Simulation Library Files(s):
+-- altera_mf
+-- ============================================================
+-- ************************************************************
+-- THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE!
+--
+-- 9.1 Build 222 10/21/2009 SJ Web Edition
+-- ************************************************************
+
+
+--Copyright (C) 1991-2009 Altera Corporation
+--Your use of Altera Corporation's design tools, logic functions
+--and other software and tools, and its AMPP partner logic
+--functions, and any output files from any of the foregoing
+--(including device programming or simulation files), and any
+--associated documentation or information are expressly subject
+--to the terms and conditions of the Altera Program License
+--Subscription Agreement, Altera MegaCore Function License
+--Agreement, or other applicable license agreement, including,
+--without limitation, that your use is for the sole purpose of
+--programming logic devices manufactured by Altera and sold by
+--Altera or its authorized distributors. Please refer to the
+--applicable agreement for further details.
+
+
+LIBRARY ieee;
+USE ieee.std_logic_1164.all;
+
+LIBRARY altera_mf;
+USE altera_mf.all;
+
+ENTITY saa5050_rom IS
+ PORT
+ (
+ address : IN STD_LOGIC_VECTOR (11 DOWNTO 0);
+ clock : IN STD_LOGIC ;
+ q : OUT STD_LOGIC_VECTOR (7 DOWNTO 0)
+ );
+END saa5050_rom;
+
+
+ARCHITECTURE SYN OF saa5050_rom IS
+
+ SIGNAL sub_wire0 : STD_LOGIC_VECTOR (7 DOWNTO 0);
+
+
+
+ COMPONENT altsyncram
+ GENERIC (
+ clock_enable_input_a : STRING;
+ clock_enable_output_a : STRING;
+ init_file : STRING;
+ intended_device_family : STRING;
+ lpm_hint : STRING;
+ lpm_type : STRING;
+ numwords_a : NATURAL;
+ operation_mode : STRING;
+ outdata_aclr_a : STRING;
+ outdata_reg_a : STRING;
+ widthad_a : NATURAL;
+ width_a : NATURAL;
+ width_byteena_a : NATURAL
+ );
+ PORT (
+ clock0 : IN STD_LOGIC ;
+ address_a : IN STD_LOGIC_VECTOR (11 DOWNTO 0);
+ q_a : OUT STD_LOGIC_VECTOR (7 DOWNTO 0)
+ );
+ END COMPONENT;
+
+BEGIN
+ q <= sub_wire0(7 DOWNTO 0);
+
+ altsyncram_component : altsyncram
+ GENERIC MAP (
+ clock_enable_input_a => "BYPASS",
+ clock_enable_output_a => "BYPASS",
+ init_file => "./roms/saa5050/saa5050.hex",
+ intended_device_family => "Cyclone II",
+ lpm_hint => "ENABLE_RUNTIME_MOD=NO",
+ lpm_type => "altsyncram",
+ numwords_a => 4096,
+ operation_mode => "ROM",
+ outdata_aclr_a => "NONE",
+ outdata_reg_a => "UNREGISTERED",
+ widthad_a => 12,
+ width_a => 8,
+ width_byteena_a => 1
+ )
+ PORT MAP (
+ clock0 => clock,
+ address_a => address,
+ q_a => sub_wire0
+ );
+
+
+
+END SYN;
+
+-- ============================================================
+-- CNX file retrieval info
+-- ============================================================
+-- Retrieval info: PRIVATE: ADDRESSSTALL_A NUMERIC "0"
+-- Retrieval info: PRIVATE: AclrAddr NUMERIC "0"
+-- Retrieval info: PRIVATE: AclrByte NUMERIC "0"
+-- Retrieval info: PRIVATE: AclrOutput NUMERIC "0"
+-- Retrieval info: PRIVATE: BYTE_ENABLE NUMERIC "0"
+-- Retrieval info: PRIVATE: BYTE_SIZE NUMERIC "8"
+-- Retrieval info: PRIVATE: BlankMemory NUMERIC "0"
+-- Retrieval info: PRIVATE: CLOCK_ENABLE_INPUT_A NUMERIC "0"
+-- Retrieval info: PRIVATE: CLOCK_ENABLE_OUTPUT_A NUMERIC "0"
+-- Retrieval info: PRIVATE: Clken NUMERIC "0"
+-- Retrieval info: PRIVATE: IMPLEMENT_IN_LES NUMERIC "0"
+-- Retrieval info: PRIVATE: INIT_FILE_LAYOUT STRING "PORT_A"
+-- Retrieval info: PRIVATE: INIT_TO_SIM_X NUMERIC "0"
+-- Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone II"
+-- Retrieval info: PRIVATE: JTAG_ENABLED NUMERIC "0"
+-- Retrieval info: PRIVATE: JTAG_ID STRING "NONE"
+-- Retrieval info: PRIVATE: MAXIMUM_DEPTH NUMERIC "0"
+-- Retrieval info: PRIVATE: MIFfilename STRING "./roms/saa5050/saa5050.hex"
+-- Retrieval info: PRIVATE: NUMWORDS_A NUMERIC "4096"
+-- Retrieval info: PRIVATE: RAM_BLOCK_TYPE NUMERIC "0"
+-- Retrieval info: PRIVATE: RegAddr NUMERIC "1"
+-- Retrieval info: PRIVATE: RegOutput NUMERIC "0"
+-- Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0"
+-- Retrieval info: PRIVATE: SingleClock NUMERIC "1"
+-- Retrieval info: PRIVATE: UseDQRAM NUMERIC "0"
+-- Retrieval info: PRIVATE: WidthAddr NUMERIC "12"
+-- Retrieval info: PRIVATE: WidthData NUMERIC "8"
+-- Retrieval info: PRIVATE: rden NUMERIC "0"
+-- Retrieval info: CONSTANT: CLOCK_ENABLE_INPUT_A STRING "BYPASS"
+-- Retrieval info: CONSTANT: CLOCK_ENABLE_OUTPUT_A STRING "BYPASS"
+-- Retrieval info: CONSTANT: INIT_FILE STRING "./roms/saa5050/saa5050.hex"
+-- Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone II"
+-- Retrieval info: CONSTANT: LPM_HINT STRING "ENABLE_RUNTIME_MOD=NO"
+-- Retrieval info: CONSTANT: LPM_TYPE STRING "altsyncram"
+-- Retrieval info: CONSTANT: NUMWORDS_A NUMERIC "4096"
+-- Retrieval info: CONSTANT: OPERATION_MODE STRING "ROM"
+-- Retrieval info: CONSTANT: OUTDATA_ACLR_A STRING "NONE"
+-- Retrieval info: CONSTANT: OUTDATA_REG_A STRING "UNREGISTERED"
+-- Retrieval info: CONSTANT: WIDTHAD_A NUMERIC "12"
+-- Retrieval info: CONSTANT: WIDTH_A NUMERIC "8"
+-- Retrieval info: CONSTANT: WIDTH_BYTEENA_A NUMERIC "1"
+-- Retrieval info: USED_PORT: address 0 0 12 0 INPUT NODEFVAL address[11..0]
+-- Retrieval info: USED_PORT: clock 0 0 0 0 INPUT NODEFVAL clock
+-- Retrieval info: USED_PORT: q 0 0 8 0 OUTPUT NODEFVAL q[7..0]
+-- Retrieval info: CONNECT: @address_a 0 0 12 0 address 0 0 12 0
+-- Retrieval info: CONNECT: q 0 0 8 0 @q_a 0 0 8 0
+-- Retrieval info: CONNECT: @clock0 0 0 0 0 clock 0 0 0 0
+-- Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all
+-- Retrieval info: GEN_FILE: TYPE_NORMAL saa5050_rom.vhd TRUE
+-- Retrieval info: GEN_FILE: TYPE_NORMAL saa5050_rom.inc FALSE
+-- Retrieval info: GEN_FILE: TYPE_NORMAL saa5050_rom.cmp FALSE
+-- Retrieval info: GEN_FILE: TYPE_NORMAL saa5050_rom.bsf FALSE
+-- Retrieval info: GEN_FILE: TYPE_NORMAL saa5050_rom_inst.vhd FALSE
+-- Retrieval info: GEN_FILE: TYPE_NORMAL saa5050_rom_waveforms.html FALSE
+-- Retrieval info: GEN_FILE: TYPE_NORMAL saa5050_rom_wave*.jpg FALSE
+-- Retrieval info: LIB_FILE: altera_mf
diff --git a/vidproc.vhd b/vidproc.vhd
index 799e159..49c0474 100644
--- a/vidproc.vhd
+++ b/vidproc.vhd
@@ -63,6 +63,7 @@ signal delayed_disen : std_logic;
-- Internal clock enable generation
signal clken_pixel : std_logic;
+signal clken_fetch : std_logic;
signal clken_counter : unsigned(3 downto 0);
-- Cursor generation - can span up to 32 pixels
@@ -70,7 +71,7 @@ signal clken_counter : unsigned(3 downto 0);
-- Segment 2 is 16 pixels wide
signal cursor_invert : std_logic;
signal cursor_active : std_logic;
-signal cursor_counter : unsigned(4 downto 0);
+signal cursor_counter : unsigned(1 downto 0);
begin
-- Synchronous register access, enabled on every clock
@@ -116,13 +117,17 @@ begin
(CLKEN and not clken_counter(0)) when r0_pixel_rate = "10" else
(CLKEN and not (clken_counter(0) or clken_counter(1))) when r0_pixel_rate = "01" else
(CLKEN and not (clken_counter(0) or clken_counter(1) or clken_counter(2)));
-
-- The CRT controller is always enabled in the 15th cycle, so that the result
-- is ready for latching into the shift register in cycle 0. If 2 MHz mode is
-- selected then the CRTC is also enabled in the 7th cycle
CLKEN_CRTC <= CLKEN and
clken_counter(0) and clken_counter(1) and clken_counter(2) and
(clken_counter(3) or r0_crtc_2mhz);
+ -- The result is fetched from the CRTC in cycle 0 and also cycle 8 if 2 MHz
+ -- mode is selected. This is used for reloading the shift register as well as
+ -- counting cursor pixels
+ clken_fetch <= CLKEN and not (clken_counter(0) or clken_counter(1) or clken_counter(2) or
+ (clken_counter(3) and not r0_crtc_2mhz));
process(CLOCK,nRESET)
begin
@@ -140,8 +145,7 @@ begin
if nRESET = '0' then
shiftreg <= (others => '0');
elsif rising_edge(CLOCK) and clken_pixel = '1' then
- if clken_counter = "0000" or
- (clken_counter = "1000" and r0_crtc_2mhz = '1') then
+ if clken_fetch = '1' then
-- Fetch next byte from RAM into shift register. This always occurs in
-- cycle 0, and also in cycle 8 if the CRTC is clocked at double rate.
shiftreg <= DI_RAM;
@@ -154,21 +158,21 @@ begin
-- Cursor generation
cursor_invert <= cursor_active and
- ((r0_cursor0 and not (cursor_counter(4) or cursor_counter(3))) or
- (r0_cursor1 and cursor_counter(3) and not cursor_counter(4)) or
- (r0_cursor2 and cursor_counter(4)));
+ ((r0_cursor0 and not (cursor_counter(0) or cursor_counter(1))) or
+ (r0_cursor1 and cursor_counter(0) and not cursor_counter(1)) or
+ (r0_cursor2 and cursor_counter(1)));
process(CLOCK,nRESET)
begin
if nRESET = '0' then
cursor_active <= '0';
cursor_counter <= (others => '0');
- elsif rising_edge(CLOCK) and CLKEN = '1' then
+ elsif rising_edge(CLOCK) and clken_fetch = '1' then
if CURSOR = '1' or cursor_active = '1' then
-- Latch cursor
cursor_active <= '1';
-- Reset on counter wrap
- if cursor_counter = "11111" then
+ if cursor_counter = "11" then
cursor_active <= '0';
end if;