Skip to content

Commit

Permalink
Merge pull request #157 from analogdevicesinc/tfcollins/fpga-update
Browse files Browse the repository at this point in the history
Add constraint for ref clock on FPGAs to be related to core clock
  • Loading branch information
tfcollins authored Sep 27, 2024
2 parents 2118925 + c801c74 commit 77bf513
Show file tree
Hide file tree
Showing 13 changed files with 97 additions and 6 deletions.
2 changes: 1 addition & 1 deletion adijif/clocks/hmc7044.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,7 @@ def set_requested_clocks(
od = self.model.Intermediate(eo * odd + (1 - eo) * even * 2)

elif self.solver == "CPLEX":
od = self._convert_input(self._d, "d_" + str(out_freq))
od = self._convert_input(self._d, f"d_{out_freq}_{d_n}")

self._add_equation(
[
Expand Down
3 changes: 3 additions & 0 deletions adijif/converters/ad9081.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ class ad9081_rx(adc, ad9081_core):
"""AD9081 Receive model."""

name = "AD9081_RX"
converter_type = "adc"

converter_clock_min = 1.45e9
converter_clock_max = 4e9
Expand Down Expand Up @@ -393,6 +394,7 @@ class ad9081_tx(dac, ad9081_core):
"""AD9081 Transmit model."""

name = "AD9081_TX"
converter_type = "dac"

converter_clock_min = 2.9e9
converter_clock_max = 12e9
Expand Down Expand Up @@ -480,6 +482,7 @@ class ad9081(ad9081_core):
converter_clock_max = ad9081_rx.converter_clock_max
quick_configuration_modes: Dict[str, Any] = {}
_nested = ["adc", "dac"]
converter_type = "adc_dac"

def __init__(
self, model: Union[GEKKO, CpoModel] = None, solver: str = None
Expand Down
1 change: 1 addition & 0 deletions adijif/converters/ad9144.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class ad9144(ad9144_bf):
"""

name = "AD9144"
converter_type = "DAC"

# JESD parameters
_jesd_params_to_skip_check = ["DualLink", "K"]
Expand Down
1 change: 1 addition & 0 deletions adijif/converters/ad9680.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class ad9680(ad9680_bf):
"""

name = "AD9680"
converter_type = "adc"

# JESD parameters
_jesd_params_to_skip_check = ["DualLink", "CS", "N", "HD"]
Expand Down
3 changes: 3 additions & 0 deletions adijif/converters/adrv9009.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ class adrv9009_rx(adc, adrv9009_clock_common, adrv9009_core):

quick_configuration_modes = {"jesd204b": quick_configuration_modes_rx}
name = "ADRV9009_RX"
converter_type = "adc"

# JESD configurations
K_available = [*np.arange(1, 32 + 1)]
Expand Down Expand Up @@ -232,6 +233,7 @@ class adrv9009_tx(dac, adrv9009_clock_common, adrv9009_core):

quick_configuration_modes = {"jesd204b": quick_configuration_modes_tx}
name = "ADRV9009_TX"
converter_type = "dac"

# JESD configurations
K_available = [*np.arange(1, 32 + 1)]
Expand Down Expand Up @@ -269,6 +271,7 @@ class adrv9009(adrv9009_core):
name = "ADRV9009"
solver = "CPLEX"
_nested = ["adc", "dac"]
converter_type = "adc_dac"

def __init__(
self, model: Union[GEKKO, CpoModel] = None, solver: str = None
Expand Down
10 changes: 10 additions & 0 deletions adijif/converters/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,16 @@ def get_current_jesd_mode_settings(self) -> Dict:
current_config[attr] = getattr(self, attr)
return current_config

@property
@abstractmethod
def converter_type(self) -> str:
"""Type of converter. ADC or DAC.
Returns:
str: Type of converter
"""
raise NotImplementedError

@property
@abstractmethod
def clocking_option_available(self) -> List[str]:
Expand Down
53 changes: 53 additions & 0 deletions adijif/fpgas/xilinx.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ class xilinx(xilinx_bf):
cases, the ref clock and device clock can be the same."""
force_separate_device_clock: bool = False

"""Constrain reference clock to be specific values. Options:
- CORE_CLOCK: Make reference clock the same as the core clock (LR/40 or LR/66)
- CORE_CLOCK_DIV2: Make reference clock the same as the core clock divided by 2
- Unconstrained: No constraints on reference clock. Simply meet PLL constraints
"""
_ref_clock_constraint = "CORE_CLOCK"

max_serdes_lanes = 24

hdl_core_version = 2.1
Expand Down Expand Up @@ -122,6 +129,42 @@ class xilinx(xilinx_bf):

configs = [] # type: ignore

@property
def ref_clock_constraint(self) -> str:
"""Get reference clock constraint.
Reference clock constraint can be set to:
- CORE_CLOCK: Make reference clock the same as the core clock (LR/40 or LR/66)
- CORE_CLOCK_DIV2: Make reference clock the same as the core clock divided by 2
- Unconstrained: No constraints on reference clock. Simply meet PLL constraints
Returns:
str: Reference clock constraint.
"""
return self._ref_clock_constraint

@ref_clock_constraint.setter
def ref_clock_constraint(self, value: str) -> None:
"""Set reference clock constraint.
Reference clock constraint can be set to:
- CORE_CLOCK: Make reference clock the same as the core clock (LR/40 or LR/66)
- CORE_CLOCK_DIV2: Make reference clock the same as the core clock divided by 2
- Unconstrained: No constraints on reference clock. Simply meet PLL constraints
Args:
value (str): Reference clock constraint.
Raises:
Exception: Invalid ref_clock_constraint selection.
"""
if value not in ["CORE_CLOCK", "CORE_CLOCK_DIV2", "Unconstrained"]:
raise Exception(
f"Invalid ref_clock_constraint {value}, "
+ "options are CORE_CLOCK, CORE_CLOCK_DIV2, Unconstrained"
)
self._ref_clock_constraint = value

@property
def out_clk_select(self) -> Union[int, float]:
"""Get current PLL clock output mux options for link layer clock.
Expand Down Expand Up @@ -848,6 +891,16 @@ def _setup_quad_tile(
[fpga_ref >= self.ref_clock_min, fpga_ref <= self.ref_clock_max]
)

if converter.jesd_class == "jesd204b":
core_clock = converter.bit_clock / 40
else:
core_clock = converter.bit_clock / 66

if self.ref_clock_constraint == "CORE_CLOCK":
self._add_equation([fpga_ref == core_clock])
elif self.ref_clock_constraint == "CORE_CLOCK_DIV2":
self._add_equation([fpga_ref == core_clock / 2])

# CPLL -> VCO = FPGA_REF * N1*N2/M
# PLLOUT = VCO
# LR = PLLOUT * 2/D
Expand Down
24 changes: 20 additions & 4 deletions adijif/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,17 +314,33 @@ def solve(self) -> Dict:
# Setup clock chip
self.clock._setup(self.vcxo)
self.fpga.configs = [] # reset
serdes_used: float = 0
serdes_used_tx: int = 0
serdes_used_rx: int = 0
sys_refs = []

for conv in convs:
if conv._nested: # MxFE, Transceivers
for name in conv._nested:
serdes_used += getattr(conv, name).L
ctype = getattr(conv, name).converter_type.lower()
if ctype == "adc":
serdes_used_rx += getattr(conv, name).L
elif ctype == "dac":
serdes_used_tx += getattr(conv, name).L
else:
raise Exception(f"Unknown converter type {ctype}")
else:
serdes_used += conv.L
ctype = conv.converter_type.lower()
if ctype == "adc":
serdes_used_rx += conv.L
elif ctype == "dac":
serdes_used_tx += conv.L
else:
raise Exception(f"Unknown converter type: {ctype}")

if serdes_used > self.fpga.max_serdes_lanes:
if (
serdes_used_rx > self.fpga.max_serdes_lanes
or serdes_used_tx > self.fpga.max_serdes_lanes
):
raise Exception(
"Max SERDES lanes exceeded. {} only available".format(
self.fpga.max_serdes_lanes
Expand Down
1 change: 1 addition & 0 deletions examples/ad9081_rx_hmc7044_ext_pll_adf4371.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

sys = adijif.system("ad9081", "hmc7044", "xilinx", vcxo, solver="CPLEX")
sys.fpga.setup_by_dev_kit_name("zcu102")
sys.fpga.ref_clock_constraint = "Unconstrained"
sys.fpga.sys_clk_select = "GTH34_SYSCLK_QPLL0" # Use faster QPLL
sys.fpga.out_clk_select = "XCVR_PROGDIV_CLK" # force reference to be core clock rate
sys.converter.adc.sample_clock = 2900000000 / (8 * 6)
Expand Down
1 change: 1 addition & 0 deletions examples/ad9081_rxtx_hmc7044.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

sys = adijif.system("ad9081", "hmc7044", "xilinx", vcxo, solver="CPLEX")
sys.fpga.setup_by_dev_kit_name("zcu102")
sys.fpga.ref_clock_constraint = "Unconstrained"
sys.fpga.sys_clk_select = "GTH34_SYSCLK_QPLL0" # Use faster QPLL
sys.converter.clocking_option = "integrated_pll"
sys.fpga.out_clk_select = "XCVR_PROGDIV_CLK" # force reference to be core clock rate
Expand Down
1 change: 1 addition & 0 deletions examples/ad9082_rxtx_hmc7044.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

sys = adijif.system("ad9082", "hmc7044", "xilinx", vcxo, solver="CPLEX")
sys.fpga.setup_by_dev_kit_name("zcu102")
sys.fpga.ref_clock_constraint = "Unconstrained"
sys.fpga.sys_clk_select = "GTH34_SYSCLK_QPLL0" # Use faster QPLL
sys.converter.clocking_option = "integrated_pll"
sys.fpga.out_clk_select = "XCVR_PROGDIV_CLK" # force reference to be core clock rate
Expand Down
1 change: 1 addition & 0 deletions tests/test_adf4371.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def test_adf4371_ad9081_sys_example():

sys = adijif.system("ad9081", "hmc7044", "xilinx", vcxo, solver="CPLEX")
sys.fpga.setup_by_dev_kit_name("zcu102")
sys.fpga.ref_clock_constraint = "Unconstrained"
sys.fpga.sys_clk_select = "GTH34_SYSCLK_QPLL0" # Use faster QPLL
sys.fpga.out_clk_select = (
"XCVR_PROGDIV_CLK" # force reference to be core clock rate
Expand Down
2 changes: 1 addition & 1 deletion tests/test_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def test_nested_converter_lane_count_valid():


def test_nested_converter_lane_count_exceeds_fpga_lane_count():
fpga_L = 2
fpga_L = 1

sys = adijif.system("adrv9009", "ad9528", "xilinx", 122.88e6)

Expand Down

0 comments on commit 77bf513

Please sign in to comment.