From a295438af482cfb1a22cf00075350a707d63540b Mon Sep 17 00:00:00 2001 From: Kamyar Mohajerani Date: Thu, 14 Nov 2024 13:56:14 -0500 Subject: [PATCH] convert str value for list fields to a list by splitting around ',' --- src/xeda/dataclass.py | 11 +++++- src/xeda/design.py | 2 ++ src/xeda/flows/verilator/__init__.py | 34 +++++++++++++------ .../yosys/templates/yosys_fpga_synth.tcl | 6 ++-- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/xeda/dataclass.py b/src/xeda/dataclass.py index 6cde59b..591eba3 100644 --- a/src/xeda/dataclass.py +++ b/src/xeda/dataclass.py @@ -6,7 +6,7 @@ import logging from abc import ABCMeta from functools import cached_property -from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Type, TypeVar +from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Type, TypeVar, get_origin import attrs @@ -20,6 +20,7 @@ root_validator, validator, ) +from pydantic.fields import ModelField, SHAPE_LIST if TYPE_CHECKING: from pydantic.error_wrappers import ErrorDict @@ -85,6 +86,14 @@ def invalidate_cached_properties(self): log.debug("invalidating: %s", str(key)) self.__dict__.pop(key, None) + @validator("*", pre=True, always=False) + def _base_all_fields_validator(cls, v, field: ModelField): + if v is not None: + origin = get_origin(field.annotation) + if field.shape == SHAPE_LIST and origin == list and isinstance(v, str): + v = v.split(",") + return v + class XedaBaseModelAllowExtra(XedaBaseModel, metaclass=ABCMeta): class Config(XedaBaseModel.Config): diff --git a/src/xeda/design.py b/src/xeda/design.py index dbf85bf..a72975c 100644 --- a/src/xeda/design.py +++ b/src/xeda/design.py @@ -285,6 +285,8 @@ def the_root_validator(cls, values: Dict[str, Any]) -> Dict[str, Any]: if not value: value = values.get("generics") if value: + if not isinstance(value, dict): + raise ValueError("parameters/generics must be a dictionary") for k, v in value.items(): if isinstance(v, dict) and ("file" in v or "path" in v): value[k] = str(FileResource(v)) diff --git a/src/xeda/flows/verilator/__init__.py b/src/xeda/flows/verilator/__init__.py index 7ccb20c..fba615d 100644 --- a/src/xeda/flows/verilator/__init__.py +++ b/src/xeda/flows/verilator/__init__.py @@ -2,6 +2,7 @@ import logging import os from pathlib import Path +from random import randint import shutil import sys from typing import Any, Dict, List, Optional, Union @@ -27,9 +28,11 @@ class Settings(SimFlow.Settings): ] warnings_fatal: bool = False include_dirs: List[str] = [] - optimize: bool = True + optimize: bool | str = True timing: bool = False - plus_args: List[str] = [] + model_args: List[str] = Field( + default=[], description="Arguments to pass to the model executable" + ) verilog_libs: List[str] = [] build: bool = True vpi: bool = False @@ -83,10 +86,11 @@ def run(self): if ss.build: args.append("--build") - args += [ - "--build-jobs", - 0, # auto - ] + + args += [ + "-j", # Parallelism for --build-jobs/--verilate-jobs + 0, # 0: auto + ] for wf in ss.warn_flags: args.append(wf) @@ -125,6 +129,11 @@ def run(self): args.append("--timing") else: args.append("--no-timing") + + # supres unhelpful warnings + args += [ + "-Wno-DECLFILENAME", + ] if not ss.timing: args += [ "-Wno-STMTDLY", @@ -137,7 +146,10 @@ def run(self): args += ["--trace-threads", ss.trace_threads] if ss.optimize: - args += ["-O3"] + if isinstance(ss.optimize, str): + args += ["-O" + ss.optimize] + else: + args += ["-O3"] args += [ "--x-initial", @@ -209,14 +221,14 @@ def run(self): sources.append(cocotb_cpp) verilator.run(*args, *sources) - plus_args = list(ss.plus_args) + model_args = ss.model_args if ss.random_init: random_seed = ( - 12345 if ss.debug else 0 + 1 if ss.debug else randint(1, 2147483648) ) # 0 = choose value from system random number generator - plus_args += [f"+verilator+seed+{random_seed}", "+verilator+rand+reset+2"] + model_args += [f"+verilator+seed+{random_seed}", "+verilator+rand+reset+2"] model = verilator.derive(verilated_bin) - model.run(*ss.plus_args, env=env) + model.run(*ss.model_args, env=env) def rm_dep_files(self): assert isinstance(self.settings, self.Settings) diff --git a/src/xeda/flows/yosys/templates/yosys_fpga_synth.tcl b/src/xeda/flows/yosys/templates/yosys_fpga_synth.tcl index 82d438d..8b6ded9 100644 --- a/src/xeda/flows/yosys/templates/yosys_fpga_synth.tcl +++ b/src/xeda/flows/yosys/templates/yosys_fpga_synth.tcl @@ -3,7 +3,7 @@ yosys logger -notime -stderr {% include 'read_files.tcl' %} {% if settings.prep is not none %} -yosys prep {% if settings.flatten %} -flatten {% endif %} {%if design.rtl.top %} -top {{design.rtl.top}} {% else %} -auto-top {% endif %} {{settings.prep|join(" ")}} +yosys prep {%- if settings.flatten %} -flatten {%- endif %} {%- if design.rtl.top %} -top {{design.rtl.top}} {%- else %} -auto-top {%- endif %} {{settings.prep|join(" ")}} {% else %} yosys proc {% if settings.flatten %} @@ -19,9 +19,9 @@ yosys opt -full -purge -sat {% endif %} {% if settings.abc9 -%} -{% if settings.flow3 %} yosys scratchpad -copy abc9.script.flow3 abc9.script {% endif %} +{% if settings.flow3 -%} yosys scratchpad -copy abc9.script.flow3 abc9.script {%- endif %} {# decrease the target delay to account for interconnect delay #} -{% if settings.main_clock and settings.main_clock.period_ps %} yosys scratchpad -set abc9.D {{settings.main_clock.period_ps / 1.5}} {% endif %} +{% if settings.main_clock and settings.main_clock.period_ps -%} yosys scratchpad -set abc9.D {{settings.main_clock.period_ps / 1.5}} {%- endif %} {%- endif %} yosys log -stdout "** FPGA synthesis for device {{settings.fpga}} **"