mirror of
https://github.com/lcbcFoo/ReonV.git
synced 2025-04-24 05:27:07 -04:00
209 lines
6.3 KiB
VHDL
209 lines
6.3 KiB
VHDL
------------------------------------------------------------------------------
|
|
-- This file is a part of the GRLIB VHDL IP LIBRARY
|
|
-- Copyright (C) 2003 - 2008, Gaisler Research
|
|
-- Copyright (C) 2008 - 2014, Aeroflex Gaisler
|
|
-- Copyright (C) 2015 - 2017, Cobham Gaisler
|
|
--
|
|
-- This program is free software; you can redistribute it and/or modify
|
|
-- it under the terms of the GNU General Public License as published by
|
|
-- the Free Software Foundation; either version 2 of the License, or
|
|
-- (at your option) any later version.
|
|
--
|
|
-- This program 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, write to the Free Software
|
|
-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
-------------------------------------------------------------------------------
|
|
-- Entity: sim_pll
|
|
-- File: sim_pll.vhd
|
|
-- Author: Magnus Hjorth, Aeroflex Gaisler
|
|
-- Description: Generic simulated PLL with input frequency checking
|
|
-------------------------------------------------------------------------------
|
|
|
|
library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
library grlib;
|
|
use grlib.stdlib.all;
|
|
|
|
entity sim_pll is
|
|
generic (
|
|
clkmul: integer := 1;
|
|
clkdiv1: integer := 1;
|
|
clkphase1: integer := 0;
|
|
clkdiv2: integer := 1;
|
|
clkphase2: integer := 0;
|
|
clkdiv3: integer := 1;
|
|
clkphase3: integer := 0;
|
|
clkdiv4: integer := 1;
|
|
clkphase4: integer := 0;
|
|
-- Frequency limits in kHz, for checking only
|
|
minfreq: integer := 0;
|
|
maxfreq: integer := 10000000;
|
|
-- Lock tolerance in ps
|
|
locktol: integer := 2
|
|
);
|
|
port (
|
|
i: in std_logic;
|
|
o1: out std_logic;
|
|
o2: out std_logic;
|
|
o3: out std_logic;
|
|
o4: out std_logic;
|
|
lock: out std_logic;
|
|
rst: in std_logic
|
|
);
|
|
end;
|
|
|
|
architecture sim of sim_pll is
|
|
signal clkout1,clkout2,clkout3,clkout4: std_logic;
|
|
signal tp: time := 1 ns;
|
|
signal timeset: boolean := false;
|
|
signal fb: std_ulogic;
|
|
signal comp: time := 0 ns;
|
|
signal llock: std_logic;
|
|
begin
|
|
|
|
o1 <= transport clkout1 after tp + (tp*clkdiv1*(clkphase1 mod 360)) / (clkmul*360);
|
|
o2 <= transport clkout2 after tp + (tp*clkdiv2*(clkphase2 mod 360)) / (clkmul*360);
|
|
o3 <= transport clkout3 after tp + (tp*clkdiv3*(clkphase3 mod 360)) / (clkmul*360);
|
|
o4 <= transport clkout4 after tp + (tp*clkdiv4*(clkphase4 mod 360)) / (clkmul*360);
|
|
lock <= llock after tp*20; -- 20 cycle inertia on lock signal
|
|
|
|
freqmeas: process(i)
|
|
variable ts,te: time;
|
|
variable mf: integer;
|
|
variable warned: boolean := false;
|
|
variable first: boolean := true;
|
|
begin
|
|
if rising_edge(i) and (now /= (0 ps)) then
|
|
ts := te;
|
|
te := now;
|
|
if first then
|
|
first := false;
|
|
else
|
|
mf := (1 ms) / (te-ts);
|
|
assert (mf >= minfreq and mf <= maxfreq) or warned or rst='0' or llock/='1'
|
|
report "Input frequency out of range, " &
|
|
"measured: " & tost(mf) & ", min:" & tost(minfreq) & ", max:" & tost(maxfreq)
|
|
severity warning;
|
|
if (mf < minfreq or mf > maxfreq) and rst/='0' and llock='1' then warned := true; end if;
|
|
if llock='0' or te-ts-tp > locktol*(1 ps) or te-ts-tp < -locktol*(1 ps) then
|
|
tp <= te-ts;
|
|
timeset <= true;
|
|
end if;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
genclk: process
|
|
variable divcount1,divcount2,divcount3,divcount4: integer;
|
|
variable compen: boolean;
|
|
variable t: time;
|
|
variable compps: integer;
|
|
|
|
begin
|
|
compen := false;
|
|
clkout1 <= '0';
|
|
clkout2 <= '0';
|
|
clkout3 <= '0';
|
|
clkout4 <= '0';
|
|
|
|
if not timeset or rst='0' then
|
|
wait until timeset and rst/='0';
|
|
end if;
|
|
divcount1 := 0;
|
|
divcount2 := 0;
|
|
divcount3 := 0;
|
|
divcount4 := 0;
|
|
fb <= '1';
|
|
clkout1 <= '1';
|
|
clkout2 <= '1';
|
|
clkout3 <= '1';
|
|
clkout4 <= '1';
|
|
oloop: loop
|
|
for x in 0 to 2*clkmul-1 loop
|
|
if x=0 then fb <= '1'; end if;
|
|
if x=clkmul then fb <= '0'; end if;
|
|
t := tp/(2*clkmul);
|
|
if compen and comp /= (0 ns) then
|
|
-- Handle compensation below resolution limit (1 ps assumed)
|
|
if comp < 2*clkmul*(1 ps) and comp > -2*clkmul*(1 ps) then
|
|
compps := abs(comp / (1 ps));
|
|
if x > 0 and x <= compps then
|
|
if comp > 0 ps then
|
|
t := t + 1 ps;
|
|
else
|
|
t := t - 1 ps;
|
|
end if;
|
|
end if;
|
|
else
|
|
t:=t+comp/(2*clkmul);
|
|
end if;
|
|
end if;
|
|
if t > (0 ns) then
|
|
wait on rst for t;
|
|
else
|
|
wait for 1 ns;
|
|
end if;
|
|
exit oloop when rst='0';
|
|
divcount1 := divcount1+1;
|
|
if divcount1 >= clkdiv1 then
|
|
clkout1 <= not clkout1;
|
|
divcount1 := 0;
|
|
end if;
|
|
divcount2 := divcount2+1;
|
|
if divcount2 >= clkdiv2 then
|
|
clkout2 <= not clkout2;
|
|
divcount2 := 0;
|
|
end if;
|
|
divcount3 := divcount3+1;
|
|
if divcount3 >= clkdiv3 then
|
|
clkout3 <= not clkout3;
|
|
divcount3 := 0;
|
|
end if;
|
|
divcount4 := divcount4+1;
|
|
if divcount4 >= clkdiv4 then
|
|
clkout4 <= not clkout4;
|
|
divcount4 := 0;
|
|
end if;
|
|
end loop;
|
|
compen := true;
|
|
end loop oloop;
|
|
end process;
|
|
|
|
fbchk: process(fb,i)
|
|
variable last_i,prev_i: time;
|
|
variable last_fb,prev_fb: time;
|
|
variable vlock: std_logic := '0';
|
|
begin
|
|
if falling_edge(i) then
|
|
prev_i := last_i;
|
|
last_i := now;
|
|
end if;
|
|
if falling_edge(fb) then
|
|
-- Update phase compensation
|
|
if last_i < last_fb+tp/2 then
|
|
comp <= (last_i - last_fb);
|
|
else
|
|
comp <= last_i - now;
|
|
end if;
|
|
prev_fb := last_fb;
|
|
last_fb := now;
|
|
end if;
|
|
if (last_i<=(last_fb+locktol*(1 ps)) and last_i>=(last_fb-locktol*(1 ps)) and
|
|
prev_i<=(prev_fb+locktol*(1 ps)) and prev_i>=(prev_fb-locktol*(1 ps))) then
|
|
vlock := '1';
|
|
end if;
|
|
if prev_fb > last_i+locktol*(1 ps) or prev_i>last_fb+locktol*(1 ps) then
|
|
vlock := '0';
|
|
end if;
|
|
llock <= vlock;
|
|
|
|
end process;
|
|
|
|
end;
|
|
|
|
|