Skip to content

Commit f045081

Browse files
authored
Merge pull request #12 from lowRISC/develop
Add new features
2 parents 3c87b2b + c2f7142 commit f045081

File tree

13 files changed

+258
-159
lines changed

13 files changed

+258
-159
lines changed

flake.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
env = pythonSet.mkVirtualEnv "python-env" workspace.deps.default;
5757
in {
5858
devShells.x86_64-linux.default = pkgs.mkShell {
59-
packages = [env];
59+
packages = [env pkgs.uv];
6060
buildInputs = [peakrdl];
6161
};
6262
formatter.x86_64-linux = nixpkgs.legacyPackages.x86_64-linux.alejandra;

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ extend-select = ["W391", "E303"]
3737
allowed-confusables = [""]
3838
ignore = [
3939
"D203", "D213", "COM812", "ISC001", "FIX", "TD", "T201", "S101", "C901", "D401",
40-
"PLR0911", "PLR0915", "INP001", "RUF012", "EXE001", "S701"
40+
"PLR0911", "PLR0915", "INP001", "RUF012", "EXE001", "S701", "FBT001", "FBT002"
4141
]
4242

4343
[tool.uv.workspace]

rdl2ot/src/rdl2ot/cli.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,23 @@ def main() -> None:
2929
default="./result",
3030
type=click.Path(writable=True),
3131
)
32-
def export_rtl(input_file: str, out_dir: str) -> None:
32+
@click.option(
33+
"--soc",
34+
is_flag=True,
35+
)
36+
def export_rtl(input_file: str, out_dir: str, soc: bool = False) -> None:
3337
"""Export opentitan rtl.
3438
3539
INPUT_FILE: The input RDL
3640
OUT_DIR: The destination dir to generate the output
41+
SOC: Indicates that the input RDL is a SoC top
3742
3843
"""
39-
print("Compiling file: {input_file}...")
44+
print(f"Compiling file: {input_file}...")
4045
rdlc = RDLCompiler()
4146
rdlc.compile_file(input_file)
4247
root = rdlc.elaborate()
4348

44-
rtl_exporter.run(root.top, Path(out_dir))
49+
rtl_exporter.run(root.top, Path(out_dir), soc)
4550

4651
print("Successfully finished!\n")

rdl2ot/src/rdl2ot/rtl_exporter.py

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,43 @@
1717
DEFAULT_INTERFACE_NAME = "regs"
1818

1919

20-
def run(root_node: node.AddrmapNode, out_dir: Path) -> None:
21-
"""Export RDL to opentitan RTL."""
20+
def _camelcase(value: str) -> str:
21+
words = value.split("_")
22+
return "".join(word.capitalize() for word in words)
23+
24+
25+
def run(root_node: node.AddrmapNode, out_dir: Path, is_soc: bool = False) -> None:
26+
"""Export RDL to opentitan RTL.
27+
28+
IS_SOC: True if the root node is a SoC with peripherals/devices.
29+
"""
2230
factory = OtInterfaceBuilder()
23-
data = factory.parse_root(root_node)
31+
data = factory.parse_soc(root_node) if is_soc else factory.parse_ip_block(root_node)
2432

2533
Path(out_dir / "rdl.json").write_text(json.dumps(data, indent=2), encoding="utf-8")
2634

35+
if not is_soc:
36+
_export(data, out_dir)
37+
return
38+
39+
for ip_block in data["devices"]:
40+
_export(ip_block, out_dir)
41+
42+
43+
def _export(ip_block: dict, out_dir: Path) -> None:
2744
file_loader = FileSystemLoader(TEMPLATES_DIR)
2845
env = Environment(loader=file_loader)
46+
env.filters["camelcase"] = _camelcase
2947

30-
ip_name = data["ip_name"]
48+
ip_name = ip_block["ip_name"].lower()
3149
reg_pkg_tpl = env.get_template("reg_pkg.sv.tpl")
32-
stream = reg_pkg_tpl.render(data)
50+
stream = reg_pkg_tpl.render(ip_block)
3351
path = out_dir / f"{ip_name}_reg_pkg.sv"
3452
path.open("w").write(stream)
3553
print(f"Generated {path}.")
3654

