Skip to content

Commit

Permalink
WIP: fixing validation of clocks. Needs more checking.
Browse files Browse the repository at this point in the history
  • Loading branch information
kammoh committed Jun 9, 2024
1 parent 148ddba commit d818e47
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 49 deletions.
3 changes: 3 additions & 0 deletions examples/mixed_language/blink/blinky.xeda.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[rtl]
sources = ["blink.sv"]
top = "blink"
# clocks = [{port = "clk"}]
clock.port = "clk"

[tb]
Expand All @@ -11,6 +13,7 @@ cxxrtl.filename = "blink.cpp"
[flows.yosys_fpga]
fpga.vendor = "xilinx"
fpga.family = "xc7"
clock.period = 5.0

[flows.vivado_synth]
fpga.part = "xc7a100tftg256-2L"
Expand Down
49 changes: 17 additions & 32 deletions src/xeda/design.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,44 +407,38 @@ class RtlSettings(DVSettings):
""",
)
# preferred way to specify a design's clock ports:
clocks: Dict[str, Clock] = {}
clocks: list[Clock] = []
# short-hand alternatives for a single clock designs:
clock: Optional[Clock] = None # DEPRECATED # TODO remove
clock_port: Optional[str] = None # TODO remove?

@root_validator(pre=True)
def rtl_settings_validate(cls, values): # pylint: disable=no-self-argument
"""copy equivalent clock fields (backward compatibility)"""
clock = values.get("clock")
clock_port = values.get("clock_port")
clock = values.get("clock") or values.get("clock_port")
clocks = values.get("clocks")

def conv_clock(clock):
if isinstance(clock, dict):
if isinstance(clock, Clock):
return clock
elif isinstance(clock, dict):
clock = Clock(**clock)
elif isinstance(clock, str):
clock = Clock(port=clock_port)
clock = Clock(port=clock)
return clock

if clocks is None:
clocks = {}
elif isinstance(clocks, list):
clocks = {clk.name or clk.port: clk for clk in map(conv_clock, clocks) if clk}
if not clock:
if clock_port:
clock = Clock(port=clock_port)
elif len(clocks) == 1:
clock = list(clocks.values())[0]
if clock:
clock = conv_clock(clock)
if not clock_port:
clock_port = clock.port
if not clocks:
clocks = {"main_clock": clock}
values["clock"] = clock
if clock:
clock = conv_clock(clock)
clocks = [clock]
else:
clocks = []
else:
assert isinstance(clocks, list)
clocks = [conv_clock(clk) for clk in clocks]
values["clocks"] = clocks
if clock_port:
values["clock_port"] = clock_port
if clocks:
values["clock"] = clocks[0]
return values


Expand Down Expand Up @@ -743,16 +737,7 @@ def process_compatibility(cls, data: Dict[str, Any]) -> Dict[str, Any]:
clocks = data.pop("clocks", None)
if clocks is None:
clock = data.pop("clock", None)
if clock:
clocks = list(clock) if isinstance(clock, (list, tuple)) else [clock]
if clocks and isinstance(clocks, (list, tuple)):
if all(isinstance(c, str) for c in clocks):
clocks = {c: {"port": c} for c in clocks}
elif all(isinstance(c, dict) for c in clocks):
clocks = {
c.get("name", c.get("port")): {"port": c.get("port"), "name": c.get("name")}
for c in clocks
}
clocks = [clock]
data["rtl"] = {
"sources": data.pop("sources", []),
"generator": data.pop("generator", None),
Expand Down
37 changes: 26 additions & 11 deletions src/xeda/flow/synth.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from pydantic import confloat

from ..dataclass import Field, XedaBaseModel, root_validator, validator
from ..design import Design
from ..design import Design, Clock
from ..units import convert_unit
from ..utils import first_value, first_key
from .flow import Flow, FlowSettingsError
Expand Down Expand Up @@ -103,6 +103,18 @@ def root_validate_phys_clock(cls, values: Dict[str, Any]) -> Dict[str, Any]:
return values


def find_matching_clock(design_clocks: list[Clock], name: str):
if len(design_clocks) == 1:
return design_clocks[0]
for clock in design_clocks:
if clock.name == name:
return clock
for clock in design_clocks:
if clock.port == name:
return clock
return None


class SynthFlow(Flow, metaclass=ABCMeta):
"""Superclass of synthesis flows"""

Expand Down Expand Up @@ -181,22 +193,22 @@ def __init__(self, flow_settings: Settings, design: Design, run_path: Path):
clock_name = first_key(flow_settings.clocks)
assert clock_name is not None
clock_obj = flow_settings.clocks.pop(clock_name)
design_clock = first_value(design.rtl.clocks)
design_clock = design.rtl.clocks[0]
assert design_clock is not None
if not clock_name:
clock_name = design_clock.name
assert clock_name is not None
assert clock_obj is not None
if not clock_obj.port:
clock_obj.port = design_clock.port
if not clock_obj.name:
clock_obj.name = design_clock.name
clock_name = clock_obj.name
assert clock_name is not None
clock_obj.name = clock_name
flow_settings.clocks[clock_name] = clock_obj
for clock_name, physical_clock in flow_settings.clocks.items():
if not physical_clock.port:
if clock_name not in design.rtl.clocks:
if clock_name not in (clk.name for clk in design.rtl.clocks):
if design.rtl.clocks:
msg = "Physical clock {} has no corresponding clock port in design. Existing clocks: {}".format(
clock_name, ", ".join(c for c in design.rtl.clocks)
)
msg = f"Physical clock {clock_name} has no corresponding clock port in design. Existing clocks: {", ".join(c.name for c in design.rtl.clocks if c and c.name)}"
else:
msg = f"No clock ports specified in 'design.rtl', while physical '{clock_name}' is set in flow settings. Set corresponding design clocks via 'design.rtl.clocks' (for multiple clocks) or 'design.rtl.clock.port' (for a single clock)"
raise FlowSettingsError(
Expand All @@ -210,9 +222,12 @@ def __init__(self, flow_settings: Settings, design: Design, run_path: Path):
],
self.Settings,
)
physical_clock.port = design.rtl.clocks[clock_name].port
matched_clock = find_matching_clock(design.rtl.clocks, clock_name)
if matched_clock:
physical_clock.port = matched_clock.port
flow_settings.clocks[clock_name] = physical_clock
for clock_name, clock in design.rtl.clocks.items():
for clock in design.rtl.clocks:
clock_name = clock.name
if clock.port not in (c.port for c in flow_settings.clocks.values()):
log.critical(
"No clock period or frequency was specified for clock: %s (design clock port: '%s')",
Expand Down
2 changes: 1 addition & 1 deletion src/xeda/flow_runner/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def translate_filename(src: DesignSource) -> str:
rtl["attributes"] = design.rtl.attributes
rtl["parameters"] = design.rtl.parameters
rtl["top"] = design.rtl.top
rtl["clocks"] = list(map(lambda kv: (kv[0], kv[1].dict()), design.rtl.clocks.items()))
rtl["clocks"] = design.rtl.clocks
# FIXME add src type/attributes
tb["sources"] = [
str(remote_sources_path / translate_filename(src)) for src in design.tb.sources
Expand Down
4 changes: 2 additions & 2 deletions src/xeda/flows/vivado/vivado_postsynthsim.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from ...flow import FlowFatalError
from ...dataclass import Field
from ...design import DesignSource, RtlSettings
from ...utils import SDF
from ...utils import SDF, first_value
from .vivado_sim import VivadoSim
from .vivado_synth import VivadoSynth

Expand Down Expand Up @@ -43,7 +43,7 @@ def init(self) -> None:
if ss.synth.input_delay is None:
input_delay = 0.0
# need to explicitly set synth.input_delay if this input is being captured by a different clock
main_clock = ss.synth.clocks.get("main_clock")
main_clock = ss.synth.clocks.get("main_clock") or first_value(ss.synth.clocks)
if main_clock:
input_delay = 0.25 * main_clock.period
ss.synth.input_delay = input_delay
Expand Down
4 changes: 2 additions & 2 deletions src/xeda/flows/vivado/vivado_synth.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@ def run(self):
}

if not self.design.rtl.clocks:
log.critical(
"No clocks specified for top RTL design. Continuing with synthesis anyways."
log.warning(
"No clocks specified for top RTL design."
)

clock_xdc_path = self.copy_from_template("clock.xdc")
Expand Down
2 changes: 1 addition & 1 deletion src/xeda/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ def settings_to_dict(
)
key, val = sp
set_hierarchy(res, key, conv(val))
elif isinstance(override, dict):
elif override and isinstance(override, dict):
for key, val in override.items():
set_hierarchy(res, key, conv(val))
return res
Expand Down

0 comments on commit d818e47

Please sign in to comment.