Project

General

Profile

-- -----------------------------------------------------------------------
--
-- VGA-64
--
-- Multi purpose FPGA expansion for the Commodore 64 computer
--
-- -----------------------------------------------------------------------
-- Copyright 2005-2018 by Peter Wendrich (pwsoft@syntiac.com)
-- http://www.syntiac.com/chameleon.html
--
-- This source file is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as published
-- by the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This source file is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
--
-- -----------------------------------------------------------------------
--
-- 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 guardBits : integer := 4; -- Extra bits to reduce rounding errors in calculations
signal phi2_n_reg : unsigned(11 downto 0) := (others => '0');
signal phiSync : std_logic := '0';

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
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 = "000000000001" then
phiSync <= '1';
end if;
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;
if phi2_n_reg(11 downto 4) = "11111111" then
docking_station <= '1';
end if;
no_clock <= '1';
end if;
if phiSync = '1' then
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 + phase_shift) = avgLen((7+guardBits) downto (1+guardBits)) then
localPreHalf <= '1';
end if;
if (slvCnt + phase_shift) = 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;
(19-19/48)