Project

General

Profile

115 markw
-- -----------------------------------------------------------------------
--
-- VGA-64
--
-- Multi purpose FPGA expansion for the Commodore 64 computer
--
-- -----------------------------------------------------------------------
126 markw
-- Copyright 2005-2012 by Peter Wendrich (pwsoft@syntiac.com)
115 markw
-- http://www.syntiac.com/chameleon.html
-- -----------------------------------------------------------------------
--
-- C64 Phi2-clock regeneration and divider
--
-- -----------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.all;

-- -----------------------------------------------------------------------

architecture rtl of chameleon_phi_clock is
--constant phaseShift : integer := 5; -- Number of cycles that FPGA runs ahead of measured phi.
constant phaseShift : integer := 8; -- Number of cycles that FPGA runs ahead of measured phi.
constant guardBits : integer := 4; -- Extra bits to reduce rounding errors in calculations
126 markw
signal phi2_n_reg : unsigned(7 downto 0);
signal phiSync : std_logic := '0';
115 markw
signal locCnt : unsigned(7 downto 0) := (others => '0');
signal fracCnt : unsigned(guardBits-1 downto 0) := (others => '0');
signal c64Cnt : unsigned(7 downto 0) := (others => '0');
signal slvCnt : unsigned(7 downto 0) := (others => '0');

signal avgDelta : signed(8 downto 0) := (others => '0');
signal avgLen : unsigned((7+guardBits) downto 0) := (others => '0');

signal localPreHalf : std_logic := '0';
signal localHalf : std_logic := '0';
signal localPreEnd : std_logic := '0';
signal localEnd : std_logic := '0';

signal localPhi : std_logic := '0';

signal localPost1 : std_logic := '0';
signal localPost2 : std_logic := '0';
signal localPost3 : std_logic := '0';
signal localPost4 : std_logic := '0';
begin
-- Average phi length
phiLength <= avgLen((7+guardBits) downto guardBits);

-- Local generated phi
phiLocal <= localPhi;

-- Cycle counter (add 1 to max-counter for each Mhz system clock)
-- For 100Mhz the cycle-counter runs to about 97 (NTSC) or 102 (PAL)
phiCnt <= locCnt;
phiPreHalf <= localPreHalf;
phiHalf <= localHalf;
phiPreEnd <= localPreEnd;
phiEnd <= localEnd;

phiPost1 <= localPost1;
phiPost2 <= localPost2;
phiPost3 <= localPost3;
phiPost4 <= localPost4;

-- Input clock synchronizer
process(clk) is
begin
if rising_edge(clk) then
126 markw
phiSync <= '0';
phi2_n_reg <= phi2_n_reg(phi2_n_reg'high-1 downto 0) & phi2_n;
-- Detect falling edge of phi2 (is rising edge here as phi2_n input is inverted).
if phi2_n_reg = "00000001" then
phiSync <= '1';
end if;
115 markw
end if;
end process;

-- Determine cycle length
process(clk) is
begin
if rising_edge(clk) then
no_clock <= '0';
docking_station <= '0';
avgDelta <= (others => '0');
avgLen <= unsigned(signed(avgLen) + avgDelta);

if (not c64Cnt) /= 0 then
c64Cnt <= c64Cnt + 1;
else
-- No Sync? Use internal speed.

-- Values for avgLen are determined experimentally using the testbench to measure actually speed.
-- Higher numbers slow down clock. Lower numbers speed clock up. Try a few times until optimum is reached
-- for particular system clock (100 Mhz at time of writing). Clocks can be accurate to atleast 3 digits with 4 guard bits.
-- PAL mode 0.985248 Mhz
avgLen <= to_unsigned(1703, 8+guardBits);
if mode = '1' then
-- NTSC mode 1.022727 Mhz
avgLen <= to_unsigned(1643, 8+guardBits);
end if;
126 markw
if (phi2_n_reg(1) = '1') and (phi2_n_reg(2) = '1') and (phi2_n_reg(3) = '1') then
115 markw
docking_station <= '1';
end if;
no_clock <= '1';
end if;
126 markw
if phiSync = '1' then
115 markw
avgDelta <= signed("0" & c64Cnt) - signed("0" & avgLen((7+guardBits) downto guardBits));
c64Cnt <= (others => '0');
end if;
end if;
end process;

process(clk) is
begin
if rising_edge(clk) then
localPost1 <= localHalf or localEnd;
localPost2 <= localPost1;
localPost3 <= localPost2;
localPost4 <= localPost3;
end if;
end process;

process(clk) is
variable newFrac : unsigned(fracCnt'high+1 downto fracCnt'low);
begin
if rising_edge(clk) then
localPreHalf <= '0';
localHalf <= localPreHalf;
localPreEnd <= '0';
localEnd <= localPreEnd;

locCnt <= locCnt + 1;
if slvCnt >= avgLen((7+guardBits) downto guardBits) then
slvCnt <= (others => '0');
else
slvCnt <= slvCnt + 1;
end if;

if (slvCnt + phaseShift) = avgLen((7+guardBits) downto (1+guardBits)) then
localPreHalf <= '1';
end if;
if (slvCnt + phaseShift) = avgLen((7+guardBits) downto guardBits)
and localPhi = '1' then
localPreEnd <= '1';
end if;
if localHalf = '1' then
localPhi <= '1';
end if;
if localEnd = '1' then
-- Add fractional part to clock counter to have higher precision
newFrac := ("0" & fracCnt) + ("0" & (not avgLen(guardBits-1 downto 0)));
fracCnt <= newFrac(fracCnt'range);

localPhi <= '0';
locCnt <= (others => '0');
slvCnt <= c64Cnt + ("0000000" & newFrac(newFrac'high));
end if;
end if;
end process;
end architecture;