Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
#
# This file is part of LiteX.
#
# Copyright (c) 2020 Florent Kermarrec <florent@enjoy-digital.fr>
# SPDX-License-Identifier: BSD-2-Clause
import io
import unittest
from contextlib import redirect_stdout
from migen import *
from litex.soc.cores.clock import *
from litex.soc.cores.clock.gowin_gw1n import GW1NOSC, GW1NPLL
from litex.soc.cores.clock.gowin_gw5a import GW5APLL
class _FakeEfinixIfaceWriter:
def __init__(self):
self.blocks = []
def get_block(self, name):
for block in self.blocks:
if block["name"] == name:
return block
raise ValueError("Unknown block: {}.".format(name))
class _FakeEfinixToolchain:
def __init__(self):
self.ifacewriter = _FakeEfinixIfaceWriter()
self.excluded_ios = []
class _FakeEfinixPlatform:
def __init__(self, family="Titanium"):
self.family = family
self.device = "T20"
self.toolchain = _FakeEfinixToolchain()
self.clks = {}
self.pll_used = 0
self.pll_available = 4
def add_iface_io(self, name):
return Signal(name=name)
def get_pin_name(self, clkin):
if clkin is None:
raise ValueError("No pin signal provided.")
return clkin.name_override or "clk"
def get_pin_location(self, clkin):
return []
def get_free_pll_resource(self):
resource = "PLL{}".format(self.pll_used)
self.pll_used += 1
return resource
class TestClock(unittest.TestCase):
def assert_frequency_close(self, actual, expected, margin=1e-2):
self.assertAlmostEqual(actual, expected, delta=expected*margin)
def get_efinix_pll_block(self, pll):
return pll.platform.toolchain.ifacewriter.get_block(pll.name)
# Xilinx / Spartan 6
def test_s6_pll(self):
pll = S6PLL()
pll.register_clkin(Signal(), 100e6)
for i in range(pll.nclkouts_max):
pll.create_clkout(ClockDomain("clkout{}".format(i)), 200e6)
pll.compute_config()
def test_s6_dcm(self):
dcm = S6DCM()
dcm.register_clkin(Signal(), 100e6)
for i in range(dcm.nclkouts_max):
dcm.create_clkout(ClockDomain("clkout{}".format(i)), 200e6)
dcm.compute_config()
# Xilinx / 7-Series
def test_s7_pll(self):
pll = S7PLL()
pll.register_clkin(Signal(), 100e6)
for i in range(pll.nclkouts_max):
pll.create_clkout(ClockDomain("clkout{}".format(i)), 200e6)
pll.compute_config()
def test_s7_mmcm(self):
mmcm = S7MMCM()
mmcm.register_clkin(Signal(), 100e6)
for i in range(mmcm.nclkouts_max):
mmcm.create_clkout(ClockDomain("clkout{}".format(i)), 200e6)
mmcm.compute_config()
# Xilinx / Ultrascale
def test_us_pll(self):
pll = USPLL()
pll.register_clkin(Signal(), 100e6)
for i in range(pll.nclkouts_max):
pll.create_clkout(ClockDomain("clkout{}".format(i)), 200e6)
pll.compute_config()
def test_us_mmcm(self):
mmcm = USMMCM()
mmcm.register_clkin(Signal(), 100e6)
for i in range(mmcm.nclkouts_max):
mmcm.create_clkout(ClockDomain("clkout{}".format(i)), 200e6)
mmcm.compute_config()
# Xilinx / Ultrascale Plus
def test_us_ppll(self):
pll = USPPLL()
pll.register_clkin(Signal(), 100e6)
for i in range(pll.nclkouts_max):
pll.create_clkout(ClockDomain("clkout{}".format(i)), 200e6)
pll.compute_config()
def test_us_pmmcm(self):
mmcm = USPMMCM()
mmcm.register_clkin(Signal(), 100e6)
for i in range(mmcm.nclkouts_max):
mmcm.create_clkout(ClockDomain("clkout{}".format(i)), 200e6)
mmcm.compute_config()
# Intel / CycloneIV
def test_cycloneiv_pll(self):
pll = CycloneIVPLL()
pll.register_clkin(Signal(), 50e6)
for i in range(pll.nclkouts_max):
pll.create_clkout(ClockDomain("clkout{}".format(i)), 100e6)
pll.compute_config()
# Intel / CycloneV
def test_cyclonev_pll(self):
pll = CycloneVPLL()
pll.register_clkin(Signal(), 50e6)
for i in range(pll.nclkouts_max):
pll.create_clkout(ClockDomain("clkout{}".format(i)), 100e6)
pll.compute_config()
# Intel / Cyclone10
def test_cyclone10_pll(self):
pll = Cyclone10LPPLL()
pll.register_clkin(Signal(), 50e6)
for i in range(pll.nclkouts_max):
pll.create_clkout(ClockDomain("clkout{}".format(i)), 100e6)
pll.compute_config()
# Intel / Max10
def test_max10_pll(self):
pll = Max10PLL()
pll.register_clkin(Signal(), 50e6)
for i in range(pll.nclkouts_max):
pll.create_clkout(ClockDomain("clkout{}".format(i)), 100e6)
pll.compute_config()
# Lattice / iCE40
def test_ice40_pll(self):
pll = iCE40PLL()
pll.register_clkin(Signal(), 100e6)
for i in range(pll.nclkouts_max):
pll.create_clkout(ClockDomain("clkout{}".format(i)), 200e6)
pll.compute_config()
def test_ice40_pll_rejects_out_of_range_frequencies(self):
pll = iCE40PLL()
with self.assertRaisesRegex(ValueError, "Input clock frequency"):
pll.register_clkin(Signal(), 1e9)
pll.register_clkin(Signal(), 100e6)
with self.assertRaisesRegex(ValueError, "Output clock frequency"):
pll.create_clkout(ClockDomain("clkout"), 1e9)
def test_xilinx_plls_reject_out_of_range_clkin(self):
test_cases = [
(S6PLL, 600e6),
(S7PLL, 1e9),
(S7MMCM, 1.2e9),
(USPLL, 50e6),
]
for pll_cls, clkin_freq in test_cases:
with self.subTest(pll=pll_cls.__name__):
pll = pll_cls()
with self.assertRaisesRegex(ValueError, "Input clock frequency"):
pll.register_clkin(Signal(), clkin_freq)
def test_intel_plls_reject_out_of_range_frequencies(self):
pll = CycloneVPLL()
with self.assertRaisesRegex(ValueError, "Input clock frequency"):
pll.register_clkin(Signal(), 1e9)
pll.register_clkin(Signal(), 50e6)
with self.assertRaisesRegex(ValueError, "Output clock frequency"):
pll.create_clkout(ClockDomain("clkout"), 600e6)
def test_agilex_pll_rejects_out_of_range_clkout(self):
pll = Agilex5PLL(platform=None)
pll.register_clkin(Signal(), 100e6)
with self.assertRaisesRegex(ValueError, "Output clock frequency"):
pll.create_clkout(ClockDomain("clkout"), 1e9)
def test_lattice_plls_reject_out_of_range_frequencies(self):
test_cases = [
(ECP5PLL, 100e6, 1e9, 1e9),
(NXPLL, 100e6, 1e9, 1e9),
]
for pll_cls, valid_clkin_freq, invalid_clkin_freq, invalid_clkout_freq in test_cases:
with self.subTest(pll=pll_cls.__name__):
pll = pll_cls()
with self.assertRaisesRegex(ValueError, "Input clock frequency"):
pll.register_clkin(Signal(), invalid_clkin_freq)
pll.register_clkin(Signal(), valid_clkin_freq)
with self.assertRaisesRegex(ValueError, "Output clock frequency"):
pll.create_clkout(ClockDomain("clkout"), invalid_clkout_freq)
def test_ice40_pll_finalize_accepts_max_input_frequency(self):
pll = iCE40PLL()
pll.register_clkin(Signal(), 133e6)
pll.create_clkout(ClockDomain("clkout"), 133e6)
pll.get_fragment()
# Lattice / ECP5
def test_ecp5_pll(self):
pll = ECP5PLL()
pll.register_clkin(Signal(), 100e6)
for i in range(pll.nclkouts_max-1):
pll.create_clkout(ClockDomain("clkout{}".format(i)), 200e6, uses_dpa=(i != 0))
pll.expose_dpa()
pll.compute_config()
# Test corner cases that have historically had trouble:
pll = ECP5PLL()
pll.register_clkin(Signal(), 100e6)
pll.create_clkout(ClockDomain("clkout1"), 350e6)
pll.create_clkout(ClockDomain("clkout2"), 350e6)
pll.create_clkout(ClockDomain("clkout3"), 175e6)
pll.create_clkout(ClockDomain("clkout4"), 175e6)
pll.compute_config()
def test_ecp5_dynamic_delay(self):
delay = ECP5DynamicDelay(i=Signal(), o=Signal(), taps=Signal(7))
delay = ECP5DynamicDelay()
# Lattice / NX
def test_nxpll(self):
pll = NXPLL()
pll.register_clkin(Signal(), 100e6)
for i in range(pll.nclkouts_max):
pll.create_clkout(ClockDomain("clkout{}".format(i)), 200e6)
pll.compute_config()
def test_nxosca_hfsdc_clk_can_be_used_without_hf_clk(self):
osc = NXOSCA()
osc.create_hfsdc_clk(ClockDomain("hfsdc"), 45e6)
osc.do_finalize()
self.assertEqual(osc.params["p_HF_SED_SEC_DIV"], "9")
self.assertIn("o_HFSDCOUT", osc.params)
def test_nxosca_hf_and_hfsdc_clks_use_independent_divisors(self):
osc = NXOSCA()
osc.create_hf_clk(ClockDomain("hf"), 90e6)
osc.create_hfsdc_clk(ClockDomain("hfsdc"), 45e6)
osc.do_finalize()
self.assertEqual(osc.params["p_HF_CLK_DIV"], "4")
self.assertEqual(osc.params["p_HF_SED_SEC_DIV"], "9")
def test_nxosca_rejects_out_of_range_hf_clk(self):
osc = NXOSCA()
with self.assertRaisesRegex(ValueError, "HF clock frequency"):
osc.create_hf_clk(ClockDomain("hf"), 1e6)
def test_nxpll_finalize_is_quiet(self):
pll = NXPLL()
pll.register_clkin(Signal(), 100e6)
pll.create_clkout(ClockDomain("clkout"), 200e6)
stdout = io.StringIO()
with redirect_stdout(stdout):
pll.get_fragment()
self.assertEqual(stdout.getvalue(), "")
def test_gatemate_pll_rejects_invalid_output_config(self):
pll = GateMatePLL()
pll.register_clkin(Signal(), 50e6)
with self.assertRaisesRegex(ValueError, "Output clock frequency"):
pll.create_clkout(ClockDomain("clkout"), 300e6)
with self.assertRaisesRegex(ValueError, "Output clock phase"):
pll.create_clkout(ClockDomain("clkout"), 100e6, phase=45)
def test_ice40_pll_rejects_invalid_primitive(self):
with self.assertRaisesRegex(ValueError, "Unsupported iCE40 PLL primitive"):
iCE40PLL(primitive="SB_PLL40_BAD")
def test_xilinx_dps_csr_requires_sys_domain(self):
pll = S7PLL()
with self.assertRaisesRegex(ValueError, "sys clock domain"):
pll.expose_dps(clk_domain="idelay", with_csr=True)
pll.expose_dps(clk_domain="idelay", with_csr=False)
def test_gatemate_pll_rejects_invalid_constructor_args(self):
test_cases = [
("perf_mode", lambda: GateMatePLL(perf_mode="turbo"), "performance mode"),
("low_jitter", lambda: GateMatePLL(low_jitter=2), "low_jitter"),
("lock_req", lambda: GateMatePLL(lock_req=2), "lock_req"),
]
for name, pll_factory, message in test_cases:
with self.subTest(argument=name):
with self.assertRaisesRegex(ValueError, message):
pll_factory()
def test_plls_reject_non_positive_frequencies(self):
test_cases = [
("S7PLL", lambda: S7PLL(), 100e6),
("CycloneVPLL", lambda: CycloneVPLL(), 50e6),
("Agilex5PLL", lambda: Agilex5PLL(platform=None), 100e6),
("ECP5PLL", lambda: ECP5PLL(), 100e6),
("iCE40PLL", lambda: iCE40PLL(), 12e6),
("NXPLL", lambda: NXPLL(), 100e6),
("GateMatePLL", lambda: GateMatePLL(), 50e6),
("GW1NPLL", lambda: GW1NPLL("GW1N-9", "GW1N-9C"), 50e6),
("GW5APLL", lambda: GW5APLL("GW5A-25", "GW5A-25"), 50e6),
]
for name, pll_factory, clkin_freq in test_cases:
with self.subTest(pll=name, clock="clkin"):
pll = pll_factory()
with self.assertRaisesRegex(ValueError, "Input clock frequency"):
pll.register_clkin(Signal(name="clk"), 0)
with self.subTest(pll=name, clock="clkout"):
pll = pll_factory()
pll.register_clkin(Signal(name="clk"), clkin_freq)
with self.assertRaisesRegex(ValueError, "Output clock frequency"):
pll.create_clkout(ClockDomain("clkout"), 0)
def test_gw1n_osc_rejects_non_positive_frequency(self):
with self.assertRaisesRegex(ValueError, "Oscillator frequency"):
GW1NOSC("GW1N-4", 0)
def test_gw1n_osc_prefers_closest_frequency(self):
osc = GW1NOSC("GW1N-9", 110e6, margin=0.30)
self.assertEqual(osc.config["div"], 2)
self.assert_frequency_close(osc.config["freq"], 125e6, margin=1e-9)
def test_clocking_rejects_negative_margins(self):
test_cases = [
("S7PLL clkout", lambda: S7PLL().create_clkout(ClockDomain("clkout"), 100e6, margin=-1e-2)),
("GW1NOSC", lambda: GW1NOSC("GW1N-9", 100e6, margin=-1e-2)),
("GW1NPLL VCO", lambda: GW1NPLL("GW1N-9", "GW1N-9C", vco_margin=-1e-2)),
("NXOSCA HF", lambda: NXOSCA().create_hf_clk(ClockDomain("hf"), 45e6, margin=-1e-2)),
("Efinix", lambda: TITANIUMPLL(_FakeEfinixPlatform()).create_clkout(None, 100e6, margin=-1e-2)),
]
for name, clocking_factory in test_cases:
with self.subTest(clocking=name):
with self.assertRaisesRegex(ValueError, "margin"):
clocking_factory()
def test_efinix_pll_accepts_dyn_phase_pads(self):
pads = {
"shift_ena": Signal(),
"shift": Signal(),
"shift_sel": Signal(2),
}
pll = TITANIUMPLL(_FakeEfinixPlatform(), dyn_phase_shift_pads=pads)
block = self.get_efinix_pll_block(pll)
self.assertIs(block["shift_ena"], pads["shift_ena"])
self.assertIs(block["shift"], pads["shift"])
self.assertIs(block["shift_sel"], pads["shift_sel"])
def test_efinix_pll_rejects_incomplete_dyn_phase_pads(self):
with self.assertRaisesRegex(ValueError, "dyn_phase_shift_pads"):
TITANIUMPLL(_FakeEfinixPlatform(), dyn_phase_shift_pads={"shift_ena": Signal()})
def test_efinix_pll_rejects_explicit_clkout_on_trion(self):
pll = TRIONPLL(_FakeEfinixPlatform(family="Trion"))
with self.assertRaisesRegex(ValueError, "not supported for Trion"):
pll.create_clkout(None, 100e6, nclkout=0)
def test_efinix_pll_rejects_invalid_clkout_index(self):
pll = TITANIUMPLL(_FakeEfinixPlatform())
with self.assertRaisesRegex(ValueError, "nclkout must be >= 0"):
pll.create_clkout(None, 100e6, nclkout=-1)
with self.assertRaisesRegex(ValueError, "nclkout must be less than"):
pll.create_clkout(None, 100e6, nclkout=pll.nclkouts_max)
def test_efinix_pll_rejects_used_clkout(self):
pll = TITANIUMPLL(_FakeEfinixPlatform())
pll.create_clkout(None, 100e6, nclkout=0)
with self.assertRaisesRegex(ValueError, "already used"):
pll.create_clkout(None, 100e6, nclkout=0)
def test_efinix_pll_rejects_missing_free_clkout(self):
pll = TITANIUMPLL(_FakeEfinixPlatform())
for _ in range(pll.nclkouts_max):
pll.create_clkout(None, 100e6)
with self.assertRaisesRegex(ValueError, "No free clock output"):
pll.create_clkout(None, 100e6)
def test_efinix_pll_rejects_duplicate_feedback_clkout(self):
pll = TITANIUMPLL(_FakeEfinixPlatform())
pll.create_clkout(None, 100e6, is_feedback=True, nclkout=0)
with self.assertRaisesRegex(ValueError, "Feedback clock output"):
pll.create_clkout(None, 100e6, is_feedback=True, nclkout=1)
def test_efinix_pll_registers_name_only_internal_clkin(self):
pll = TITANIUMPLL(_FakeEfinixPlatform())
pll.register_clkin(None, 50e6, name="core_clk")
block = self.get_efinix_pll_block(pll)
self.assertEqual(block["input_clock_name"], "core_clk")
self.assertEqual(block["input_signal"], "core_clk")
self.assertEqual(block["input_clock"], "INTERNAL")
def test_efinix_pll_rejects_missing_internal_clkin_name(self):
pll = TITANIUMPLL(_FakeEfinixPlatform())
with self.assertRaisesRegex(ValueError, "No clkin name"):
pll.register_clkin(None, 50e6)
def test_efinix_pll_remaps_sparse_feedback_clkout(self):
pll = TRIONPLL(_FakeEfinixPlatform())
block = self.get_efinix_pll_block(pll)
block["input_freq"] = 50e6
pll.create_clkout(None, 100e6, nclkout=2, is_feedback=True)
pll.compute_config()
self.assertEqual(block["feedback"], 0)
self.assertEqual(len(block["clk_out"]), 1)
self.assertIn("CLKOUT0_DIV", block)
def test_efinix_pll_rejects_unconfigured_feedback_clkout(self):
pll = TRIONPLL(_FakeEfinixPlatform())
block = self.get_efinix_pll_block(pll)
block["input_freq"] = 50e6
block["feedback"] = 2
pll.create_clkout(None, 100e6, nclkout=0)
with self.assertRaisesRegex(ValueError, "Feedback clock output"):
pll.compute_config()
def test_efinix_pll_rejects_unsupported_phase(self):
pll = TRIONPLL(_FakeEfinixPlatform())
block = self.get_efinix_pll_block(pll)
block["input_freq"] = 50e6
pll.create_clkout(None, 100e6, phase=30, is_feedback=True)
with self.assertRaisesRegex(ValueError, "PLL clock phase"):
pll.compute_config()
def test_efinix_pll_tolerates_subhz_float_noise(self):
pll = TRIONPLL(_FakeEfinixPlatform())
block = self.get_efinix_pll_block(pll)
block["input_freq"] = 50e6
pll.create_clkout(None, 100e6 + 1e-7, is_feedback=True)
pll.compute_config()
self.assertIn("CLKOUT0_DIV", block)
def test_plls_reject_missing_clkout(self):
test_cases = [
("S7PLL", lambda: S7PLL(), 100e6),
("CycloneVPLL", lambda: CycloneVPLL(), 50e6),
("Agilex5PLL", lambda: Agilex5PLL(platform=None), 100e6),
("ECP5PLL", lambda: ECP5PLL(), 100e6),
("iCE40PLL", lambda: iCE40PLL(), 12e6),
("NXPLL", lambda: NXPLL(), 100e6),
("GW1NPLL", lambda: GW1NPLL("GW1N-9", "GW1N-9C"), 50e6),
("GW5APLL", lambda: GW5APLL("GW5A-25", "GW5A-25"), 50e6),
]
for name, pll_factory, clkin_freq in test_cases:
with self.subTest(pll=name):
pll = pll_factory()
pll.register_clkin(Signal(name="clk"), clkin_freq)
with self.assertRaisesRegex(ValueError, "At least one output clock"):
pll.compute_config()
def test_pll_rejects_missing_clkin(self):
pll = S7PLL()
pll.create_clkout(ClockDomain("clkout"), 100e6)
with self.assertRaisesRegex(ValueError, "Input clock"):
pll.compute_config()
def test_pll_rejects_too_many_clkouts(self):
pll = S7PLL()
for i in range(pll.nclkouts_max):
pll.create_clkout(ClockDomain("clkout{}".format(i)), 100e6)
with self.assertRaisesRegex(ValueError, "Cannot add more"):
pll.create_clkout(ClockDomain("clkout_extra"), 100e6)
def test_plls_reject_duplicate_clkout_domain(self):
test_cases = [
("S6PLL", lambda: S6PLL(), 100e6, 200e6),
("S6DCM", lambda: S6DCM(), 100e6, 200e6),
("S7PLL", lambda: S7PLL(), 100e6, 200e6),
("S7MMCM", lambda: S7MMCM(), 100e6, 200e6),
("USPLL", lambda: USPLL(), 100e6, 200e6),
("USMMCM", lambda: USMMCM(), 100e6, 200e6),
("USPPLL", lambda: USPPLL(), 100e6, 200e6),
("USPMMCM", lambda: USPMMCM(), 100e6, 200e6),
("CycloneIVPLL", lambda: CycloneIVPLL(), 50e6, 100e6),
("CycloneVPLL", lambda: CycloneVPLL(), 50e6, 100e6),
("Cyclone10PLL", lambda: Cyclone10LPPLL(), 50e6, 100e6),
("Max10PLL", lambda: Max10PLL(), 50e6, 100e6),
("Agilex5PLL", lambda: Agilex5PLL(platform=None), 100e6, 100e6),
("ECP5PLL", lambda: ECP5PLL(), 100e6, 200e6),
("iCE40PLL", lambda: iCE40PLL(), 12e6, 48e6),
("NXPLL", lambda: NXPLL(), 100e6, 200e6),
("GateMatePLL", lambda: GateMatePLL(), 50e6, 100e6),
("GW1NPLL", lambda: GW1NPLL("GW1N-9", "GW1N-9C"), 50e6, 100e6),
("GW5APLL", lambda: GW5APLL("GW5A-25", "GW5A-25"), 50e6, 100e6),
]
for name, pll_factory, clkin_freq, clkout_freq in test_cases:
with self.subTest(pll=name):
pll = pll_factory()
cd = ClockDomain("clkout")
pll.register_clkin(Signal(), clkin_freq)
pll.create_clkout(cd, clkout_freq)
with self.assertRaisesRegex(ValueError, "already driven"):
pll.create_clkout(cd, clkout_freq)
def test_pll_rejects_duplicate_clkout_domain_name(self):
pll = S7PLL()
pll.register_clkin(Signal(), 100e6)
pll.create_clkout(ClockDomain("clkout"), 100e6)
with self.assertRaisesRegex(ValueError, "already driven"):
pll.create_clkout(ClockDomain("clkout"), 200e6)
def test_efinix_pll_rejects_duplicate_clkout_domain(self):
pll = TITANIUMPLL(_FakeEfinixPlatform())
cd = ClockDomain("clkout")
pll.create_clkout(cd, 100e6)
with self.assertRaisesRegex(ValueError, "already driven"):
pll.create_clkout(cd, 100e6)
def test_nxosca_rejects_duplicate_clkout_domain(self):
osc = NXOSCA()
cd = ClockDomain("clkout")
osc.create_hf_clk(cd, 45e6)
with self.assertRaisesRegex(ValueError, "already driven"):
osc.create_hfsdc_clk(cd, 45e6)
def test_gw5a_pll_rejects_unreachable_high_clkout(self):
pll = GW5APLL("GW5A-25", "GW5A-25")
pll.register_clkin(Signal(), 50e6)
pll.create_clkout(ClockDomain("clkout"), 10e9)
with self.assertRaisesRegex(ValueError, r"No PLL config found.*ClkIn=50.00MHz.*ClkOut0=10000.00MHz"):
pll.compute_config()
def test_gw1n_pll_rejects_multiple_nonzero_phases(self):
pll = GW1NPLL("GW1N-9", "GW1N-9C")
pll.register_clkin(Signal(), 50e6)
pll.create_clkout(ClockDomain("clkout0"), 100e6, phase=90)
pll.create_clkout(ClockDomain("clkout1"), 100e6, phase=180)
with self.assertRaisesRegex(ValueError, "only one non-zero phase"):
pll.compute_config()
def test_pll_configs_prefer_closest_frequency(self):
test_cases = [
(S7PLL, 100e6, 70e6, 0.20, "clkout0_freq"),
(USPMMCM, 100e6, 70e6, 0.25, "clkout0_freq"),
(ECP5PLL, 100e6, 10e6, 0.20, "clko0_freq"),
(NXPLL, 100e6, 10e6, 0.20, "clko0_freq"),
(iCE40PLL, 12e6, 16.125e6, 0.20, "clkout_freq"),
]
for pll_cls, clkin_freq, clkout_freq, margin, freq_key in test_cases:
with self.subTest(pll=pll_cls.__name__):
pll = pll_cls()
pll.register_clkin(Signal(), clkin_freq)
pll.create_clkout(ClockDomain("clkout"), clkout_freq, margin=margin)
config = pll.compute_config()
self.assert_frequency_close(config[freq_key], clkout_freq, margin=1e-9)
def test_intel_pll_config_score_balances_outputs(self):
pll = CycloneVPLL()
pll.register_clkin(Signal(), 50e6)
pll.create_clkout(ClockDomain("clkout0"), 20e6, margin=.05)
pll.create_clkout(ClockDomain("clkout1"), 31e6, margin=.05)
config = pll.compute_config()
self.assert_frequency_close(config["clk0_freq"], 20e6, margin=1e-9)
self.assert_frequency_close(config["clk1_freq"], 31e6, margin=1e-9)
def test_pll_configs_match_requested_frequencies(self):
test_cases = [
(S7PLL, 100e6, 200e6, "clkout0_freq", 0),
(S7MMCM, 100e6, 125e6, "clkout0_freq", 90),
(CycloneVPLL, 50e6, 100e6, "clk0_freq", 45),
(ECP5PLL, 100e6, 200e6, "clko0_freq", 90),
(iCE40PLL, 12e6, 48e6, "clkout_freq", 0),
]
for pll_cls, clkin_freq, clkout_freq, freq_key, phase in test_cases:
with self.subTest(pll=pll_cls.__name__):
pll = pll_cls()
pll.register_clkin(Signal(), clkin_freq)
if phase:
pll.create_clkout(ClockDomain("clkout"), clkout_freq, phase=phase)
else:
pll.create_clkout(ClockDomain("clkout"), clkout_freq)
config = pll.compute_config()
self.assert_frequency_close(config[freq_key], clkout_freq)
if phase:
self.assertEqual(config[freq_key.replace("freq", "phase")], phase)
def test_agilex_pll_uses_relative_frequency_margin(self):
pll = Agilex5PLL(platform=None)
pll.register_clkin(Signal(), 100e6)
pll.create_clkout(ClockDomain("clkout"), 123.4e6, margin=1e-2)
config = pll.compute_config()
self.assert_frequency_close(config["clk0_freq"], 123.4e6)
def test_common_pll_finalize_smoke(self):
test_cases = [
(S6PLL, 100e6, 200e6),
(S6DCM, 100e6, 200e6),
(S7PLL, 100e6, 200e6),
(S7MMCM, 100e6, 200e6),
(USPLL, 100e6, 200e6),
(USMMCM, 100e6, 200e6),
(USPPLL, 100e6, 200e6),
(USPMMCM, 100e6, 200e6),
(CycloneIVPLL, 50e6, 100e6),
(CycloneVPLL, 50e6, 100e6),
(Cyclone10LPPLL, 50e6, 100e6),
(Max10PLL, 50e6, 100e6),
(ECP5PLL, 100e6, 200e6),
(iCE40PLL, 12e6, 48e6),
(NXPLL, 100e6, 200e6),
(GateMatePLL, 50e6, 100e6),
(lambda: GW1NPLL("GW1N-9", "GW1N-9C"), 50e6, 100e6),
(lambda: GW5APLL("GW5A-25", "GW5A-25"), 50e6, 100e6),
]
for pll_cls, clkin_freq, clkout_freq in test_cases:
pll = pll_cls()
with self.subTest(pll=pll.__class__.__name__):
pll.register_clkin(Signal(), clkin_freq)
pll.create_clkout(ClockDomain("clkout"), clkout_freq)
pll.get_fragment()
fb851434fThis file is pinned to the selected release. Download the original to open it in your design tool.