Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C Structs for Register Bit Fields #63

Merged
merged 3 commits into from
Nov 20, 2024
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ Code generated by Cheby is under your license (the one of the source file).

## Version 1.7 (dev)

Add option `--gen-c-bit-struct` to generate C `struct`s for register bit fields (github PR #63)

## Version 1.6

Add generation of field widths in constant files (github PR #50)
Expand Down
5 changes: 5 additions & 0 deletions doc/cheby-ug.txt
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,11 @@ set, the structure tag will be prefixed by the name of the root.
It avoids compiler errors if the name of the block is used in different
designs. It is strongly recommended to set this attribute.

The `--gen-c-bit-struct` option adds C `struct`s also for bit fields within
registers. Note that bit-field structures depend on implementation-defined (i.e.
compiler and ABI-dependent) behavior. The order of bit fields may not be
respected. For more information, refer to C Spec C99 6.7.2.1-11.

It is also possible to generate a C program that check the layout is the same
as the layout seen by Cheby. This can be used as a consistency check.

Expand Down
270 changes: 179 additions & 91 deletions proto/cheby/gen_c.py
Original file line number Diff line number Diff line change
@@ -1,58 +1,120 @@
"""Print as a C structure."""
import cheby.tree as tree
import cheby.print_consts as print_consts
from operator import attrgetter


class CPrinter(tree.Visitor):
def __init__(self, style):
self.buffer = ''
def __init__(self, style, gen_bit_struct=False):
self.struct_prefix = ""
self._reg_struct_buffer = ""
self._bit_struct_buffer = ""
self._reg_struct_indent = 0
self._bit_struct_indent = 0
self._padding_ids = [0]

self.submaps = []
self.pad_ids = [0]
self.indent = 0
assert style in ['neutral', 'arm']

assert style in ["neutral", "arm"]
self.style = style
self.struct_prefix = ''
self.utypes = {1: 'uint8_t',
2: 'uint16_t',
4: 'uint32_t',
8: 'uint64_t'}
self.stypes = {1: 'int8_t',
2: 'int16_t',
4: 'int32_t',
8: 'int64_t'}
self.ftypes = {4: 'float',
8: 'double'}
self.access = {'ro': ['__IM', 'volatile const'],
'rw': ['__IOM', 'volatile'],
'wo': ['__OM', 'volatile']}

def cp_raw(self, s):
self.buffer += s

def inc(self):
self.indent += 1

def dec(self):
self.indent -= 1

def start_struct(self, n):
self.cp_raw('{}struct {}{} {{\n'.format(
' ' * self.indent, self.struct_prefix, n.c_name))
self.inc()
self.pad_ids.append(0) # track padding IDs per struct

def end_struct(self, name):
self.dec()
self.pad_ids.pop()
if name is None:
self.cp_txt('};')

self.gen_bit_struct = gen_bit_struct
self.utypes = {1: "uint8_t", 2: "uint16_t", 4: "uint32_t", 8: "uint64_t"}
self.stypes = {1: "int8_t", 2: "int16_t", 4: "int32_t", 8: "int64_t"}
self.ftypes = {4: "float", 8: "double"}
self.access = {
"ro": ["__IM", "volatile const"],
"rw": ["__IOM", "volatile"],
"wo": ["__OM", "volatile"],
}

@staticmethod
def has_bit_fields(n):
return len(n.children) > 1 or n.children[0].name is not None

def get_type(self, n):
if self.gen_bit_struct and self.has_bit_fields(n):
return self.get_bit_union_name(n)
elif n.c_type == "signed":
return self.stypes[n.c_size]
elif n.c_type == "float":
return self.ftypes[n.c_size]
else:
self.cp_txt('}} {};'.format(name))
return self.utypes[n.c_size]

def reg_struct_start(self, n):
self.reg_struct_txt("struct {}{} {{".format(self.struct_prefix, n.c_name))

def cp_txt(self, txt):
self._reg_struct_indent += 1
# track padding IDs per struct
self._padding_ids.append(0)

def reg_struct_txt(self, txt=""):
if txt:
self.cp_raw('{}{}'.format(' ' * self.indent, txt))
self.cp_raw('\n')
self._reg_struct_buffer += "{}{}".format(
" " * self._reg_struct_indent, txt
)
self._reg_struct_buffer += "\n"

def reg_struct_end(self, name=""):
self._reg_struct_indent -= 1
self._padding_ids.pop()

if name:
name = " {}".format(name)
self.reg_struct_txt("}}{};".format(name))

@property
def reg_struct(self):
return self._reg_struct_buffer

@property
def padding_idx(self):
# Get current padding index for returning
padding_idx = self._padding_ids[-1]

# Increment index counter
self._padding_ids[-1] += 1

return padding_idx

def bit_struct_start(self, n):
# Start struct definition
self.bit_struct_txt("typedef struct {")
self._bit_struct_indent += 1

def bit_struct_field(self, n, name, width):
self.bit_struct_txt("{} {}: {};".format(self.utypes[n.c_size], name, width))

def bit_struct_txt(self, txt=""):
if txt:
self._bit_struct_buffer += "{}{}".format(
" " * self._bit_struct_indent, txt
)
self._bit_struct_buffer += "\n"

def bit_struct_end(self, n):
# End struct definition
self._bit_struct_indent -= 1
self.bit_struct_txt("}} {};".format(self.get_bit_struct_name(n)))

# Start and end union definition
self.bit_struct_txt("\ntypedef union {")
self._bit_struct_indent += 1
self.bit_struct_txt("{} v;".format(self.utypes[n.c_size]))
self.bit_struct_txt("{} s;".format(self.get_bit_struct_name(n)))
self._bit_struct_indent -= 1
self.bit_struct_txt("}} {};\n".format(self.get_bit_union_name(n)))

def get_bit_struct_name(self, n, suffix="_s"):
return "{}{}{}".format(self.struct_prefix, n.c_name, suffix)

def get_bit_union_name(self, n):
return self.get_bit_struct_name(n, "_u")

@property
def bit_struct(self):
return self._bit_struct_buffer


def maybe_pad(cp, diff, addr, pad_target):
Expand All @@ -64,11 +126,12 @@ def maybe_pad(cp, diff, addr, pad_target):
sz = 4
else:
sz = 1
cp.cp_txt('')
cp.cp_txt('/* padding to: {} Bytes */'.format(pad_target))
cp.cp_txt('{} __padding_{}[{}];'.format(
cp.utypes[sz], cp.pad_ids[-1], diff // sz))
cp.pad_ids[-1] += 1
cp.reg_struct_txt()
cp.reg_struct_txt("/* padding to: {} Bytes */".format(pad_target))
cp.reg_struct_txt(
"{} __padding_{}[{}];".format(cp.utypes[sz], cp.padding_idx, diff // sz)
)


def cprint_children(cp, n, size, off):
"Generate declarations for children of :param n:, and pad to :param size:"
Expand All @@ -80,7 +143,7 @@ def cprint_children(cp, n, size, off):
assert diff >= 0
maybe_pad(cp, diff, addr, el.c_address)
if i != 0:
cp.cp_txt('')
cp.reg_struct_txt()
cp.visit(el)
if isinstance(el, tree.Submap) and el.filename is not None:
# Boxed instance. There might be a difference of size
Expand Down Expand Up @@ -112,60 +175,81 @@ def comment(n):

@CPrinter.register(tree.Reg)
def cprint_reg(cp, n):
cp.cp_txt(
# Create union structures for register with bit fields
if cp.has_bit_fields(n):
cp.bit_struct_txt(
"/* [0x{:x}]: REG {} */".format(n.c_address, n.comment or n.name)
)
cp.bit_struct_start(n)

bit_idx = 0
for n_child in sorted(n.children, key=attrgetter("lo")):
if bit_idx < n_child.lo:
# Insert padding
pad_width = n_child.lo - bit_idx
cp.bit_struct_field(n, "", pad_width)
bit_idx += pad_width

cp.bit_struct_field(n, n_child.name, n_child.c_rwidth)
bit_idx += n_child.c_rwidth

if bit_idx < 8 * n.c_size:
# Insert padding at the end
cp.bit_struct_field(n, "", 8 * n.c_size - bit_idx)

cp.bit_struct_end(n)

# Structure field for register
cp.reg_struct_txt(
"/* [0x{:x}]: REG ({}){} */".format(n.c_address, n.access, comment(n))
)
if n.c_type == 'signed':
typ = cp.stypes[n.c_size]
elif n.c_type == 'float':
typ = cp.ftypes[n.c_size]
else:
typ = cp.utypes[n.c_size]
if cp.style == 'arm':

typ = cp.get_type(n)
if cp.style == "arm":
acc = cp.access[n.access][0]
cp.cp_txt('{} {} {};'.format(acc, typ, n.name))
cp.reg_struct_txt("{} {} {};".format(acc, typ, n.name))
else:
cp.cp_txt('{} {};'.format(typ, n.name))
cp.reg_struct_txt("{} {};".format(typ, n.name))


@CPrinter.register(tree.Block)
def cprint_block(cp, n):
cp.cp_txt("/* [0x{:x}]: BLOCK{} */".format(n.c_address, comment(n)))
cp.reg_struct_txt("/* [0x{:x}]: BLOCK{} */".format(n.c_address, comment(n)))
if n.hdl_blk_prefix:
cp.start_struct(n)
cp.reg_struct_start(n)
cprint_children(cp, n, n.c_size, n.c_address)
if n.hdl_blk_prefix:
cp.end_struct(n.name)
cp.reg_struct_end(n.name)


@CPrinter.register(tree.Memory)
def cprint_memory(cp, n):
cp.cp_txt("/* [0x{:x}]: MEMORY{} */".format(n.c_address, comment(n)))
cp.start_struct(n)
cp.reg_struct_txt("/* [0x{:x}]: MEMORY{} */".format(n.c_address, comment(n)))
cp.reg_struct_start(n)
cprint_children(cp, n, n.c_elsize, 0)
cp.end_struct('{}[{}]'.format(n.name, n.memsize_val // n.c_elsize))
cp.reg_struct_end("{}[{}]".format(n.name, n.memsize_val // n.c_elsize))


@CPrinter.register(tree.Repeat)
def cprint_repeat(cp, n):
cp.cp_txt("/* [0x{:x}]: REPEAT{} */".format(n.c_address, comment(n)))
cp.start_struct(n)
cp.reg_struct_txt("/* [0x{:x}]: REPEAT{} */".format(n.c_address, comment(n)))
cp.reg_struct_start(n)
cprint_children(cp, n, n.c_elsize, 0)
cp.end_struct('{}[{}]'.format(n.name, n.count))
cp.reg_struct_end("{}[{}]".format(n.name, n.count))


@CPrinter.register(tree.Submap)
def cprint_submap(cp, n):
cp.cp_txt("/* [0x{:x}]: SUBMAP{} */".format(n.c_address, comment(n)))
cp.reg_struct_txt("/* [0x{:x}]: SUBMAP{} */".format(n.c_address, comment(n)))
if n.filename is None:
# Should depend on bus size ?
if n.c_size % 4 == 0:
sz = 4
else:
sz = 1
cp.cp_txt('{} {}[{}];'.format(cp.utypes[sz], n.name, n.c_size // sz))
cp.reg_struct_txt("{} {}[{}];".format(cp.utypes[sz], n.name, n.c_size // sz))
else:
cp.cp_txt('struct {} {};'.format(n.c_submap.name, n.name))
cp.reg_struct_txt("struct {} {};".format(n.c_submap.name, n.name))
cp.submaps.append(n.c_submap)


Expand All @@ -177,29 +261,29 @@ def cprint_composite(cp, n):
@CPrinter.register(tree.Root)
def cprint_root(cp, n):
if n.version:
cp.cp_txt("/* For {} version: {} */".format(n.name, n.version))
cp.reg_struct_txt("/* For {} version: {} */".format(n.name, n.version))
if n.c_address_spaces_map is None:
cp.start_struct(n)
cp.reg_struct_start(n)
if n.c_prefix_c_struct:
cp.struct_prefix = n.name + '_'
cp.struct_prefix = n.name + "_"
cprint_composite(cp, n)
cp.end_struct(None)
cp.reg_struct_end()
else:
for i, el in enumerate(n.children):
if i != 0:
cp.cp_txt('')
cp.struct_prefix = n.name + '_'
cp.start_struct(el)
cp.reg_struct_txt()
cp.struct_prefix = n.name + "_"
cp.reg_struct_start(el)
cp.visit(el)
cp.end_struct(None)
cp.reg_struct_end()


def to_cmacro(name):
return "__CHEBY__{}__H__".format(name.upper())


def gen_c_cheby(fd, root, style):
cp = CPrinter(style)
def gen_c_cheby(fd, root, style, gen_bit_struct=False):
cp = CPrinter(style, gen_bit_struct)

# Print in a buffer, needed to gather submaps.
cprint_root(cp, root)
Expand All @@ -212,7 +296,7 @@ def gen_c_cheby(fd, root, style):
# Add the includes for submaps
submaps = [n.name for n in cp.submaps]
if submaps:
fd.write('\n')
fd.write("\n")
# Ideally we want an ordered set.
done = set()
for s in submaps:
Expand All @@ -221,9 +305,9 @@ def gen_c_cheby(fd, root, style):
# Note: we assume the filename is the name of the memmap + h
fd.write('#include "{}.h"\n'.format(s))

if cp.style == 'arm':
if cp.style == "arm":
# Add definition of access macros
fd.write('\n')
fd.write("\n")
for m in sorted(cp.access):
acc = cp.access[m]
fd.write("#ifndef {}\n".format(acc[0]))
Expand All @@ -232,10 +316,14 @@ def gen_c_cheby(fd, root, style):

# Consts
print_consts.pconsts_for_gen_c(fd, root)
fd.write('\n')

fd.write('#ifndef __ASSEMBLER__\n')
fd.write(cp.buffer)
fd.write('#endif /* !__ASSEMBLER__*/\n')
fd.write('\n')
fd.write("\n")

fd.write("#ifndef __ASSEMBLER__\n")
if gen_bit_struct and cp.bit_struct:
fd.write("/* Bit Field Structures */\n")
fd.write(cp.bit_struct)
fd.write("/* Register Map Structure */\n")
fd.write(cp.reg_struct)
fd.write("#endif /* !__ASSEMBLER__*/\n")
fd.write("\n")
fd.write("#endif /* {} */\n".format(csym))
Loading