From b9547b5b46ccd0546c3fc312b57dd4fd07f08a64 Mon Sep 17 00:00:00 2001 From: Mike Stirling Date: Sun, 7 Aug 2011 19:58:44 +0100 Subject: Fixed cursor generation in VIDPROC. Added SAA5050 for MODE 7 support. Added tools for generating SAA5050 character ROM. --- bbc_micro_de1.qsf | 1 + bbc_micro_de1.vhd | 92 ++++++++++++- saa5050.vhd | 378 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ saa5050_rom.qip | 3 + saa5050_rom.vhd | 168 ++++++++++++++++++++++++ vidproc.vhd | 22 ++-- 6 files changed, 649 insertions(+), 15 deletions(-) create mode 100644 saa5050.vhd create mode 100644 saa5050_rom.qip create mode 100644 saa5050_rom.vhd 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; -- cgit v1.2.3