-
Notifications
You must be signed in to change notification settings - Fork 568
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1627 from trabucayre/CC_PLL
soc/cores/clock: adding CologneChip CC_PLL
- Loading branch information
Showing
1 changed file
with
151 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
# | ||
# This file is part of LiteX. | ||
# | ||
# Copyright (c) 2023 Gwenhael Goavec-merou<[email protected]> | ||
# SPDX-License-Identifier: BSD-2-Clause | ||
|
||
from migen import * | ||
from migen.genlib.resetsync import AsyncResetSynchronizer | ||
|
||
from litex.gen import * | ||
|
||
from litex.soc.cores.clock.common import * | ||
|
||
# CologneChip GateMate CC_PLL --------------------------------------------------------------------- | ||
|
||
class GateMatePLL(LiteXModule): | ||
""" | ||
CC_PLL generator for CologneChip GateMate FPGAs (UG1001 7.3) | ||
Parameters | ||
---------- | ||
perf_mode: str | ||
FPGA operation mode for VDD_PLL (UNDEFINED, LOWPOWER, ECONOMY, SPEED) (default: UNDEFINED) | ||
low_jitter: int | ||
Low Jitter Mode (0,1) (default: 1) | ||
lock_req: int | ||
Lock status required before PLL output enable (0,1) (default: 0) | ||
Attributes | ||
---------- | ||
reset: Signal in | ||
locked: Signal out | ||
""" | ||
|
||
def __init__(self, | ||
perf_mode = "undefined", | ||
low_jitter = 1, | ||
lock_req = 0): | ||
|
||
assert perf_mode.lower() in ["undefined", "lowpower", "economy", "speed"] | ||
assert low_jitter in [0, 1] | ||
assert lock_req in [0, 1] | ||
|
||
self.logger = logging.getLogger("CC_PLL") | ||
self.reset = Signal() | ||
self.locked = Signal() | ||
self._clkin_freq = None | ||
self._clkouts = {} | ||
self._perf_mode = perf_mode.upper() | ||
self._low_jitter = low_jitter | ||
self._lock_req = lock_req | ||
|
||
self._max_freq = { | ||
"undefined" : 250e6, | ||
"lowpower" : 250e6, | ||
"economy" : 312.5e6, | ||
"speed" : 416.75e6 | ||
}[perf_mode.lower()] | ||
|
||
def register_clkin(self, clkin, freq, usr_clk_ref=False): | ||
""" | ||
Register clkin signal as input PLL input signal | ||
Parameters | ||
---------- | ||
clkin: ClockSignal / Signal | ||
input clock signal | ||
freq: float | ||
input clock frequency (Hz) | ||
usr_clk_ref: bool | ||
select if clkin is connected to CLK_REF or USR_CLK_REF | ||
""" | ||
self._usr_clk_ref = usr_clk_ref | ||
self._clkin = Signal() | ||
if isinstance(clkin, (Signal, ClockSignal)): | ||
self.comb += self._clkin.eq(clkin) | ||
else: | ||
raise ValueError | ||
self._clkin_freq = freq | ||
register_clkin_log(self.logger, clkin, freq) | ||
|
||
def create_clkout(self, cd, freq, phase=0, with_reset=True): | ||
""" | ||
Register cd ClockDomain as PLL output signal | ||
Parameters | ||
---------- | ||
cd: ClockDomain | ||
input clock signal | ||
freq: float | ||
output clock frequency (Hz) | ||
phase: int | ||
must be 0, 90, 180, 270 | ||
with_reset: bool | ||
drive cd reset | ||
""" | ||
assert phase in [0, 90, 180, 270] | ||
assert phase not in self._clkouts | ||
assert freq <= self._max_freq | ||
|
||
clkout = Signal() | ||
self._clkouts[phase] = (clkout, freq) | ||
if with_reset: | ||
self.specials += AsyncResetSynchronizer(cd, ~self.locked) | ||
self.comb += cd.clk.eq(clkout) | ||
create_clkout_log(self.logger, cd.name, freq, 0, phase) | ||
|
||
def do_finalize(self): | ||
assert hasattr(self, "_clkin") | ||
assert len(self._clkouts) > 0 | ||
|
||
# set/unset frequency doubler for CLK180/CLK270 | ||
clk_doub = {180:0, 270:0} | ||
# extract slowest frequency -> ref | ||
clkout_freq = min([f for (_, f) in self._clkouts.values()]) | ||
|
||
for phase in [0, 90, 180, 270]: | ||
(clk, freq) = self._clkouts.get(phase, (Open(), 0)) | ||
self._clkouts[phase] = (clk, freq) # force update (add unselected output) | ||
if freq != 0: | ||
# clk0 and clk90 frequency must be equal to clkout freq | ||
if phase in [0, 90]: | ||
assert freq == clkout_freq | ||
else: | ||
# clk180 and clk270 must be x1 or x2 clkout frequency | ||
assert freq in [clkout_freq, 2 * clkout_freq] | ||
# when clk180 or clk270 == x2 clkout: CLKxx_DOUB must be set | ||
if freq == 2 * clkout_freq: | ||
clk_doub[phase] = 1 | ||
|
||
assert clkout_freq is not None | ||
|
||
freqInMHz = self._clkin_freq/1e6 | ||
freqOutMHz = clkout_freq/1e6 | ||
|
||
self.specials += Instance("CC_PLL", | ||
p_REF_CLK = freqInMHz, # reference input in MHz | ||
p_OUT_CLK = freqOutMHz, # pll output frequency in MHz | ||
p_LOW_JITTER = self._low_jitter, # 0: disable, 1: enable low jitter mode | ||
p_PERF_MD = self._perf_mode, # FPGA operation mode for VDD_PLL | ||
p_LOCK_REQ = self._lock_req, # Lock status required before PLL output enable | ||
p_CI_FILTER_CONST = 2, # optional CI filter constant | ||
p_CP_FILTER_CONST = 4, # optional CP filter constant | ||
i_CLK_REF = self._clkin if not self._usr_clk_ref else Open(), | ||
i_USR_CLK_REF = self._clkin if self._usr_clk_ref else Open(), | ||
i_CLK_FEEDBACK = 0, | ||
i_USR_LOCKED_STDY_RST = self.reset, | ||
o_CLK_REF_OUT = Open(), | ||
o_USR_PLL_LOCKED_STDY = Open(), | ||
o_USR_PLL_LOCKED = self.locked, | ||
**{f"o_CLK{p}" : c for (p, (c, _)) in self._clkouts.items()}, | ||
**{f"p_CLK{p}_DOUB" : v for (p, v) in clk_doub.items()}, | ||
) | ||
|