3755
reg_top_tpl = env.get_template("reg_top.sv.tpl")
38-
for interface in data["interfaces"]:
56+
for interface in ip_block["interfaces"]:
3957
name = "_{}".format(interface["name"].lower()) if "name" in interface else ""
4058
data_ = {"ip_name": ip_name, "interface": interface}
4159
stream = reg_top_tpl.render(data_).replace(" \n", "\n")
@@ -187,14 +205,10 @@ def get_interface(self, addrmap: node.AddrmapNode, defalt_name: None | str = Non
187205
self.any_shadowed_reg = False
188206
self.async_registers.clear()
189207

190-
if addrmap.is_array:
191-
print(f"WARNING: Unsupported array type: {type(addrmap)}, skiping...")
192-
193208
interface = {}
194209
if defalt_name:
195210
interface["name"] = addrmap.inst_name or defalt_name
196211

197-
interface["offset"] = addrmap.address_offset
198212
interface["regs"] = []
199213
interface["windows"] = []
200214
for child in addrmap.children():
@@ -233,29 +247,38 @@ def get_interface(self, addrmap: node.AddrmapNode, defalt_name: None | str = Non
233247
interface["any_integrity_bypass"] = any(
234248
win["integrity_bypass"] for win in interface["windows"]
235249
)
250+
interface["alerts"] = [
251+
f["name"]
252+
for reg in interface["regs"]
253+
for f in reg["fields"]
254+
if reg["name"] == "ALERT_TEST"
255+
]
236256
return interface
237257

238-
def parse_root(self, root: node.AddrmapNode) -> dict:
239-
"""Parse the root node and return a dictionary representing a window."""
240-
if root.is_array:
241-
print("Error: Unsupported array type on the top")
242-
raise RuntimeError
243-
if not isinstance(root, node.AddrmapNode):
244-
print("Error: Top level must be an addrmap")
245-
raise TypeError
246-
258+
def parse_ip_block(self, ip_block: node.AddrmapNode) -> dict:
259+
"""Parse the ip_block node of an IP block and return a dictionary."""
247260
obj = {}
248-
params = self.get_paramesters(root)
261+
params = self.get_paramesters(ip_block)
249262
if params:
250263
obj["parameters"] = params
251-
obj["ip_name"] = root.inst_name
252-
obj["offset"] = root.address_offset
264+
obj["ip_name"] = ip_block.inst_name
265+
266+
obj["offsets"] = []
267+
if ip_block.is_array:
268+
offset = ip_block.raw_address_offset
269+
for _idx in range(ip_block.array_dimensions[0]):
270+
obj["offsets"].append(offset)
271+
offset += ip_block.array_stride
272+
else:
273+
obj["offsets"].append(ip_block.address_offset)
253274

254275
obj["interfaces"] = []
255-
for child in root.children():
276+
obj["alerts"] = []
277+
for child in ip_block.children():
256278
if isinstance(child, node.AddrmapNode):
257279
child_obj = self.get_interface(child, DEFAULT_INTERFACE_NAME)
258280
obj["interfaces"].append(child_obj)
281+
obj["alerts"].extend(child_obj["alerts"])
259282
elif isinstance(child, node.RegNode | node.MemNode | node.RegfileNode):
260283
continue
261284
else:
@@ -265,9 +288,24 @@ def parse_root(self, root: node.AddrmapNode) -> dict:
265288
)
266289
raise TypeError
267290

268-
# If the root contain imediate registers, use a default interface name
269-
if len(root.registers()) > 0:
270-
interface = self.get_interface(root)
291+
# If the ip_block contain imediate registers, use a default interface name
292+
if len(ip_block.registers()) > 0:
293+
interface = self.get_interface(ip_block)
271294
obj["interfaces"].append(interface)
295+
obj["alerts"].extend(interface["alerts"])
296+
297+
return obj
272298

299+
def parse_soc(self, root: node.AddrmapNode) -> dict:
300+
"""Parse the SoC root node and return a dictionary."""
301+
if root.is_array:
302+
print("Error: Unsupported array type on the top")
303+
raise RuntimeError
304+
if not isinstance(root, node.AddrmapNode):
305+
print("Error: Top level must be an addrmap")
306+
raise TypeError
307+
308+
obj = {"devices": []}
309+
for child in root.children():
310+
obj["devices"].append(self.parse_ip_block(child))
273311
return obj

rdl2ot/src/templates/reg_pkg.sv.tpl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ package {{ ip_name }}_reg_pkg;
2525
parameter int NumRegs{{ interface.name|title }} = {{ interface.num_regs }};
2626
{%- endfor %}
2727

28+
29+
{%- if alerts|length > 0 %}
30+
31+
// Alert indices
32+
typedef enum int {
33+
{%- for alert in alerts %}
34+
Alert{{ alert | camelcase }}Idx = {{ loop.index0 }}{{ "," if not loop.last }}
35+
{%- endfor %}
36+
} {{ ip_name | lower }}_alert_idx_t;
37+
{%- endif %}
38+
2839
{%- for interface in interfaces %}
2940
{%- set registers = interface.regs %}
3041
{%- set interface_name = ("_" + interface.name)|lower if interface.name %}

rdl2ot/src/templates/reg_top.sv.tpl

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ module {{ ip_name|lower }}{{interface_name}}_reg_top (
3535
{%- endif %}
3636

3737
{%- if has_regs %}
38-
3938
// To HW
4039
output {{ ip_name|lower }}_reg_pkg::{{ ip_name|lower }}{{interface_name}}_reg2hw_t reg2hw, // Write
4140
input {{ ip_name|lower }}_reg_pkg::{{ ip_name|lower }}{{interface_name}}_hw2reg_t hw2reg, // Read
@@ -174,9 +173,11 @@ module {{ ip_name|lower }}{{interface_name}}_reg_top (
174173
{%- set num_dsp = (interface.num_regs > 0)|int + interface.num_windows %}
175174
{%- if (num_dsp) <= 1 %}
176175
{%- if interface.num_windows == 0 %}
176+
177177
assign tl_reg_h2d = tl_i;
178178
assign tl_o_pre = tl_reg_d2h;
179179
{%- else %}
180+
180181
assign tl_win_o = tl_i;
181182
assign tl_o_pre = tl_win_i;
182183
{%- endif %}
@@ -413,12 +414,13 @@ module {{ ip_name|lower }}{{interface_name}}_reg_top (
413414
{%- endif %}
414415

415416
{%- if has_regs %}
416-
{{- space }}
417+
{{ space }}
417418
// Register instances
418419
{%- endif %}
419420

420421
{%- set assign = namespace(expr="") %}
421422
{%- for reg in registers %}
423+
{{- space }}
422424
{%- for offset in reg.offsets %}
423425
{%- set multireg_idx = loop.index0 if reg.is_multireg %}
424426
{%- set multireg_suffix = "_{}".format(multireg_idx) if reg.offsets|length > 1 %}
@@ -442,8 +444,9 @@ module {{ ip_name|lower }}{{interface_name}}_reg_top (
442444
logic unused_{{ reg.name|lower }}_flds_we;
443445
assign unused_{{ reg.name|lower }}_flds_we = {{ "^({}_flds_we & {}'h{:x})".format(reg.name|lower, reg.fields|length, reg.fields_no_write_en ) }};
444446
{%- endif %}
445-
{%- set opt_expr = " | {}'h{:x}".format(reg.fields|length, reg.fields_no_write_en ) if reg.fields_no_write_en > 0 %}
446-
assign {{regname }}_qe = &{{ "({}_flds_we{})".format(regname, opt_expr) }};
447+
{%- set right_expr = "{}_flds_we".format(regname) %}
448+
{%- set right_expr = right_expr ~ (" | {}'h{:x}".format(reg.fields|length, reg.fields_no_write_en ) if reg.fields_no_write_en > 0) %}
449+
assign {{regname }}_qe = &{{ "({})".format(right_expr) if reg.fields_no_write_en > 0 else "{}".format(right_expr) }};
447450
{%- else %}
448451
prim_flop #(
449452
.Width(1),
@@ -482,7 +485,7 @@ module {{ ip_name|lower }}{{interface_name}}_reg_top (
482485
// F{{ '[{}{}]: {}:{}'.format(field.name, multireg_suffix, field.msb, field.lsb)|lower }}
483486
{%- endif %}
484487
prim_subreg{{ '_ext' if reg.external else ('_shadow' if reg.shadowed) }} #(
485-
.DW ({{ field.width }})
488+
.DW ({{ field.width }})
486489
{%- if not reg.external -%}
487490
,
488491
.SwAccess(prim_subreg_pkg::SwAccess{{ field.reggen_sw_access }}),
@@ -539,8 +542,8 @@ module {{ ip_name|lower }}{{interface_name}}_reg_top (
539542
{{- space }}
540543
{{ space }}
541544
{%- endfor %}
545+
{{ space }}
542546
{%- endfor %}
543-
{{ space }}
544547
{%- endfor %}
545548

546549
{%- if has_regs %}
@@ -569,7 +572,7 @@ module {{ ip_name|lower }}{{interface_name}}_reg_top (
569572
{%- for offset in reg.offsets %}
570573
{%- set index = "{num:>{width}}".format(num=ns.counter, width=num_regs_digits) %}
571574
{%- set ns.counter = ns.counter + 1 %}
572-
{%- if loop.first and outer_loop.first -%} ( {%- endif %}
575+
{{"(" if loop.first and outer_loop.first else " " -}}
573576
(addr_hit[{{ index }}] & (|({{ (ip_name ~ interface_name)|upper}}_PERMIT[{{ index }}] & ~reg_be)))
574577
{%- if loop.last and outer_loop.last %}));{% else %} |{% endif %}
575578
{%- endfor %}

rdl2ot/tests/snapshots/lc_ctrl_dmi_reg_top.sv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ module lc_ctrl_dmi_reg_top (
4646
.tl_i(tl_o_pre),
4747
.tl_o(tl_o)
4848
);
49+
4950
assign tl_win_o = tl_i;
5051
assign tl_o_pre = tl_win_i;
5152

rdl2ot/tests/snapshots/lc_ctrl_reg_pkg.sv

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ package lc_ctrl_reg_pkg;
2828
parameter int NumRegsRegs = 35;
2929
parameter int NumRegsDmi = 0;
3030

31+
// Alert indices
32+
typedef enum int {
33+
AlertFatalProgErrorIdx = 0,
34+
AlertFatalStateErrorIdx = 1,
35+
AlertFatalBusIntegErrorIdx = 2
36+
} lc_ctrl_alert_idx_t;
37+
3138
///////////////////////////////////////////////
3239
// Typedefs for registers for regs interface //
3340
///////////////////////////////////////////////

0 commit comments

Comments
 (0)