Skip to content
Open
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
9 changes: 9 additions & 0 deletions src/peakrdl_cheader/__peakrdl__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ def add_exporter_arguments(self, arg_group: 'argparse._ActionsContainer') -> Non
"""
)

arg_group.add_argument(
"-e", "--generate-enums",
action="store_true",
default=False,
help="""
Enable generation of enum definitions.
"""
)
Copy link
Member

Choose a reason for hiding this comment

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

I think we can remove the option, and have enums always be part of the output.


arg_group.add_argument(
"-x", "--explode-top",
action="store_true",
Expand Down
3 changes: 3 additions & 0 deletions src/peakrdl_cheader/design_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ def __init__(self, top_node: AddrmapNode, kwargs: Any) -> None:
self.bitfield_order_ltoh: bool
self.bitfield_order_ltoh = kwargs.pop("bitfield_order_ltoh", True)

self.generate_enums: bool
self.generate_enums = kwargs.pop("generate_enums", False)

# If a register is wider than 64-bits, it cannot be represented by a stdint
# type. Therefore it must be represented by an array of subwords
self.wide_reg_subword_size: int
Expand Down
51 changes: 48 additions & 3 deletions src/peakrdl_cheader/header_generator.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import TextIO, Set, Optional, List, Union
from typing import Type, TextIO, Set, Optional, List, Union
import os
import re

Expand Down Expand Up @@ -36,6 +36,10 @@ def run(self, path: str, top_nodes: List[Union[AddrmapNode, MemNode, RegfileNode
template.stream(context).dump(f) # type: ignore # jinja incorrectly typed
f.write("\n")

# Write enums
if self.ds.generate_enums:
self.write_enums(top_nodes)

# Generate definitions
for node in top_nodes:
self.root_node = node
Expand Down Expand Up @@ -99,14 +103,19 @@ def write_bitfields(self, grp_name: str, regwidth: int, fields: List[FieldNode])
self.write("struct __attribute__ ((__packed__)) {\n")
self.push_indent()

def get_field_type(field: FieldNode) -> str:
encode = field.get_property("encode")
return f"uint{regwidth}_t" if not self.ds.generate_enums or encode is None else \
Copy link
Member

Choose a reason for hiding this comment

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

This code will cause a runtime error. The variable regwidth is not set.

f"{self.get_enum_prefix(encode)}_e"

if self.ds.bitfield_order_ltoh:
# Bits are packed in struct LSb --> MSb
current_offset = 0
for field in fields:
if field.low > current_offset:
self.write(f"uint{regwidth}_t :{field.low - current_offset:d};\n")
current_offset = field.low
self.write(f"uint{regwidth}_t {kwf(field.inst_name)} :{field.width:d};\n")
self.write(f"{get_field_type(field)} {kwf(field.inst_name)} :{field.width:d};\n")
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure it is generically safe to use the enum type in C bitfields.
C enums are interpreted as type int which does not have a guaranteed bit-width like the stdint types.
This may work ok for compilers that use 32-bit ints, and register maps that use 32-bit regs, but beyond that the bit-packing may fail. (what if a design uses registers that are 8 or 16-bits wide, or the target GCC architecture uses 64-bit ints?)

I would recommend removing this change, and only generate the enum typedefs on the side without actually using them in the struct definition. C is weakly typed in this sense, so the utility of having the enum definition still benefits the user.

current_offset += field.width

if current_offset < regwidth:
Expand All @@ -118,7 +127,7 @@ def write_bitfields(self, grp_name: str, regwidth: int, fields: List[FieldNode])
if field.high < current_offset:
self.write(f"uint{regwidth}_t :{current_offset - field.high:d};\n")
current_offset = field.high
self.write(f"uint{regwidth}_t {kwf(field.inst_name)} :{field.width:d};\n")
self.write(f"{get_field_type(field)} {kwf(field.inst_name)} :{field.width:d};\n")
current_offset -= field.width

if current_offset > -1:
Expand Down Expand Up @@ -344,3 +353,39 @@ def write_group_struct_member(self, node: AddressableNode) -> None:

struct_name = self.get_struct_name(node)
self.write(f"{struct_name} {kwf(node.inst_name)}{array_suffix};\n")

@staticmethod
def get_enum_prefix(user_enum: Type['UserEnum']) -> str:
scope = user_enum.get_scope_path("__")
if scope:
return f"{scope}__{user_enum.type_name}"
else:
return user_enum.type_name

def write_enum(self, user_enum: Type['UserEnum']) -> None:
prefix = self.get_enum_prefix(user_enum)
lines = []
for enum_member in user_enum:
lines.append(f" {prefix}__{enum_member.name} = {enum_member.value}")

self.write("typedef enum {\n"
+ ",\n".join(lines)
+ f"\n}} {prefix}_e;\n")

def write_enums(self, top_nodes: List[AddrmapNode]) -> None:
user_enums = []

class Listener(RDLListener):
def enter_Field(listener, node: FieldNode) -> Optional[WalkerAction]:
encode = node.get_property("encode")
if encode is not None and encode not in user_enums:
user_enums.append(encode)
return None

for node in top_nodes:
self.root_node = node
RDLWalker().walk(node, Listener())
Copy link
Member

Choose a reason for hiding this comment

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

Please do not nest another listener/walker here.

I think it would be cleaner to have enum typedefs be emitted alongside the registers that use them in the existing listener.

Recommend emitting enums in an enter_Reg() callback in the HeaderGenerator listener. That will result in enums be defined right next to the register they belong to, as well as not result in the tool traversing the entire design hierarchy a second time.


for user_enum in user_enums:
self.write("\n")
self.write_enum(user_enum)