diff --git a/util/dtgen/dt_api.c.tpl b/util/dtgen/dt_api.c.tpl index c93c831e22c7a..3e810989fafa9 100644 --- a/util/dtgen/dt_api.c.tpl +++ b/util/dtgen/dt_api.c.tpl @@ -5,86 +5,31 @@ // Device table API auto-generated by `dtgen` <% from topgen.lib import Name, is_top_reggen, is_ipgen - -module_types = {m["type"] for m in top["module"]} -module_types = sorted(module_types) -top_name = Name(["top", top["name"]]) -irq_base_name = top_name + Name(["plic", "irq", "id"]) -top_clock_prefix = Name(["dt", "clock"]) - -def snake_to_constant_name(s): - return Name.from_snake_case(s).as_c_enum() -%>\ - +%> #include "dt/dt_api.h" -#include "hw/top_${top["name"]}/sw/autogen/top_${top["name"]}.h" +#include "hw/top_${helper.top["name"]}/sw/autogen/top_${helper.top["name"]}.h" #include <% - dev_prefix = Name(["dt", "instance", "id"]) - irq_prefix = Name(["top", top["name"], "plic", "irq", "id"]) - none_irq_name = irq_prefix + Name(["None"]) - unknown_peripheral_name = dev_prefix + Name(["unknown"]) - irq_table = {none_irq_name: unknown_peripheral_name} - if False: - for module_name, irqs in helper.device_irqs.items(): - dev_name = Name.from_snake_case(module_name) - module_type = [m for m in top["module"] if m["name"] == module_name] - assert len(module_type) == 1 - for irq in irqs: - irq_name = irq_prefix + Name.from_snake_case(irq) - irq_table[irq_name] = dev_prefix + dev_name - print("{} -> {}".format(irq_name, dev_prefix + dev_name)) - - for intr in top["interrupt"]: - width = int(intr["width"]) - for i in range(width): - name = Name.from_snake_case(intr["name"]) - if width > 1: - name += Name([str(i)]) - module_name = Name.from_snake_case(intr["module_name"]) - irq_table[irq_prefix + name] = dev_prefix + module_name -%>\ - + top_plic_irq_id_name = Name.from_snake_case("top_" + helper.top["name"] + "_plic_irq_id") + top_plic_irq_id_last = top_plic_irq_id_name + Name(["last"]) + top_plic_irq_id_count = top_plic_irq_id_name + Name(["count"]) +%> enum { - kDtIrqIdCount = ${str(len(irq_table.keys()))}, + ${top_plic_irq_id_count.as_c_enum()} = ${top_plic_irq_id_last.as_c_enum()} + 1, }; -static const dt_instance_id_t instance_from_irq[kDtIrqIdCount] = { -% for irq, device_id in irq_table.items(): - [${irq.as_c_enum()}] = ${device_id.as_c_enum()}, -% endfor -}; +static const ${helper.inst_from_irq_map.render_var_def(Name.from_snake_case("instance_from_irq"), helper.inst_from_irq_values)} dt_instance_id_t dt_plic_id_to_instance_id(dt_plic_irq_id_t irq) { - if (irq < (dt_plic_irq_id_t)kDtIrqIdCount) { + if (irq <= ${top_plic_irq_id_last.as_c_enum()}) { return instance_from_irq[irq]; } return kDtInstanceIdUnknown; } -static const dt_device_type_t device_type[kDtInstanceIdCount] = { -% for module_name in module_types: -<% - modules = [m for m in top["module"] if m["type"] == module_name] -%>\ -% for (dev_index, m) in enumerate(modules): - [${snake_to_constant_name("dt_instance_id_" + m["name"])}] = ${snake_to_constant_name("dt_device_type_" + m["type"])}, -% endfor -% endfor -}; - -static const dt_device_type_t instance_index[kDtInstanceIdCount] = { -% for module_name in module_types: -<% - modules = [m for m in top["module"] if m["type"] == module_name] -%>\ -% for (dev_index, m) in enumerate(modules): - [${snake_to_constant_name("dt_instance_id_" + m["name"])}] = ${dev_index}, -% endfor -% endfor -}; +static const ${helper.dev_type_map.render_var_def(Name.from_snake_case("device_type"), helper.dev_type_values)} dt_device_type_t dt_device_type(dt_instance_id_t dev) { if (dev < kDtInstanceIdCount) { @@ -93,76 +38,18 @@ dt_device_type_t dt_device_type(dt_instance_id_t dev) { return kDtDeviceTypeUnknown; } -size_t dt_instance_index(dt_instance_id_t dev) { - if (dev < kDtInstanceIdCount) { - return instance_index[dev]; - } - return 0; -} - -<% - # List all muxed pads directly from the top. - pads = {pad["name"]: pad for pad in top['pinout']['pads'] if pad['connection'] == 'muxed'} - - # List direct pads from the pinmux to avoid pins which are not relevant. - for pad in top['pinmux']['ios']: - if pad['connection'] == 'muxed': - continue - name = pad['name'] - if pad['width'] > 1: - name += str(pad['idx']) - pads[name] = pad -%>\ /** * Pad description. * * A `dt_pad_t` represents a chip's physical pad. */ -typedef struct dt_pad_desc { - /** Pad type */ - dt_pad_type_t type; - /** For `kDtPadTypeMio` pads: MIO out number. This is the index of the MIO_OUTSEL register - * that controls this pad (or the output part of this pad). - * - * For `kDtPadTypeDio`: DIO pad number. This is the index of the various DIO_PAD_* registers - * that control this pad. - */ - uint16_t mio_out_or_direct_pad; - /** For `kDtPadTypeMio` pads: MIO pad number. This is the value to put in the MIO_PERIPH_INSEL - * registers to connect a peripheral to this pad. - */ - uint16_t insel; -} dt_pad_desc_t; +${helper.pad_struct.render_type_def()} -// Pad descriptions. -static const dt_pad_desc_t dt_pad[kDtPadCount] = { -% for (padname, pad) in pads.items(): <% - if pad["connection"] == "muxed": - pad_type = "Mio" - pad_mio_out_or_direct_pad = "0" - pad_insel = "0" - if pad["port_type"] in ["input", "inout"]: - pad_mio_out_or_direct_pad = snake_to_constant_name("top_{}_pinmux_mio_out_{}".format(top["name"], padname)) - if pad["port_type"] in ["output", "inout"]: - pad_insel = snake_to_constant_name("top_{}_pinmux_insel_{}".format(top["name"], padname)) - elif pad["connection"] == "direct": - pad_type = "Dio" - pad_mio_out_or_direct_pad = snake_to_constant_name("top_{}_direct_pads_{}".format(top["name"], padname)) - pad_insel = "0" - else: - assert pad["connection"] == "manual", "unexpected connection type '{}'".format(pad["connection"]) - pad_mio_out_or_direct_pad = "0" - pad_insel = "0" - pad_type = "Unspecified" -%>\ - [${snake_to_constant_name("dt_pad_" + padname)}] = { - .type = kDtPadType${pad_type}, - .mio_out_or_direct_pad = ${pad_mio_out_or_direct_pad}, - .insel = ${pad_insel}, - }, -% endfor -}; + dt_pad_array_name = Name.from_snake_case("dt_pad") +%> +// Pad descriptions. +static const ${helper.pad_dt_map.render_var_def(dt_pad_array_name, helper.pad_dt_values)} <% invalid_pad_check = "pad < (dt_pad_t)0 || pad >= kDtPadCount" diff --git a/util/dtgen/dt_api.h.tpl b/util/dtgen/dt_api.h.tpl index f61110fa3d830..f69635166b962 100644 --- a/util/dtgen/dt_api.h.tpl +++ b/util/dtgen/dt_api.h.tpl @@ -44,21 +44,6 @@ ${helper.instance_id_enum.render()} */ dt_device_type_t dt_device_type(dt_instance_id_t id); -/** - * Get the instance number of a device instance. - * - * If a top has several instances of the same type, this will return the - * instance number. This function guarantees that the instance - * number can be used to index into the corresponding devicetable. - * - * For example, if the instance index of `kDtUart3` is 3 then it is guaranteed - * then that `kDtUart[3] == kDtUart3`. - * - * @param dev An instance ID. - * @return The instance number, or 0 if the ID is not valid. - */ -size_t dt_instance_index(dt_instance_id_t dev); - /** PLIC IRQ ID type. * * This type represents a raw IRQ ID from the PLIC. @@ -141,25 +126,7 @@ typedef enum dt_periph_io_dir { * * NOTE The fields of this structure are internal, use the dt_periph_io_* functions to access them. */ -typedef struct dt_periph_io { - struct { - /** Peripheral I/O type */ - dt_periph_io_type_t type; - /** Peripheral I/O direction. */ - dt_periph_io_dir_t dir; - /** For `kDtPeriphIoTypeMio`: peripheral input number. This is the index of the MIO_PERIPH_INSEL register - * that controls this peripheral I/O. - * - * For `kDtPeriphIoTypeDio`: DIO pad number. This is the index of the various DIO_PAD_* registers - * that control this peripheral I/O. - */ - uint16_t periph_input_or_direct_pad; - /** For `kDtPeriphIoTypeMio`: peripheral output number. This is the value to put in the MIO_OUTSEL registers - * to connect an output to this peripheral I/O. - */ - uint16_t outsel; - } __internal; -} dt_periph_io_t; +${helper.periph_io_struct.render_type_def()} /** Tie constantly to zero. */ static const dt_pinmux_outsel_t kDtPinmuxOutselConstantZero = k${top_name.as_camel_case()}PinmuxOutselConstantZero; diff --git a/util/dtgen/dt_ip.c.tpl b/util/dtgen/dt_ip.c.tpl index 55f205bea9048..7e0ac22ba49f3 100644 --- a/util/dtgen/dt_ip.c.tpl +++ b/util/dtgen/dt_ip.c.tpl @@ -6,173 +6,30 @@ <% from topgen.lib import Name, is_top_reggen, is_ipgen -top = helper.top -top_name = Name(["top", top["name"]]) -irq_base_name = top_name + Name(["plic", "irq", "id"]) -top_clock_prefix = Name(["dt", "clock"]) - -def snake_to_constant_name(s): - return Name.from_snake_case(s).as_c_enum() - module_name = helper.ip.name %>\ #include "dt/dt_${module_name}.h" -// Device tables for ${module_name} -<% - modules = helper.inst_map.values() - block = helper.ip - reg_count_enum = Name.from_snake_case( - "dt_{}_reg_block_count".format(module_name) - ).as_c_enum() - clk_count_enum = Name.from_snake_case( - "dt_{}_clock_count".format(module_name) - ).as_c_enum() - irq_count_enum = Name.from_snake_case( - "dt_{}_irq_count".format(module_name) - ).as_c_enum() - reg_count = len(block.reg_blocks) - irq_count = 0 - for irq in block.interrupts: - irq_count += irq.bits.width() - irqs = {} - for m in modules: - irqs_packed = [irq - for irq in top["interrupt"] if irq["module_name"] == m["name"]] - irqs[m["name"]] = [] - for irq in irqs_packed: - irq_name = irq_base_name + Name.from_snake_case(irq["name"]) - irq_width = int(irq["width"]) - if irq_width > 1: - for i in range(irq_width): - irqs[m["name"]].append(irq_name + Name([str(i)])) - else: - irqs[m["name"]].append(irq_name) - block_clock_prefix = Name.from_snake_case(f"dt_{module_name}_clock") - block_clocks = {} - for clock in block.clocking.items: - if clock.internal or clock.clock == None or clock.clock == "scan_clk_i": - continue - if clock.clock_base_name == "": - block_clock = block_clock_prefix + Name(["clk"]) - else: - block_clock = (block_clock_prefix + - Name.from_snake_case(clock.clock_base_name)) - block_clocks[clock.clock] = block_clock - clk_count = len(block_clocks.keys()) - - inouts, inputs, outputs = block.xputs - device_ports = [] - for sig in inputs + outputs + inouts: - device_ports.append(sig.name) - - pinmux_info = top["pinmux"] -%>\ - /** * Description of instances. */ -${helper.inst_struct.render()} +${helper.inst_struct.render_type_def()} <% - dt_array = (helper.ip_name + Name(["desc"])).as_snake_case() + dt_array_name = helper.ip_name + Name(["desc"]) + dt_array = dt_array_name.as_snake_case() %> -static const dt_${module_name}_desc_t ${dt_array}[${(helper.inst_enum.name + Name(["count"])).as_c_enum()}] = { -% for (dev_index, inst_name) in enumerate(helper.inst_map.keys()): - // Properties of ${m["name"]} - [${(helper.inst_enum.name + inst_name).as_c_enum()}] = { - .inst_id = ${snake_to_constant_name("dt_instance_id_" + m["name"])}, - .base_addr = { -% for (rb, addr) in m["base_addrs"].items(): -<% - # If the register block name is not specified, it will be serialized as "null" - # in the top-generated Hjson file. In this case, use the default node created - # in dt_ip.h.tpl - if rb == "null": - rb = "core" - # Only consider those address spaces that the hart can talk to. - if "hart" not in addr: - continue -%>\ - [${snake_to_constant_name(f"dt_{module_name}_reg_block_{rb}")}] = ${addr["hart"]}, -% endfor - }, - .clock = { -% for port, clock in m["clock_srcs"].items(): -% if port in block_clocks: -<% - if type(clock) is str: - clock_id = top_clock_prefix + Name.from_snake_case(clock) - else: - clock_id = top_clock_prefix + Name.from_snake_case(clock["clock"]) - block_clock_enum = block_clocks[port].as_c_enum() -%>\ - [${block_clock_enum}] = ${clock_id.as_c_enum()}, -% endif -% endfor - }, -% if len(block.interrupts) > 0: -## It can happen that a block declares some interrupts but the block is not connected to the PLIC. -## For example, on english breakfast, the rv_timer is directly connected to Ibex and not to the PLIC. -## In this case, we set the first_irq to kDtPlicIrqIdNone. -% if len(irqs[m["name"]]) == 0: - .first_irq = kDtPlicIrqIdNone, -% else: - .first_irq = ${irqs[m["name"]][0].as_c_enum()}, -% endif -% endif -% if len(device_ports) > 0: - .periph_io = { -% for port in device_ports: -% for conn in [c for c in pinmux_info["ios"] if c["name"] == m["name"] + "_" + port]: -<% - pin_name = port - if conn["type"] == "input": - pin_dir = "kDtPeriphIoDirIn" - elif conn["type"] == "output": - pin_dir = "kDtPeriphIoDirOut" - else: - assert conn["type"] == "inout", "unexpected connection dir '{}'".format(conn["type"]) - pin_dir = "kDtPeriphIoDirInout" +static const ${helper.inst_dt_map.render_var_def(dt_array_name, helper.inst_dt_values)} - if conn["idx"] != -1: - pin_name += str(conn["idx"]) - if conn["connection"] == "muxed": - pin_type = "Mio" - pin_periph_input_or_direct_pad = "0" - pin_outsel = "0" - if conn["type"] in ["input", "inout"]: - pin_periph_input_or_direct_pad = snake_to_constant_name("top_{}_pinmux_peripheral_in_{}_{}".format(top["name"], m["name"], pin_name)) - if conn["type"] in ["output", "inout"]: - pin_outsel = snake_to_constant_name("top_{}_pinmux_outsel_{}_{}".format(top["name"], m["name"], pin_name)) - elif conn["connection"] == "direct": - pin_type = "Dio" - pin_periph_input_or_direct_pad = snake_to_constant_name("top_{}_direct_pads_{}_{}".format(top["name"], m["name"], pin_name)) - pin_outsel = "0" - else: - assert conn["connection"] == "manual", "unexpected connection type '{}'".format(conn["connection"]) - pin_periph_input_or_direct_pad = "0" - pin_outsel = "0" - pin_type = "Unspecified" -%>\ - [${snake_to_constant_name(f"dt_{module_name}_periph_io_{pin_name}")}] = { - .__internal = { - .type = kDtPeriphIoType${pin_type}, - .dir = ${pin_dir}, - .periph_input_or_direct_pad = ${pin_periph_input_or_direct_pad}, - .outsel = ${pin_outsel}, - } - }, -% endfor -% endfor - }, -% endif - }, -% endfor -}; +dt_${module_name}_t dt_${module_name}_from_instance_id(dt_instance_id_t inst_id) { + if (inst_id >= ${helper.first_inst_id.as_c_enum()} && inst_id <= ${helper.last_inst_id.as_c_enum()}) { + return (dt_${module_name}_t)(inst_id - ${helper.first_inst_id.as_c_enum()}); + } + return (dt_${module_name}_t)0; +} dt_instance_id_t dt_${module_name}_instance_id( dt_${module_name}_t dt) { diff --git a/util/dtgen/dt_ip.h.tpl b/util/dtgen/dt_ip.h.tpl index ccce8b2399bf5..f8f6c9d506ccb 100644 --- a/util/dtgen/dt_ip.h.tpl +++ b/util/dtgen/dt_ip.h.tpl @@ -14,7 +14,7 @@ #ifndef ${include_guard} #define ${include_guard} -#include "dt/dt_api.h" +#include "dt_api.h" #include /** @@ -58,12 +58,25 @@ ${helper.clock_enum.render()} /** * List of peripheral I/O. * - * peripheral I/O are guaranteed to be numbered consecutively from 0. + * Peripheral I/O are guaranteed to be numbered consecutively from 0. */ ${helper.periph_io_enum.render()} % endif +/** + * Get the ${device_name} instance from an instance ID + * + * For example, `dt_uart_from_instance_id(kDtInstanceIdUart3) == kDtUart3`. + * + * @param dt Instance ID. + * @return A ${device_name} instance. + * + * NOTE This function only makes sense if the instance ID has device type ${device_name}, + * otherwise the returned value is unspecified. + */ +dt_${device_name}_t dt_${device_name}_from_instance_id(dt_instance_id_t inst_id); + /** * Get the instance ID of an instance. * diff --git a/util/dtgen/helper.py b/util/dtgen/helper.py index b03e919e89289..8799b90fc63fa 100644 --- a/util/dtgen/helper.py +++ b/util/dtgen/helper.py @@ -4,77 +4,148 @@ """This contains a class which is used to help generate the device tables (DT) files. """ -from typing import Dict +from abc import ABC, abstractmethod +from typing import Optional from collections import OrderedDict from topgen.lib import CEnum, CArrayMapping, Name from reggen.ip_block import IpBlock +import logging + def indent_text(text, indent): return "".join(indent + line for line in text.splitlines(True)) -class ScalarType: - def __init__(self, typename: object): - self.typename = typename +class BaseType(ABC): + @abstractmethod + def render_type_def(self) -> str: + """ + Render the type definition. - def render(self, name: Name): - return "{} {}".format(render_type(self.typename), name.as_snake_case()) + Example: "struct X {int field;};" + """ + @abstractmethod + def render_var_decl(self, name: Name) -> str: + """ + Render the declaration of a variable with this type and the name + in argument. -class BitFieldType: - def __init__(self, typename: object, width: int): - self.typename = typename - self.width = width + Example (name="my_var"): "int my_var" + """ - def render(self, name: Name): - return "{} {}: {}".format(render_type(self.typename), name.as_snake_case(), self.width) + @abstractmethod + def render_value(self, value: object): + """ + Render a value of this type. + """ + def render_var_def(self, name: Name, value: object) -> str: + """ + Render the definition of a variable with this type. -class ArrayType: - def __init__(self, typename: object, length: Name): - self.typename = typename - self.length = length + Example (name="my_var"): "int my_var = 42" + """ + return "{} = {};\n".format(self.render_var_decl(name), self.render_value(value)) + + +class ScalarType(BaseType): + def __init__(self, base_type: object): + """ + Wrapper around the following types: + - str: values are also str and rendered as-is + - Name/CEnum: values are Name() and the Name/enum name + is prefixed to the value which is rendered using .as_c_enum() + """ + self.base_type = base_type - def render(self, name: Name): - return "{} {}[{}]".format(render_type(self.typename), name.as_snake_case(), - self.length.as_c_enum()) + assert isinstance(self.base_type, Name) or isinstance(self.base_type, str), \ + "except either a Name or str as base type, not {}".format(type(self.base_type)) + def render_type_def(self) -> str: + return "" -class StructType: - def __init__(self, typename: object): - self.typename = typename + def _render_type(self) -> str: + if isinstance(self.base_type, Name): + return self.base_type.as_c_type() - def render(self, name: Name): - return "{} {}".format(self.typename.render(nested = True), name.as_snake_case()) + if isinstance(self.base_type, str): + return self.base_type + assert False, "I don't know how to render a {}".format(type(self.base_type)) -def render_type(obj: object) -> str: - if isinstance(obj, Name): - return obj.as_c_type() - elif isinstance(obj, str): - return obj - else: - assert False, "I don't know how to render a {}".format(type(obj)) + def render_var_decl(self, name: Name) -> str: + return "{} {}".format(self._render_type(), name.as_snake_case()) + def render_value(self, value: object) -> str: + if isinstance(self.base_type, Name): + assert isinstance(value, Name), \ + "ScalarType({}) can only render a Name(), not {}".format(self.base_type, value) + return (self.base_type + value).as_c_enum() -class Struct: + if isinstance(self.base_type, str): + return value + + assert False, "I don't know how to render a {}".format(type(self.base_type)) + + +class ArrayMapType(BaseType): + """ + An array type that maps values to other values. + """ + def __init__(self, elem_type: BaseType, index_type: BaseType, length: object): + self.elem_type = elem_type + self.index_type = index_type + self.length = length + + def render_type_def(self) -> str: + return "" + + def render_var_decl(self, name: Name) -> str: + return "{}[{}]".format(self.elem_type.render_var_decl(name), + self.index_type.render_value(self.length)) + + def render_value(self, value: dict[object, object]): + text = "" + for (entry, value) in value.items(): + text += "[{}] = {},\n".format(self.index_type.render_value(entry), + self.elem_type.render_value(value)) + return "{\n" + indent_text(text, " ") + "}" + + +class StructType(BaseType): """ This class holds the description of a struct. It allows nesting - of structures as well as arbitrary types. + of structures as well as arbitrary types. If the name is left to + None, this is considered an anonymous structure which can be + nested in another struct. """ - def __init__(self, name: Name): + def __init__(self, name: Optional[Name] = None): self.name = name - self.fields = [] + self.fields = OrderedDict() + self.internal = False + + def __str__(self) -> str: + return "StructType{{name={}, {}}}".format(self.name, self.fields.keys()) + + def add_field(self, name: Name, field_type: BaseType, docstring: str = ""): + self.fields[name] = (field_type, docstring) + + def has_field(self, name: Name) -> bool: + return name in self.fields - def add_field(self, name: Name, typename: object, docstring: str = ""): - self.fields.append((typename, name, docstring)) + def field_type(self, name: Name) -> object: + return self.fields[name][0] - def render(self, nested = False) -> str: + def as_c_type(self) -> str: + return self.name.as_c_type() + + def _render_type_def(self) -> str: text = "" - for (typename, name, docstring) in self.fields: + for (name, (field_type, docstring)) in self.fields.items(): if len(docstring.splitlines()) > 1: predocstring = "/**\n{}\n */\n".format(indent_text(docstring, " * ")) postdocstring = "" @@ -82,27 +153,77 @@ def render(self, nested = False) -> str: predocstring, postdocstring = "", " /**< {} */".format(docstring) else: predocstring, postdocstring = "", "" - text += predocstring + typename.render(name) + ";" + postdocstring + "\n" + text += predocstring + field_type.render_var_decl(name) + ";" + postdocstring + "\n" - text = "struct " + (self.name.as_snake_case() + " " if not nested else "") + \ + if self.name is None: + st_name = "" + else: + st_name = self.name.as_snake_case() + " " + text = "struct " + st_name + \ "{\n" + indent_text(text, " ") + "}" - if not nested: - text = "typedef " + text + " " + self.name.as_c_type() + ";" return text + def render_type_def(self) -> str: + text = "" + # Only render non-anonymous typedef + if self.name is not None: + text += "typedef " + self._render_type_def() + " " + self.name.as_c_type() + ";\n" + return text + + def render_var_decl(self, name: Name) -> str: + # Anonymous structures need to include the type definition. + if self.name is None: + typename = self._render_type_def() + else: + typename = self.name.as_c_type() + return "{} {}".format(typename, name.as_snake_case()) + + def render_value(self, value: dict[Name, object]) -> str: + """ + Render a value which is a dictionary mapping fields to value. + """ + text = "" + for (name, (field_type, _)) in self.fields.items(): + # TODO warn about missing fields? + if name not in value: + logging.warn("field {} not found in {}".format(name, value)) + continue + text += ".{} = {},\n".format(name.as_snake_case(), field_type.render_value(value[name])) + value.pop(name) + assert not value, \ + "Extra keys when rendering {} of type {}: {}".format(value, self, value.keys()) + + return "{\n" + indent_text(text, " ") + "}" + class TopHelper: + """ + Helper class to generate dt_api.{c,h}. + """ DT_INSTANCE_ID_NAME = Name(["dt", "instance", "id"]) DT_DEVICE_TYPE_NAME = Name(["dt", "device", "type"]) DT_CLOCK_ENUM_NAME = Name(["dt", "clock"]) DT_PAD_NAME = Name(["dt", "pad"]) DT_PAD_DESC_NAME = Name(["dt", "pad", "desc"]) + DT_PERIPH_IO_NAME = Name(["dt", "periph", "io"]) + DT_PERIPH_IO_TYPE_FIELD_NAME = Name(["type"]) + DT_PERIPH_IO_TYPE_ENUM_NAME = Name.from_snake_case("dt_periph_io_type") + DT_PERIPH_IO_DIR_FIELD_NAME = Name(["dir"]) + DT_PERIPH_IO_DIR_ENUM_NAME = Name.from_snake_case("dt_periph_io_dir") + DT_PERIPH_IO_PERIPH_IN_DIO_PAD_FIELD_NAME = Name.from_snake_case("periph_input_or_direct_pad") + DT_PERIPH_IO_OUTSEL_FIELD_NAME = Name(["outsel"]) + + DT_PAD_NAME = Name(["dt", "pad"]) + DT_PAD_STRUCT_NAME = Name(["dt", "pad", "desc"]) + DT_PAD_TYPE_FIELD_NAME = Name(["type"]) + DT_PAD_TYPE_ENUM_NAME = Name.from_snake_case("dt_pad_type") + DT_PAD_MIO_OUT_DIO_FIELD_NAME = Name.from_snake_case("mio_out_or_direct_pad") + DT_PAD_INSEL_FIELD_NAME = Name(["insel"]) - def __init__(self, topcfg, name_to_block: Dict[str, IpBlock], enum_type, array_mapping_type): + def __init__(self, topcfg, enum_type, array_mapping_type): self.top = topcfg self._top_name = Name(["top"]) + Name.from_snake_case(topcfg["name"]) - self._name_to_block = name_to_block assert enum_type in [CEnum], "Unsupported enum type" assert array_mapping_type in [CArrayMapping], \ @@ -115,9 +236,16 @@ def __init__(self, topcfg, name_to_block: Dict[str, IpBlock], enum_type, array_m self._module_types = sorted({m["type"] for m in topcfg["module"]}) self._init_api() + self._init_pads() + self._init_irq_map() + self._init_dev_type_map() def _init_api(self): + """ + Create all API types/enums. + """ # List of all module instance types (i.e. uart, aes, etc) + # and put them in an enum. self.device_type_enum = self._enum_type(Name([]), self.DT_DEVICE_TYPE_NAME) self.device_type_enum.add_constant(Name(["unknown"]), "Instance of unknown type") for module_name in self._module_types: @@ -128,7 +256,7 @@ def _init_api(self): if isinstance(self.device_type_enum, CEnum): self.device_type_enum.add_constant(Name(["count"]), "Number of instance types") - # List of all module instance IDs + # List of all module instance IDs and put them in an enum. self.instance_id_enum = self._enum_type(Name([]), self.DT_INSTANCE_ID_NAME) self.instance_id_enum.add_constant(Name(["unknown"]), "Unknown instance") for module_name in self._module_types: @@ -146,18 +274,21 @@ def _init_api(self): # List direct pads from the pinmux to avoid pins which are not relevant. pads += [pad for pad in self.top['pinmux']['ios'] if pad['connection'] != 'muxed'] + # List all pads and put them in an enum. self.pad_enum = self._enum_type(Name([]), self.DT_PAD_NAME) + self._pad_map = OrderedDict() for pad in pads: name = pad['name'] if 'width' in pad and pad['width'] > 1: name += str(pad['idx']) + self._pad_map[name] = pad self.pad_enum.add_constant( Name.from_snake_case(name), ) if isinstance(self.pad_enum, CEnum): self.pad_enum.add_constant(Name(["count"]), "Number of pads") - # List of all clocks. + # List of all clocks and put them in an enum. self.clock_enum = self._enum_type(Name([]), self.DT_CLOCK_ENUM_NAME) clocks = self.top['clocks'] for clock in clocks["srcs"] + clocks["derived_srcs"]: @@ -165,10 +296,168 @@ def _init_api(self): self.clock_enum.add_constant(clock_name) self.clock_enum.add_constant(Name(["count"]), "Number of clocks") + # Create structure to describe a peripheral I/O and a pad. + self._create_periph_io_struct() + self._create_pad_struct() + + def _create_periph_io_struct(self): + """ + Create a structure to represent a peripheral I/O. + """ + self.periph_io_struct = StructType(self.DT_PERIPH_IO_NAME) + # In C, we want to make the fields private to we wrap them in a nested + # "__internal" struct. + st = StructType() + self.periph_io_struct.add_field( + name = Name(["__internal"]), + field_type = st, + docstring = "Private fields", + ) + + st.add_field( + name = self.DT_PERIPH_IO_TYPE_FIELD_NAME, + field_type = ScalarType(self.DT_PERIPH_IO_TYPE_ENUM_NAME), + docstring = "Peripheral I/O type", + ) + st.add_field( + name = self.DT_PERIPH_IO_DIR_FIELD_NAME, + field_type = ScalarType(self.DT_PERIPH_IO_DIR_ENUM_NAME), + docstring = "Peripheral I/O direction", + ) + st.add_field( + name = self.DT_PERIPH_IO_PERIPH_IN_DIO_PAD_FIELD_NAME, + field_type = ScalarType("uint16_t"), + docstring = """For `kDtPeriphIoTypeMio`: peripheral input number. This is the index of the MIO_PERIPH_INSEL register +that controls this peripheral I/O. + +For `kDtPeriphIoTypeDio`: DIO pad number. This is the index of the various DIO_PAD_* registers +that control this peripheral I/O.""", # noqa:E501 + ) + st.add_field( + name = self.DT_PERIPH_IO_OUTSEL_FIELD_NAME, + field_type = ScalarType("uint16_t"), + docstring = """For `kDtPeriphIoTypeMio`: peripheral output number. This is the value to put in the MIO_OUTSEL registers +to connect an output to this peripheral I/O.""", # noqa:E501 + ) + + def _create_pad_struct(self): + """ + Create a structure to represent a pad. + """ + self.pad_struct = StructType(self.DT_PAD_STRUCT_NAME) + self.pad_struct.add_field( + name = self.DT_PAD_TYPE_FIELD_NAME, + field_type = ScalarType(self.DT_PAD_TYPE_ENUM_NAME), + docstring = "Pad type", + ) + self.pad_struct.add_field( + name = self.DT_PAD_MIO_OUT_DIO_FIELD_NAME, + field_type = ScalarType("uint16_t"), + docstring = """For `kDtPadTypeMio` pads: MIO out number. This is the index of the MIO_OUTSEL register +that controls this pad (or the output part of this pad). + +For `kDtPadTypeDio`: DIO pad number. This is the index of the various DIO_PAD_* registers +that control this pad.""", # noqa:E501 + ) + self.pad_struct.add_field( + name = self.DT_PAD_INSEL_FIELD_NAME, + field_type = ScalarType("uint16_t"), + docstring = """For `kDtPadTypeMio` pads: MIO pad number. This is the value to put in the MIO_PERIPH_INSEL +registers to connect a peripheral to this pad.""", # noqa:E501 + ) + + def _init_pads(self): + """ + Create an array mapping for the list of all pads. + """ + self.pad_dt_map = ArrayMapType( + elem_type = self.pad_struct, + index_type = ScalarType(self.pad_enum.name), + length = Name(["count"]) + ) + self.pad_dt_values = OrderedDict() + for (padname, pad) in self._pad_map.items(): + topname = self.top["name"] + if pad["connection"] == "muxed": + pad_type = Name.from_snake_case("mio") + pad_mio_out_or_direct_pad = "0" + pad_insel = "0" + if pad["port_type"] in ["input", "inout"]: + pad_mio_out_or_direct_pad = \ + Name.from_snake_case(f"top_{topname}_pinmux_mio_out_{padname}").as_c_enum() + if pad["port_type"] in ["output", "inout"]: + pad_insel = \ + Name.from_snake_case(f"top_{topname}_pinmux_insel_{padname}").as_c_enum() + elif pad["connection"] == "direct": + pad_type = Name.from_snake_case("dio") + pad_mio_out_or_direct_pad = \ + Name.from_snake_case(f"top_{topname}_direct_pads_{padname}").as_c_enum() + pad_insel = "0" + else: + assert pad["connection"] == "manual", \ + "unexpected connection type '{}'".format(pad["connection"]) + pad_mio_out_or_direct_pad = "0" + pad_insel = "0" + pad_type = Name.from_snake_case("unspecified") + self.pad_dt_values[Name.from_snake_case(padname)] = { + self.DT_PAD_TYPE_FIELD_NAME: pad_type, + self.DT_PAD_MIO_OUT_DIO_FIELD_NAME: pad_mio_out_or_direct_pad, + self.DT_PAD_INSEL_FIELD_NAME: pad_insel, + } + + def _init_irq_map(self): + """ + Create the array mappings to dispatch interrupts. + """ + self.inst_from_irq_map = ArrayMapType( + elem_type = ScalarType(self.instance_id_enum.name), + index_type = ScalarType(Name(["top"]) + + Name.from_snake_case(self.top["name"]) + + Name(["plic", "irq", "id"])), + length = Name(["count"]) + ) + self.inst_from_irq_values = OrderedDict() + self.inst_from_irq_values = {Name(["none"]): Name(["unknown"])} + for intr in self.top["interrupt"]: + width = int(intr["width"]) + for i in range(width): + name = Name.from_snake_case(intr["name"]) + if width > 1: + name += Name([str(i)]) + module_name = Name.from_snake_case(intr["module_name"]) + self.inst_from_irq_values[name] = module_name + + def _init_dev_type_map(self): + """ + Create an array mapping from instance ID to device type. + """ + self.dev_type_map = ArrayMapType( + elem_type = ScalarType(self.device_type_enum.name), + index_type = ScalarType(self.instance_id_enum.name), + length = Name(["count"]), + ) + self.dev_type_values = OrderedDict() + for module_name in self._module_types: + for m in self.top["module"]: + if m["type"] != module_name: + continue + self.dev_type_values[Name.from_snake_case(m["name"])] = \ + Name.from_snake_case(module_name) + class IpHelper: - def __init__(self, topcfg, ip: IpBlock, default_node: str, enum_type, array_mapping_type): - self.top = topcfg + UNNAMED_REG_BLOCK_NAME = "core" + INST_ID_FIELD_NAME = Name(["inst", "id"]) + BASE_ADDR_FIELD_NAME = Name(["base", "addr"]) + CLOCK_FIELD_NAME = Name(["clock"]) + PERIPH_IO_FIELD_NAME = Name(["periph", "io"]) + DT_STRUCT_NAME_PREFIX = Name(["dt", "desc"]) + FIRST_IRQ_FIELD_NAME = Name(["first", "irq"]) + + def __init__(self, top_helper: TopHelper, ip: IpBlock, default_node: str, + enum_type, array_mapping_type): + self.top_helper = top_helper + self.top = top_helper.top self.ip = ip self.default_node = default_node self.ip_name = Name.from_snake_case(self.ip.name) @@ -179,11 +468,11 @@ def __init__(self, topcfg, ip: IpBlock, default_node: str, enum_type, array_mapp self._enum_type = enum_type self._array_mapping_type = array_mapping_type + # TODO: discover automatically or take that as argument. self._addr_space = "hart" # Name to assign to the unnamed reg block (if any) - self.unnamed_reg_block_name = "core" if self.default_node is None: - self.default_node = self.unnamed_reg_block_name + self.default_node = self.UNNAMED_REG_BLOCK_NAME self._init_reg_blocks() self._init_irqs() @@ -195,7 +484,7 @@ def _init_reg_blocks(self): reg_blocks = [] for rb in self.ip.reg_blocks.keys(): if rb is None: - reg_blocks.append(self.unnamed_reg_block_name) + reg_blocks.append(self.UNNAMED_REG_BLOCK_NAME) else: reg_blocks.append(rb) @@ -213,7 +502,7 @@ def has_irqs(self): return len(self.ip.interrupts) > 0 def _init_irqs(self): - device_irqs = {} + device_irqs = OrderedDict() for sig in self.ip.interrupts: if sig.bits.width() > 1: for bit in range(sig.bits.width()): @@ -238,7 +527,9 @@ def _init_clocks(self): self._device_clocks.remove("scan_clk_i") self.clock_enum = self._enum_type(Name([]), Name(["dt"]) + self.ip_name + Name(["clock"])) + self.clock_map = OrderedDict() for clk in self._device_clocks: + clk_orig = clk # Remove the clk_ prefix and _i suffix of clocks. assert clk.startswith("clk_") and clk.endswith("_i"), \ f"clock '{clk}' does not start with clk_ and end with _i" @@ -247,6 +538,7 @@ def _init_clocks(self): clk = "clk" else: clk = clk.removeprefix("clk_").removesuffix("_i") + self.clock_map[clk_orig] = clk self.clock_enum.add_constant(Name.from_snake_case(clk)) if isinstance(self.reg_block_enum, CEnum): self.clock_enum.add_constant(Name(["count"]), "Number of clock ports") @@ -256,19 +548,19 @@ def has_periph_io(self): def _init_periph_io(self): inouts, inputs, outputs = self.ip.xputs - self._device_signals = [] + self._device_signals = OrderedDict() for sig in inputs + outputs + inouts: if sig.bits.width() > 1: for bit in range(sig.bits.width()): - self._device_signals.append(sig.name + str(bit)) + self._device_signals[sig.name + str(bit)] = (sig.name, bit) else: - self._device_signals.append(sig.name) + self._device_signals[sig.name] = (sig.name, -1) self.periph_io_enum = self._enum_type( Name([]), Name(["dt"]) + self.ip_name + Name(["periph", "io"]) ) - for sig in self._device_signals: + for sig in self._device_signals.keys(): self.periph_io_enum.add_constant(Name.from_snake_case(sig)) if isinstance(self.reg_block_enum, CEnum): self.periph_io_enum.add_constant(Name(["count"]), "Number of peripheral I/O") @@ -276,6 +568,15 @@ def _init_periph_io(self): def _init_instances(self): self.inst_enum = self._enum_type(Name([]), Name(["dt"]) + self.ip_name) self.inst_map = OrderedDict() + self._create_dt_struct() + self.inst_dt_map = ArrayMapType( + elem_type = self.inst_struct, + index_type = ScalarType(self.inst_enum.name), + length = Name(["count"]) + ) + self.inst_dt_values = OrderedDict() + self.first_inst_id = None + self.last_inst_id = None for m in self.top["module"]: if m["type"] != self.ip.name: continue @@ -289,48 +590,191 @@ def _init_instances(self): inst_name = m["name"] inst_name = Name.from_snake_case(inst_name) if inst_name != "" else Name([]) self.inst_enum.add_constant(inst_name) + if self.first_inst_id is None: + self.first_inst_id = TopHelper.DT_INSTANCE_ID_NAME + Name.from_snake_case(m["name"]) + self.last_inst_id = TopHelper.DT_INSTANCE_ID_NAME + Name.from_snake_case(m["name"]) + self.inst_dt_values[inst_name] = self._create_instance(m) self.inst_map[inst_name] = m if isinstance(self.inst_enum, CEnum): self.inst_enum.add_constant(Name(["count"]), "Number of instances") - self.inst_struct = Struct(Name(["dt"]) + self.ip_name + Name(["desc"])) + def _create_dt_struct(self): + self.inst_struct = StructType(self.DT_STRUCT_NAME_PREFIX + self.ip_name) self.inst_struct.add_field( - name = Name(["inst", "id"]), - typename = ScalarType(TopHelper.DT_INSTANCE_ID_NAME), + name = self.INST_ID_FIELD_NAME, + field_type = ScalarType(TopHelper.DT_INSTANCE_ID_NAME), docstring = "Instance ID" ) self.inst_struct.add_field( - name = Name(["base", "addr"]), - typename = ArrayType( - typename = Name(["uint32"]), - length = self.reg_block_enum.name + Name(["count"]), + name = self.BASE_ADDR_FIELD_NAME, + field_type = ArrayMapType( + elem_type = ScalarType("uint32_t"), + index_type = ScalarType(self.reg_block_enum.name), + length = Name(["count"]), ), docstring = "Base address of each register block" ) if self.has_irqs(): # FIXME We need to handle better the case where a block is not connected to the PLIC. self.inst_struct.add_field( - name = Name(["first", "irq"]), - typename = ScalarType(Name(["dt", "plic", "irq", "id"])), + name = self.FIRST_IRQ_FIELD_NAME, + field_type = ScalarType(Name(["top"]) + + Name.from_snake_case(self.top["name"]) + + Name(["plic", "irq", "id"])), docstring = """PLIC ID of the first IRQ of this instance This can be `kDtPlicIrqIdNone` if the block is not connected to the PLIC.""" ) if self.has_clocks(): self.inst_struct.add_field( - name = Name(["clock"]), - typename = ArrayType( - typename = TopHelper.DT_CLOCK_ENUM_NAME, - length = self.clock_enum.name + Name(["count"]), + name = self.CLOCK_FIELD_NAME, + field_type = ArrayMapType( + elem_type = ScalarType(TopHelper.DT_CLOCK_ENUM_NAME), + index_type = ScalarType(self.clock_enum.name), + length = Name(["count"]), ), docstring = "Clock signal connected to each clock port" ) if self.has_periph_io(): self.inst_struct.add_field( - name = Name(["periph", "io"]), - typename = ArrayType( - typename = TopHelper.DT_PERIPH_IO_NAME, - length = self.periph_io_enum.name + Name(["count"]), + name = self.PERIPH_IO_FIELD_NAME, + field_type = ArrayMapType( + elem_type = self.top_helper.periph_io_struct, + index_type = ScalarType(self.periph_io_enum.name), + length = Name(["count"]), ), docstring = "Description of each peripheral I/O" ) + + def _create_instance(self, m): + """ + Fill the description structure of an instance. + """ + modname = m["name"] + inst_desc = OrderedDict() + # Instance ID. + inst_desc[self.INST_ID_FIELD_NAME] = Name.from_snake_case(modname) + # Base address map. + base_addr_map = OrderedDict() + for (rb, addr) in m["base_addrs"].items(): + if self._addr_space not in addr: + continue + if rb == "null": + rb = self.UNNAMED_REG_BLOCK_NAME + rb = Name.from_snake_case(rb) + base_addr_map[rb] = addr[self._addr_space] + inst_desc[self.BASE_ADDR_FIELD_NAME] = base_addr_map + # Clock map. + if self.has_clocks(): + inst_clock_map = OrderedDict() + for (port, clock) in m["clock_srcs"].items(): + if port not in self.clock_map: + continue + # The clock source can either be just a string with the clock name, e.g. + # clock_srcs: {clk_i: "main", clk_edn_i: "main"} + # Or a dictionary with the clock name and group: + # clock_srcs: { clk_esc_i: { clock: io_div4, group: secure } } + if isinstance(clock, str): + clk_name = clock + else: + clk_name = clock["clock"] + inst_clock_map[Name.from_snake_case(self.clock_map[port])] = \ + Name.from_snake_case(clk_name) + inst_desc[self.CLOCK_FIELD_NAME] = inst_clock_map + # First IRQ + if self.has_irqs(): + irqs_packed = [irq for irq in self.top["interrupt"] if irq["module_name"] == modname] + irqs = [] + for irq in irqs_packed: + irq_name = Name.from_snake_case(irq["name"]) + irq_width = int(irq["width"]) + if irq_width > 1: + for i in range(irq_width): + irqs.append(irq_name + Name([str(i)])) + else: + irqs.append(irq_name) + # It can happen that a block declares some interrupts but the block is not connected to + # the PLIC. For example, on english breakfast, the rv_timer is directly connected to + # Ibex and not to the PLIC. In this case, we set the first_irq to None. + # + # TODO Handle this better in the future. + if len(irqs) == 0: + first_irq = Name(["none"]) + else: + first_irq = irqs[0] + inst_desc[self.FIRST_IRQ_FIELD_NAME] = first_irq + # Periph I/O + if self.has_periph_io(): + periph_ios = OrderedDict() + for (sig, (port, idx)) in self._device_signals.items(): + found = False + for conn in self.top["pinmux"]["ios"]: + if conn["name"] != m["name"] + "_" + port or idx != conn["idx"]: + continue + if found: + logging.warning( + f"multiple connections found for device {modname}, signal {sig}") + found = True + periph_ios[Name.from_snake_case(sig)] = \ + self._create_periph_io_desc(modname, port, conn) + # If no connections were found, create a manual one to fake it. + if not found: + logging.warning(f"no connection found for device {modname}, signal {sig}") + periph_ios[Name.from_snake_case(sig)] = self._create_periph_io_missing_desc() + inst_desc[self.PERIPH_IO_FIELD_NAME] = periph_ios + + return inst_desc + + def _create_periph_io_desc(self, modname, pin_name, conn): + topname = self.top["name"] + if conn["type"] == "input": + pin_dir = "in" + elif conn["type"] == "output": + pin_dir = "out" + else: + assert conn["type"] == "inout", \ + "unexpected connection dir '{}'".format(conn["type"]) + pin_dir = "inout" + + if conn["idx"] != -1: + pin_name += str(conn["idx"]) + if conn["connection"] == "muxed": + pin_type = "Mio" + pin_periph_input_or_direct_pad = "0" + pin_outsel = "0" + if conn["type"] in ["input", "inout"]: + periph_in = f"top_{topname}_pinmux_peripheral_in_{modname}_{pin_name}" + pin_periph_input_or_direct_pad = Name.from_snake_case(periph_in).as_c_enum() + if conn["type"] in ["output", "inout"]: + outsel = f"top_{topname}_pinmux_outsel_{modname}_{pin_name}" + pin_outsel = Name.from_snake_case(outsel).as_c_enum() + elif conn["connection"] == "direct": + pin_type = "Dio" + direct_pad = f"top_{topname}_direct_pads_{modname}_{pin_name}" + pin_periph_input_or_direct_pad = Name.from_snake_case(direct_pad).as_c_enum() + pin_outsel = "0" + else: + assert conn["connection"] == "manual", \ + "unexpected connection type '{}'".format(conn["connection"]) + pin_periph_input_or_direct_pad = "0" + pin_outsel = "0" + pin_type = "Unspecified" + + return { + Name(["__internal"]): { + TopHelper.DT_PERIPH_IO_TYPE_FIELD_NAME: Name.from_snake_case(pin_type), + TopHelper.DT_PERIPH_IO_DIR_FIELD_NAME: Name.from_snake_case(pin_dir), + TopHelper.DT_PERIPH_IO_PERIPH_IN_DIO_PAD_FIELD_NAME: pin_periph_input_or_direct_pad, + TopHelper.DT_PERIPH_IO_OUTSEL_FIELD_NAME: pin_outsel, + } + } + + def _create_periph_io_missing_desc(self): + return { + Name(["__internal"]): { + TopHelper.DT_PERIPH_IO_TYPE_FIELD_NAME: Name(["unspecified"]), + TopHelper.DT_PERIPH_IO_DIR_FIELD_NAME: Name(["inout"]), + TopHelper.DT_PERIPH_IO_PERIPH_IN_DIO_PAD_FIELD_NAME: "0", + TopHelper.DT_PERIPH_IO_OUTSEL_FIELD_NAME: "0", + } + } diff --git a/util/dttool.py b/util/dttool.py index 3605a1891a966..53d99f0e3955c 100644 --- a/util/dttool.py +++ b/util/dttool.py @@ -7,7 +7,6 @@ import argparse import logging import hjson -import sys from mako.template import Template from mako import exceptions from pathlib import Path @@ -89,33 +88,21 @@ def main(): with open(args.topgencfg) as handle: topcfg = hjson.load(handle, use_decimal=True) - if args.gen_top: - helper = TopHelper(topcfg, name_to_block, CEnum, CArrayMapping) - - module_types = {m["type"] for m in topcfg["module"]} - dt_headers = [] - for m in module_types: - dt_headers.append(f"dt_{m}.h") - if m not in name_to_block: - logging.error("IP block {} required by top but not specified using -i".format(m)) - sys.exit(1) + top_helper = TopHelper(topcfg, CEnum, CArrayMapping) + if args.gen_top: top_lib_header = "hw/top_{0}/sw/autogen/top_{0}.h".format(topcfg["name"]) render_template( TOPGEN_TEMPLATE_PATH / "dt_api.h.tpl", outdir / "dt_api.h", - helper = helper, + helper = top_helper, top_lib_header = top_lib_header, ) - render_template( TOPGEN_TEMPLATE_PATH / "dt_api.c.tpl", outdir / "dt_api.c", - top=topcfg, - name_to_block = name_to_block, - dt_headers = dt_headers, - helper = helper, + helper = top_helper, ) if args.gen_ip: @@ -128,7 +115,7 @@ def main(): logging.warning(f"IP {ipname} has more than one register block node " + f"but no default was specified, will use {default_node}") - helper = IpHelper(topcfg, ip, default_node, CEnum, CArrayMapping) + helper = IpHelper(top_helper, ip, default_node, CEnum, CArrayMapping) render_template( TOPGEN_TEMPLATE_PATH / "dt_ip.h.tpl", diff --git a/util/topgen/lib.py b/util/topgen/lib.py index a0b6091e8d32e..250f3a39a36e2 100644 --- a/util/topgen/lib.py +++ b/util/topgen/lib.py @@ -253,7 +253,8 @@ class Name: To simplify parsing and reassembling of name strings, this class stores the name parts as a canonical list of strings internally - (in self.parts). + (in self._parts). The content of a name cannot be changed once it is + created. The "from_*" functions parse and split a name string into the canonical list, whereas the "as_*" functions reassemble the canonical list in the @@ -264,23 +265,32 @@ class Name: internal representation into "ExampleName". """ def __add__(self, other): - return Name(self.parts + other.parts) + return Name(self._parts + other._parts) + + def __repr__(self): + return "Name({})".format(self._parts) + + def __hash__(self): + return hash(self._parts) + + def __eq__(self, other): + return self._parts == other._parts @staticmethod def from_snake_case(input: str) -> 'Name': return Name(input.split("_")) def __init__(self, parts: List[str]): - self.parts = parts + self._parts = tuple(parts) for p in parts: assert len(p) > 0, "cannot add zero-length name piece" def as_snake_case(self) -> str: - return "_".join([p.lower() for p in self.parts]) + return "_".join([p.lower() for p in self._parts]) def as_camel_case(self) -> str: out = "" - for p in self.parts: + for p in self._parts: # If we're about to join two parts which would introduce adjacent # numbers, put an underscore between them. if out[-1:].isnumeric() and p[:1].isnumeric(): @@ -290,7 +300,7 @@ def as_camel_case(self) -> str: return out def as_c_define(self) -> str: - return "_".join([p.upper() for p in self.parts]) + return "_".join([p.upper() for p in self._parts]) def as_c_enum(self) -> str: return "k" + self.as_camel_case() @@ -302,13 +312,13 @@ def as_rust_type(self) -> str: return self.as_camel_case() def as_rust_const(self) -> str: - return "_".join([p.upper() for p in self.parts]) + return "_".join([p.upper() for p in self._parts]) def as_rust_enum(self) -> str: return self.as_camel_case() def remove_part(self, part_to_remove: str) -> "Name": - return Name([p for p in self.parts if p != part_to_remove]) + return Name([p for p in self._parts if p != part_to_remove]) class MemoryRegion(object):