|
-- -----------------------------------------------------------------------
|
|
--
|
|
-- 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;
|