Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,5 @@ jobs:
run: uv run pytest
working-directory: rdlexporter

- name: Build rdlexporter package
run: uv run python -m build
working-directory: rdlexporter
- name: Build packages
run: uv build --all
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ nix develop
## rdl2ot cli tool
A PeakRDL extension to generate Opentitan style source files from SystemRDL files.

For more details, refer to [rdl2ot](./rdl2ot)

### How to run tests
```sh
cd rdl2ot
Expand All @@ -33,6 +35,8 @@ python src/rdl2ot export-rtl tests/snapshots/lc_ctrl.rdl /tmp/
## Rdl-exporter
A library to generate SystemRDL files from the Hierarchical Register Model.

For more details, refer to [rdlexporter](./rdlexporter)

### How to run tests
```sh
cd rdl-exporter
Expand All @@ -46,8 +50,7 @@ uv sync --all-extras
```
Build package
```sh
cd rdl-exporter
uv run python -m build
uv build --all
```
Install the package locally
```sh
Expand Down
35 changes: 21 additions & 14 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,29 +1,22 @@
[project]
name = "benevisrdl"
version = "0.0.0"
version = "0.1.0"
description = "Houses PeakRDL pluggins."
requires-python = ">=3.10"
readme = "README.md" # Path to your README file
license = { file = "LICENSE" } # Path to your LICENSE file
readme = "README.md"
license = { file = "LICENSE" }
dependencies = [
"uv==0.6.1",
"isort==5.10.1",
"yapf==0.32.0",
"click>=8.2.1",
"jinja2>=3.1.6",
"peakrdl>=1.4.0",
]

[project.optional-dependencies]
linting = [
"pyright>=1.1.403",
"ruff>=0.9.6",
"mypy==0.971",
"flake8 ~= 7.1",
]
dev = [
"pytest>=8.4.1",
"hatch>=1.4.1",
"twine>=6.1.0",
"build>=1.2.2",
"uv-build>=0.8.4",
]
ci = ["benevisrdl[linting,dev]"]

Expand All @@ -33,11 +26,25 @@ py-modules = []
[tool.ruff]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noticed you have the ruff config here. If the LSPs pick this up okay then great! Might be worth checking they don't stop at the package level pyproject.toml when discovering the ruff config.

A quick test could be to set the line length to 10 or something and then run ruff inside one of the package directories?

Actually I guess the IDE may pick up the pyproject.toml from the root of the repo first? Not sure, but it might be worth opening neovim from the context of a subdir within a package and then again at the repo root and see if there is a difference in terms of rule discovery.

target-version = "py310"
line-length = 100
extend-exclude = [
]

[tool.ruff.lint]
preview = true
explicit-preview-rules = true
extend-select = ["E", "E303", "W391"]
select = ["ALL"]
extend-select = ["W391", "E303"]
allowed-confusables = ["−"]
ignore = [
"D203", "D213", "COM812", "ISC001", "FIX", "TD", "T201", "S101", "C901", "D401",
"PLR0911", "PLR0915", "INP001", "RUF012", "EXE001", "S701"
]

[tool.uv.workspace]
members = [ "rdl2ot", "rdlexporter"]

[[tool.uv.index]]
name = "testpypi"
url = "https://test.pypi.org/simple/"
publish-url = "https://test.pypi.org/legacy/"
explicit = true
21 changes: 21 additions & 0 deletions rdl2ot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# rdl2ot cli tool
A PeakRDL extension to generate Opentitan style source files from SystemRDL files.

## How to generate the Opentitan register interfaces from a RDL file
```sh
rdl2ot export-rtl <input_rdl> <output_dir>
```

Example:
```sh
mkdir -p /tmp/lc_ctrl
rdl2ot export-rtl tests/snapshots/lc_ctrl.rdl /tmp/lc_ctrl/
```

## Contributing
### How to run tests
```sh
cd rdl2ot
pytest
```

29 changes: 29 additions & 0 deletions rdl2ot/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,38 @@ name = "rdl2ot"
version = "0.1.0"
description = "An extension of PeakRDL to generate Opentitan RTL."
requires-python = ">=3.10"
keywords = ["SystemRDL", "Opentitan", "Codegen"]
readme = "README.md"
dependencies = [
"click>=8.2.1",
"jinja2>=3.1.6",
"peakrdl>=1.4.0",
]

authors = [
{ name = "lowRISC contributors"},
]

[project.scripts]
rdl2ot = "rdl2ot.cli:main"

[project.urls]
Homepage = "https://github.com/lowrisc/benevisrdl"
Issues = "https://github.com/lowrisc/benevisrdl/issues"
Documentation = "https://github.com/lowrisc/benevisrdl"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.pyright]
include = ["src"]
reportMissingImports = "error"
reportMissingTypeStubs = false
venv = ".venv"
executionEnvironments = [
{ root = "src" },
]

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no ruff tool config here. Which is fine if the defaults are okay, but it might be worth explicitly specifying in case the defaults change?

[tool.hatch.build.targets.wheel]
packages = ["src/rdl2ot", "src/templates"]
2 changes: 2 additions & 0 deletions rdl2ot/src/rdl2ot/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Copyright lowRISC contributors (OpenTitan project).
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0

"""Init."""
2 changes: 2 additions & 0 deletions rdl2ot/src/rdl2ot/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0

"""Main."""

from cli import main

if __name__ == "__main__":
Expand Down
40 changes: 21 additions & 19 deletions rdl2ot/src/rdl2ot/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,44 @@
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0

import click

"""Cli."""

from pathlib import Path

import click
from systemrdl import RDLCompiler

