repo2/chameleon/chameleon_phi_clock_a.vhd
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;
|