Skip to content

Auto-generate device register C headers from SystemRDL description#290

Merged
ziuziakowska merged 4 commits intolowRISC:mainfrom
ziuziakowska:rdl-autogen-dif
Mar 9, 2026
Merged

Auto-generate device register C headers from SystemRDL description#290
ziuziakowska merged 4 commits intolowRISC:mainfrom
ziuziakowska:rdl-autogen-dif

Conversation

@ziuziakowska
Copy link
Copy Markdown
Contributor

@ziuziakowska ziuziakowska commented Feb 11, 2026

This PR extends util/rdlgenerator.py to generate device and register definition header files. The generated files live in lib/hal/autogen for the corresponding library code in lib/hal to make use of.

The generated files consist of a struct declaration of the device's layout in memory, which is to be interacted with through a volatile pointer typedef in library code. The fields of this struct correspond to the registers of the device, separated by reserved padding fields to match the memory layout.

The layouts of individual registers are also generated and are used as the types of the registers in the device struct declaration, with some processing to reduce the amount of code generated and simplify usage by the HAL:

  • If a register consists of a single 32-bit field, or 32 1-bit "flag" fields, the register is coalesced into a uint32_t type. This avoids the need to emit a struct with one uint32_t field of usually the same name as the register (e.g timer.TIMER_V_{LOWER,UPPER}0, timer.COMPARE_{LOWER_UPPER}0_0), or a large structure with many distinct bits where software might want to expose toggling arbitrarily bits for ease of use (spi_device.CMD_FILTER).
  • If a register consists of only 1-bit "flag" fields starting from bit 0, the register is emitted as an enum with each flag bit as a variant. This is to enable a usage pattern where arbitrary combinations of the flags can be queried or atomically set/unset (e.g uart.STATUS, spi_device.INTERCEPT_EN, various interrupt enable/state/test registers). This is not done for registers with a single flag field as there is no benefit to be had over emitting a struct.
  • In the general case, the register declaration is emitted as a struct with bitfields corresponding to the register fields, with unnamed padding bitfields between them if necessary.
  • enum registers that have equivalent register fields are combined into a single enum declaration that is used for all those registers. This de-duplicates the enum types for each device's INTR_{STATE,ENABLE,TEST} registers. These are handled by an initial first pass.

Attributes and assertions are used to ensure that all register types are 4 bytes in size and naturally aligned, and that all registers are at their expected offsets within the device's memory layout.

This PR only consists of the auto-generator changes. Rework of the HAL to use these is in #299 which builds upon the changes in this branch.

Closes: #29.

@ziuziakowska ziuziakowska force-pushed the rdl-autogen-dif branch 7 times, most recently from 6862ff2 to 51a054e Compare February 16, 2026 16:00
@ziuziakowska ziuziakowska changed the title [WIP] Auto-generate register access/DIF C headers from SystemRDL description [WIP] Auto-generate device/register C headers from SystemRDL description Feb 17, 2026
@ziuziakowska ziuziakowska changed the title [WIP] Auto-generate device/register C headers from SystemRDL description [WIP] Auto-generate device register C headers from SystemRDL description Feb 17, 2026
@ziuziakowska ziuziakowska changed the title [WIP] Auto-generate device register C headers from SystemRDL description Auto-generate device register C headers from SystemRDL description Feb 17, 2026
@ziuziakowska ziuziakowska marked this pull request as ready for review February 17, 2026 18:45
@ziuziakowska ziuziakowska marked this pull request as draft February 17, 2026 19:01
@ziuziakowska ziuziakowska marked this pull request as draft February 17, 2026 19:01
@ziuziakowska ziuziakowska force-pushed the rdl-autogen-dif branch 3 times, most recently from ec17380 to 80ea546 Compare February 20, 2026 13:48
@ziuziakowska ziuziakowska marked this pull request as ready for review February 20, 2026 13:49
Copy link
Copy Markdown

@AlexJones0 AlexJones0 left a comment

Choose a reason for hiding this comment

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

Thanks for all the work on fine-tuning this. I've left some comments and there's a couple of parts where I would appreciate just a little more clarity to help my understanding.

I do think that if we need to extend this in the future, it may be more maintainable to use a templating engine (to deal with the presentation) and just pass in dataclasses corresponding to the different C constructs and declarations, but I'm reasonably convinced that it would be similarly complicated at this point and so sticking with dynamic string construction is fine.

pass
input_file = Path(args.input_file)
with input_file.open("r") as f:
rdl = json.load(f)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

It's fine to leave this as is for this PR, but as a general comment it would be nice in the future if code like this would use e.g. a Pydantic dataclass. That way we can define the structure we expect out of the RDL JSON file (and validate it at load-time), and we can then encapsulate the various computed fields as properties on this model. check_register_rdl(...) could then just be defined as a validator on the Pydantic model, and registers_are_structurally_equal(...) as __eq__ or similar.

I don't have too much context on the RDL flows, but maybe these models are something they should provide for you and expose as part of their library if the format is known and consistent?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm not familiar at all with SystemRDL tooling as I am just consuming the generated JSON blob of the RDL data that we've got, but these Pydantic dataclasses seem very useful as typing (for example) lists of registers as just list[dict] is not as type-safe as I would like it. I think something like this can be addressed in a future PR, as I'm more concerned about the format of the generated output.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I had a comment a few days ago but I forgot to hit the Submit review button.

We should use templates for this codegen, it will significantly reduce python code, it's easier to read and maintain.

@ziuziakowska ziuziakowska force-pushed the rdl-autogen-dif branch 2 times, most recently from 9b32d46 to c1c2737 Compare February 26, 2026 15:21

def get_and_emit_register_types(
device_name: str, registers: list[dict]
) -> tuple[list[tuple[dict, str]], str]:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Long-term, for maintainability, we should probably be capturing this compound information inside an object/dataclass.

Copy link
Copy Markdown

@AlexJones0 AlexJones0 left a comment

Choose a reason for hiding this comment

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

Thanks for the work on this, @ziuziakowska.

As mentioned in other comments it would be nice to move to Pydantic models and templating in the future, but I think that's a goal for refactoring and this works well for now.

It would be good to also have @engdoreis check this.

@ziuziakowska
Copy link
Copy Markdown
Contributor Author

Minor changes from discussion in #299: device structs suffixed with _t

Signed-off-by: Alice Ziuziakowska <a.ziuziakowska@lowrisc.org>
Signed-off-by: Alice Ziuziakowska <a.ziuziakowska@lowrisc.org>
Signed-off-by: Alice Ziuziakowska <a.ziuziakowska@lowrisc.org>
Signed-off-by: Alice Ziuziakowska <a.ziuziakowska@lowrisc.org>
@ziuziakowska ziuziakowska merged commit 0c9a924 into lowRISC:main Mar 9, 2026
4 checks passed
@ziuziakowska ziuziakowska deleted the rdl-autogen-dif branch March 9, 2026 17:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[SW] Autogenerate the HAL (DIF)

3 participants