from rdl2ot import rtl_exporter


@click.group()
def main():
pass
def main() -> None:
"""Cli."""


@main.command()
@click.argument(
"input_file",
type=click.Path(writable=True),
# help="The input RDL.",
)
@click.argument(
"out_dir",
default="./result",
type=click.Path(writable=True),
# help="The destination dir to generate the output.",
)
def export_rtl(input_file: str, out_dir: str):
from systemrdl import RDLCompiler
def export_rtl(input_file: str, out_dir: str) -> None:
"""Export opentitan rtl.

INPUT_FILE: The input RDL
OUT_DIR: The destination dir to generate the output

"""
print("Compiling file: {input_file}...")
rdlc = RDLCompiler()
try:
rdlc.compile_file(input_file)
root = rdlc.elaborate()
except Exception as e:
raise RuntimeError(f"In file {input_file}") from e

import export_rtl

try:
export_rtl.run(rdlc, root, Path(out_dir))
except Exception as e:
raise RuntimeError(f"In file {input_file}") from e
rdlc.compile_file(input_file)
root = rdlc.elaborate()

rtl_exporter.run(rdlc, root, Path(out_dir))

print("Successfully finished!\n")
65 changes: 34 additions & 31 deletions rdl2ot/src/rdl2ot/opentitan.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,28 @@
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0

from systemrdl.rdltypes import OnReadType, OnWriteType, AccessType
from systemrdl import node
"""Functions with opentitan specific logic."""

import re

from systemrdl import node
from systemrdl.rdltypes import AccessType, OnReadType, OnWriteType


def register_permit_mask(reg: dict) -> int:
"""
One bit presents one byte in the register, so in total 4 bits are used.
"""
"""One bit presents one byte in the register, so in total 4 bits are used."""
w = reg["msb"] + 1
if w > 24:
if w > 24: # noqa: PLR2004
return 0b1111
if w > 16:
if w > 16: # noqa: PLR2004
return 0b0111
if w > 8:
if w > 8: # noqa: PLR2004
return 0b0011
return 0b0001


def needs_read_en(reg: dict()) -> bool:
"""Return true if at least one field needs a read-enable
def needs_read_en(reg: dict) -> bool:
"""Return true if at least one field needs a read-enable.

This is true if any of the following are true:

Expand All @@ -40,43 +41,42 @@ def needs_read_en(reg: dict()) -> bool:
side might need the re signal)
"""
return reg["shadowed"] or any(
[
(field["clear_onread"] or (reg["external"] and field["sw_readable"]))
for field in reg["fields"]
]
(field["clear_onread"] or (reg["external"] and field["sw_readable"]))
for field in reg["fields"]
)


def needs_write_en(reg: dict()) -> bool:
"""Should the register for this field have a write-enable signal?
def needs_write_en(reg: dict) -> bool:
"""Return register for this field should have a write-enable signal.

This is almost the same as allows_write(), but doesn't return true for
RC registers, which should use a read-enable signal (connected to their
prim_subreg's we port).
"""
return any([(not field["clear_onread"] and field["sw_writable"]) for field in reg["fields"]])
return any((not field["clear_onread"] and field["sw_writable"]) for field in reg["fields"])


def needs_qe(reg: dict) -> bool:
"""Return true if the register or at least one field needs a q-enable."""
return any(field["swmod"] for field in reg["fields"])

def needs_qe(reg: dict()) -> bool:
"""Return true if the register or at least one field needs a q-enable"""
return any([field["swmod"] for field in reg["fields"]])

def needs_int_qe(reg: dict) -> bool:
"""Return true if the register or at least one field needs an internal q-enable.

def needs_int_qe(reg: dict()) -> bool:
"""Return true if the register or at least one field needs an
internal q-enable. An internal q-enable means the net
may be consumed by other reg logic but will not be exposed
in the package file."""
An internal q-enable means the net may be consumed by other reg logic but will
not be exposed in the package file.
"""
return (bool(reg["async_clk"]) and reg["hw_writable"]) or needs_qe(reg)


def get_bit_width(offset: int) -> int:
"""Calculate the number of bits to address every byte of the block"""
"""Calculate the number of bits to address every byte of the block."""
return (offset - 1).bit_length()


def get_sw_access_enum(field: node.FieldNode) -> str:
"""Map the rdl access permissions to reggen SwAccess enum"""
"""Map the rdl access permissions to reggen SwAccess enum."""
sw = field.get_property("sw")
onwrite = field.get_property("onwrite")
onread = field.get_property("onread")
Expand All @@ -98,15 +98,17 @@ def get_sw_access_enum(field: node.FieldNode) -> str:
return "NONE"


def fields_no_write_en(reg: dict()) -> int:
def fields_no_write_en(reg: dict) -> int:
"""Count how many fields has write enable."""
res = 0
for idx, field in enumerate(reg["fields"]):
res |= (not needs_we(field)) << idx
return res


def needs_we(field: dict) -> bool:
"""Should the register for this field have a write-enable signal?
"""True if the register for this field should have a write-enable signal.

This is almost the same as allows_write(), but doesn't return true for
RC registers, which should use a read-enable signal (connected to their
prim_subreg's we port).
Expand All @@ -115,8 +117,9 @@ def needs_we(field: dict) -> bool:


def is_homogeneous(reg: dict) -> bool:
"""Return true if all fields of a register are equal. The offset are excluded from
the comparison.
"""Return true if all fields of a register are equal.

The offset are excluded from the comparison.
"""
exclude = ["name", "msb", "lsb", "bitmask", "type"]
unamed_fields = [
Expand Down
Loading