diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2021-05-03 10:36:15 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2021-07-21 16:27:58 +0200 |
commit | 72508ab04e1008d9afe278a44b688c692199f5bb (patch) | |
tree | 6f885658b0147638c23799b672adb3cc556c97a9 | |
parent | validation: Support test cases in build objects (diff) | |
download | rtems-central-72508ab04e1008d9afe278a44b688c692199f5bb.tar.bz2 |
interface: Add register-block support
-rw-r--r-- | rtemsspec/interface.py | 191 | ||||
-rw-r--r-- | rtemsspec/tests/spec-interface/c/if/uint32_t.yml | 17 | ||||
-rw-r--r-- | rtemsspec/tests/spec-interface/irqamp-timestamp.yml | 123 | ||||
-rw-r--r-- | rtemsspec/tests/spec-interface/irqamp.yml | 72 | ||||
-rw-r--r-- | rtemsspec/tests/spec/interface-more.yml | 4 | ||||
-rw-r--r-- | rtemsspec/tests/test_interface.py | 192 | ||||
-rw-r--r-- | spec/spec/interface-register-block.yml | 26 |
7 files changed, 619 insertions, 6 deletions
diff --git a/rtemsspec/interface.py b/rtemsspec/interface.py index 61335562..4a637cf6 100644 --- a/rtemsspec/interface.py +++ b/rtemsspec/interface.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: BSD-2-Clause """ This module provides functions for the generation of interfaces. """ -# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) +# Copyright (C) 2020, 2021 embedded brains GmbH (http://www.embedded-brains.de) # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -24,13 +24,18 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +import collections from contextlib import contextmanager +import functools +import itertools import os -from typing import Any, Callable, Dict, Iterator, List, Optional, Union, Set +from typing import Any, Callable, Dict, Iterator, List, NamedTuple, Optional, \ + Union, Set, Tuple from rtemsspec.content import CContent, CInclude, enabled_by_to_exp, \ ExpressionMapper, get_value_double_colon, get_value_doxygen_function, \ - get_value_doxygen_group, get_value_hash, get_value_params, get_value_plural + get_value_doxygen_group, get_value_hash, get_value_params, \ + get_value_plural, to_camel_case from rtemsspec.items import Item, ItemCache, ItemGetValueContext, \ ItemGetValueMap, ItemMapper @@ -182,6 +187,35 @@ def _add_definition(node: "Node", item: Item, prefix: str, return content +class _RegisterMemberContext(NamedTuple): + sizes: Dict[int, int] + regs: Dict[str, Any] + reg_counts: Dict[str, int] + reg_indices: Dict[str, int] + + +def _add_register_padding(content: CContent, new_offset: int, old_offset: int, + default_padding: int) -> None: + delta = new_offset - old_offset + if delta > 0: + padding = default_padding + while delta % padding != 0: + padding //= 2 + count = delta // padding + array = f"[ {count} ]" if count > 1 else "" + content.add(f"uint{padding * 8}_t " + f"reserved_{old_offset:x}_{new_offset:x}{array};") + + +def _get_register_name(definition: Dict[str, Any]) -> Tuple[str, str]: + name = definition["name"] + try: + name, alias = name.split(":") + except ValueError: + alias = name + return name, alias + + class Node: """ Nodes of a header file. """ @@ -297,6 +331,105 @@ class Node: """ Generates a macro. """ self._add_generic_definition(Node._get_macro_definition) + def _add_register_bits(self, group: str) -> _RegisterMemberContext: + ctx = _RegisterMemberContext({}, {}, collections.defaultdict(int), + collections.defaultdict(int)) + for index, register in enumerate(self.item["registers"]): + name = register["name"] + group_ident = group + to_camel_case(name) + ctx.regs[name] = {} + width = register["width"] + assert width in [8, 16, 32, 64] + ctx.regs[name]["size"] = width // 8 + ctx.regs[name]["type"] = f"uint{width}_t" + ctx.regs[name]["group"] = group_ident + with self.content.defgroup_block(group_ident, name): + self.content.add_brief_description( + self.substitute_text(register["brief"])) + self.content.doxyfy( + self.substitute_text(register["description"])) + self.content.add("@{") + for index_2, bits in enumerate(register["bits"]): + self.content.add( + _add_definition( + self, self.item, f"registers[{index}]/bits[{index_2}]", + bits, + functools.partial(Node._get_register_bits_definition, + reg_name=name))) + self.content.close_add_to_group() + return ctx + + def _add_register_block_includes(self, + ctx: _RegisterMemberContext) -> None: + for link in self.item.links_to_parents("register-block-include"): + name = link["name"] + ctx.regs[name] = {} + ctx.regs[name]["size"] = link.item["register-block-size"] + ctx.regs[name]["type"] = link.item["name"] + ctx.regs[name]["group"] = link.item["identifier"] + + def _get_register_member_info(self, ctx: _RegisterMemberContext) -> None: + offset = -1 + for index, member in enumerate(self.item["definition"]): + assert member["offset"] > offset + offset = member["offset"] + default = [member["default"]] if member["default"] else [] + for index_2, definition in enumerate( + itertools.chain(default, + (variant["definition"] + for variant in member["variants"]))): + name, alias = _get_register_name(definition) + assert name.lower() != "reserved" + count = definition["count"] + if index_2 == 0: + ctx.sizes[index] = ctx.regs[name]["size"] * count + else: + assert ctx.sizes[index] == ctx.regs[name]["size"] * count + ctx.reg_counts[alias] += 1 + + def _add_register_members(self, ctx: _RegisterMemberContext) -> None: + default_padding = min(ctx.sizes.values()) + with self.content.doxygen_block(): + self.content.add_brief_description( + self.substitute_text(self.item["brief"])) + self.content.doxyfy(self.substitute_text(self.item["description"])) + self.content.append(f"typedef struct {self.item['name']} {{") + offset = 0 + with self.content.indent(): + for index, member in enumerate(self.item["definition"]): + member_offset = member["offset"] + _add_register_padding(self.content, member_offset, offset, + default_padding) + self.content.add( + _add_definition( + self, self.item, f"definition[{index}]", member, + functools.partial(Node._get_register_member_definition, + ctx=ctx))) + offset = member_offset + ctx.sizes[index] + size = self.item["register-block-size"] + assert offset <= size + _add_register_padding(self.content, size, offset, default_padding) + self.content.add(f"}} {self.item['name']};") + + def generate_register_block(self) -> None: + """ Generates a register block. """ + self.header_file.add_includes(self.item.map("/c/if/uint32_t")) + for parent in self.item.parents("register-block-include"): + self.header_file.add_includes(parent) + self.header_file.add_dependency(self, parent) + group = self.item["identifier"] + name = self.item["register-block-group"] + with self.content.defgroup_block(group, name): + self.content.add_ingroup(_get_group_identifiers(self.ingroups)) + self.content.add_brief_description( + f"This group contains the {name} interfaces.") + self.content.add("@{") + ctx = self._add_register_bits(group) + self._add_register_block_includes(ctx) + self._get_register_member_info(ctx) + self._add_register_members(ctx) + self.content.close_add_to_group() + def generate_typedef(self) -> None: """ Generates a typedef. """ self._add_generic_definition(Node._get_typedef_definition) @@ -400,6 +533,54 @@ class Node: body += " \\\n ".join(body_lines) return line + body + def _get_register_bits_definition(self, _item: Item, definition: Any, + reg_name: str) -> Lines: + lines = [] # List[str] + prefix = self.item["register-prefix"] + if prefix is None: + prefix = self.item["name"] + prefix = f"{prefix}_{reg_name}_" if prefix else f"{reg_name}_" + for index, bit in enumerate(definition): + start = bit["start"] + width = bit["width"] + end = start + width + sfx = "ULL" if end > 32 else "U" + define = f"#define {prefix.upper()}{bit['name'].upper()}" + if index != 0: + lines.append("") + if width == 1: + val = 1 << start + lines.append(f"{define} {val:#x}{sfx}") + else: + mask = ((1 << width) - 1) << start + get = (1 << width) - 1 + lines.extend([ + f"{define}_SHIFT {start}", f"{define}_MASK {mask:#x}{sfx}", + f"{define}_GET( _reg ) \\", + f" ( ( ( _reg ) >> {start} ) & {get:#x}{sfx} )", + f"{define}( _val ) ( ( _val ) << {start} )" + ]) + return lines + + def _get_register_member_definition(self, _item: Item, definition: Any, + ctx: _RegisterMemberContext) -> Lines: + # pylint: disable=no-self-use + name, alias = _get_register_name(definition) + count = definition["count"] + array = f"[ {count} ]" if count > 1 else "" + if ctx.reg_counts[alias] > 1: + index = ctx.reg_indices[alias] + ctx.reg_indices[alias] = index + 1 + idx = f"_{index}" + else: + idx = "" + content = CContent() + with content.doxygen_block(): + content.add(f"@brief See @ref {ctx.regs[name]['group']}.") + content.append( + f"{ctx.regs[name]['type']} {alias.lower()}{idx}{array};") + return content.lines + def _get_typedef_definition(self, _item: Item, definition: Any) -> Lines: return f"typedef {self.substitute_code(definition)};" @@ -450,6 +631,7 @@ _NODE_GENERATORS = { "function": Node.generate_function, "group": Node.generate_group, "macro": Node.generate_macro, + "register-block": Node.generate_register_block, "struct": Node.generate_compound, "typedef": Node.generate_typedef, "union": Node.generate_compound, @@ -585,8 +767,7 @@ class _HeaderFile: includes.extend([ CInclude(link.item["path"], enabled_by_to_exp(link["enabled-by"], exp_mapper)) - for link in self._item.links_to_parents() - if link.role == "interface-include" + for link in self._item.links_to_parents("interface-include") ]) self._content.add_includes(includes) with self._content.extern_c(): diff --git a/rtemsspec/tests/spec-interface/c/if/uint32_t.yml b/rtemsspec/tests/spec-interface/c/if/uint32_t.yml new file mode 100644 index 00000000..ce9cd90d --- /dev/null +++ b/rtemsspec/tests/spec-interface/c/if/uint32_t.yml @@ -0,0 +1,17 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +brief: null +copyrights: +- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) +definition: + default: unsigned + variants: [] +description: null +enabled-by: true +index-entries: [] +interface-type: define +links: +- role: interface-placement + uid: ../../stdint +name: uint32_t +notes: null +type: interface diff --git a/rtemsspec/tests/spec-interface/irqamp-timestamp.yml b/rtemsspec/tests/spec-interface/irqamp-timestamp.yml new file mode 100644 index 00000000..9ec01ead --- /dev/null +++ b/rtemsspec/tests/spec-interface/irqamp-timestamp.yml @@ -0,0 +1,123 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +brief: | + This structure defines the ${.:/register-block-group} register block memory + map. +copyrights: +- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de) +description: null +enabled-by: true +identifier: IrqampTimestamp +index-entries: [] +interface-type: register-block +links: +- role: interface-placement + uid: h +definition: +- default: + count: 1 + name: ITCNT + offset: 0x0 + variants: [] +- default: + count: 1 + name: ITSTMPC + offset: 0x4 + variants: [] +- default: + count: 1 + name: ITSTMPAS + offset: 0x8 + variants: [] +- default: + count: 1 + name: ITSTMPAC + offset: 0xc + variants: [] +register-prefix: IRQAMP +register-block-group: IRQ(A)MP Timestamp +register-block-size: 16 +register-block-type: memory +registers: +- bits: + - default: + - access: [r] + brief: null + description: null + name: 'TCNT' + start: 0 + width: 32 + variants: [] + brief: | + Interrupt timestamp counter n register + description: null + name: ITCNT + width: 32 +- bits: + - default: + - access: [r] + brief: null + description: null + name: 'TSTAMP' + start: 27 + width: 5 + - access: [r, w1c] + brief: null + description: null + name: 'S1' + start: 26 + width: 1 + - access: [r, w1c] + brief: null + description: null + name: 'S2' + start: 25 + width: 1 + - access: [r, w] + brief: null + description: null + name: 'KS' + start: 5 + width: 1 + - access: [r, w] + brief: null + description: null + name: 'TSISEL' + start: 0 + width: 5 + variants: [] + brief: | + Interrupt timestamp n control register + description: null + name: ITSTMPC + width: 32 +- bits: + - default: + - access: [r] + brief: null + description: null + name: 'TASSERTION' + start: 0 + width: 32 + variants: [] + brief: | + Interrupt Assertion Timestamp n register + description: null + name: ITSTMPAS + width: 32 +- bits: + - default: + - access: [r] + brief: null + description: null + name: 'TACKNOWLEDGE' + start: 0 + width: 32 + variants: [] + brief: | + Interrupt Acknowledge Timestamp n register + description: null + name: ITSTMPAC + width: 32 +name: irqamp_timestamp +notes: null +type: interface diff --git a/rtemsspec/tests/spec-interface/irqamp.yml b/rtemsspec/tests/spec-interface/irqamp.yml new file mode 100644 index 00000000..beccec9a --- /dev/null +++ b/rtemsspec/tests/spec-interface/irqamp.yml @@ -0,0 +1,72 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +brief: | + This structure defines the ${.:/register-block-group} register block memory + map. +copyrights: +- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de) +description: null +enabled-by: true +identifier: Irqamp +index-entries: [] +interface-type: register-block +links: +- role: interface-placement + uid: h +- name: ITSTMP + role: register-block-include + uid: irqamp-timestamp +definition: +- default: + count: 1 + name: ILEVEL:FOOBAR + offset: 0x0 + variants: [] +- default: + count: 1 + name: ILEVEL:FOOBAR + offset: 0x4 + variants: + - definition: + count: 4 + name: IPEND8 + enabled-by: defined(RTEMS_SMP) +- default: + count: 4 + name: IPEND8 + offset: 0x9 + variants: [] +- default: + count: 16 + name: ITSTMP + offset: 0x100 + variants: [] +register-prefix: null +register-block-group: IRQ(A)MP +register-block-size: 1024 +register-block-type: memory +registers: +- bits: + - default: + - access: [r, w] + brief: null + description: null + name: 'IL_15_1' + start: 1 + width: 15 + variants: [] + brief: | + Interrupt level register + description: null + name: ILEVEL + width: 32 +- bits: + - default: [] + variants: [] + brief: | + Interrupt pending register + description: null + name: IPEND8 + width: 8 +name: irqamp +notes: null +type: interface diff --git a/rtemsspec/tests/spec/interface-more.yml b/rtemsspec/tests/spec/interface-more.yml index 9c7917ec..aff1b49a 100644 --- a/rtemsspec/tests/spec/interface-more.yml +++ b/rtemsspec/tests/spec/interface-more.yml @@ -59,6 +59,10 @@ links: uid: interface - role: spec-refinement spec-key: interface-type + spec-value: register-block + uid: interface +- role: spec-refinement + spec-key: interface-type spec-value: typedef uid: interface - role: spec-refinement diff --git a/rtemsspec/tests/test_interface.py b/rtemsspec/tests/test_interface.py index b7b0feea..9e23bb39 100644 --- a/rtemsspec/tests/test_interface.py +++ b/rtemsspec/tests/test_interface.py @@ -61,7 +61,7 @@ def test_interface(tmpdir): */ /* - * Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) + * Copyright (C) 2020, 2021 embedded brains GmbH (http://www.embedded-brains.de) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -242,6 +242,196 @@ void Function( /* Generated from spec:/func6 */ void Function6( int Param0 ); +/* Generated from spec:/irqamp-timestamp */ + +/** + * @defgroup IrqampTimestamp IRQ(A)MP Timestamp + * + * @brief This group contains the IRQ(A)MP Timestamp interfaces. + * + * @{ + */ + +/** + * @defgroup IrqampTimestampITCNT ITCNT + * + * @brief Interrupt timestamp counter n register + * + * @{ + */ + +#define IRQAMP_ITCNT_TCNT_SHIFT 0 +#define IRQAMP_ITCNT_TCNT_MASK 0xffffffffU +#define IRQAMP_ITCNT_TCNT_GET( _reg ) \\ + ( ( ( _reg ) >> 0 ) & 0xffffffffU ) +#define IRQAMP_ITCNT_TCNT( _val ) ( ( _val ) << 0 ) + +/** @} */ + +/** + * @defgroup IrqampTimestampITSTMPC ITSTMPC + * + * @brief Interrupt timestamp n control register + * + * @{ + */ + +#define IRQAMP_ITSTMPC_TSTAMP_SHIFT 27 +#define IRQAMP_ITSTMPC_TSTAMP_MASK 0xf8000000U +#define IRQAMP_ITSTMPC_TSTAMP_GET( _reg ) \\ + ( ( ( _reg ) >> 27 ) & 0x1fU ) +#define IRQAMP_ITSTMPC_TSTAMP( _val ) ( ( _val ) << 27 ) + +#define IRQAMP_ITSTMPC_S1 0x4000000U + +#define IRQAMP_ITSTMPC_S2 0x2000000U + +#define IRQAMP_ITSTMPC_KS 0x20U + +#define IRQAMP_ITSTMPC_TSISEL_SHIFT 0 +#define IRQAMP_ITSTMPC_TSISEL_MASK 0x1fU +#define IRQAMP_ITSTMPC_TSISEL_GET( _reg ) \\ + ( ( ( _reg ) >> 0 ) & 0x1fU ) +#define IRQAMP_ITSTMPC_TSISEL( _val ) ( ( _val ) << 0 ) + +/** @} */ + +/** + * @defgroup IrqampTimestampITSTMPAS ITSTMPAS + * + * @brief Interrupt Assertion Timestamp n register + * + * @{ + */ + +#define IRQAMP_ITSTMPAS_TASSERTION_SHIFT 0 +#define IRQAMP_ITSTMPAS_TASSERTION_MASK 0xffffffffU +#define IRQAMP_ITSTMPAS_TASSERTION_GET( _reg ) \\ + ( ( ( _reg ) >> 0 ) & 0xffffffffU ) +#define IRQAMP_ITSTMPAS_TASSERTION( _val ) ( ( _val ) << 0 ) + +/** @} */ + +/** + * @defgroup IrqampTimestampITSTMPAC ITSTMPAC + * + * @brief Interrupt Acknowledge Timestamp n register + * + * @{ + */ + +#define IRQAMP_ITSTMPAC_TACKNOWLEDGE_SHIFT 0 +#define IRQAMP_ITSTMPAC_TACKNOWLEDGE_MASK 0xffffffffU +#define IRQAMP_ITSTMPAC_TACKNOWLEDGE_GET( _reg ) \\ + ( ( ( _reg ) >> 0 ) & 0xffffffffU ) +#define IRQAMP_ITSTMPAC_TACKNOWLEDGE( _val ) ( ( _val ) << 0 ) + +/** @} */ + +/** + * @brief This structure defines the IRQ(A)MP Timestamp register block memory + * map. + */ +typedef struct irqamp_timestamp { + /** + * @brief See @ref IrqampTimestampITCNT. + */ + uint32_t itcnt; + + /** + * @brief See @ref IrqampTimestampITSTMPC. + */ + uint32_t itstmpc; + + /** + * @brief See @ref IrqampTimestampITSTMPAS. + */ + uint32_t itstmpas; + + /** + * @brief See @ref IrqampTimestampITSTMPAC. + */ + uint32_t itstmpac; +} irqamp_timestamp; + +/** @} */ + +/* Generated from spec:/irqamp */ + +/** + * @defgroup Irqamp IRQ(A)MP + * + * @brief This group contains the IRQ(A)MP interfaces. + * + * @{ + */ + +/** + * @defgroup IrqampILEVEL ILEVEL + * + * @brief Interrupt level register + * + * @{ + */ + +#define IRQAMP_ILEVEL_IL_15_1_SHIFT 1 +#define IRQAMP_ILEVEL_IL_15_1_MASK 0xfffeU +#define IRQAMP_ILEVEL_IL_15_1_GET( _reg ) \\ + ( ( ( _reg ) >> 1 ) & 0x7fffU ) +#define IRQAMP_ILEVEL_IL_15_1( _val ) ( ( _val ) << 1 ) + +/** @} */ + +/** + * @defgroup IrqampIPEND8 IPEND8 + * + * @brief Interrupt pending register + * + * @{ + */ + +/** @} */ + +/** + * @brief This structure defines the IRQ(A)MP register block memory map. + */ +typedef struct irqamp { + /** + * @brief See @ref IrqampILEVEL. + */ + uint32_t foobar_0; + + #if defined(RTEMS_SMP) + /** + * @brief See @ref IrqampIPEND8. + */ + uint8_t ipend8_0[ 4 ]; + #else + /** + * @brief See @ref IrqampILEVEL. + */ + uint32_t foobar_1; + #endif + + uint8_t reserved_8_9; + + /** + * @brief See @ref IrqampIPEND8. + */ + uint8_t ipend8_1[ 4 ]; + + uint8_t reserved_d_100[ 243 ]; + + /** + * @brief See @ref IrqampTimestamp. + */ + irqamp_timestamp itstmp[ 16 ]; + + uint32_t reserved_200_400[ 128 ]; +} irqamp; + +/** @} */ + /* Generated from spec:/macro */ /** diff --git a/spec/spec/interface-register-block.yml b/spec/spec/interface-register-block.yml new file mode 100644 index 00000000..23f3ef4e --- /dev/null +++ b/spec/spec/interface-register-block.yml @@ -0,0 +1,26 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +copyrights: +- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) +enabled-by: true +links: +- role: spec-member + uid: root +- role: spec-refinement + spec-key: interface-type + spec-value: register-block + uid: interface +spec-description: null +spec-example: null +spec-info: + dict: + attributes: {} + description: | + This set of attributes defines a register block. + generic-attributes: + description: null + key-spec-type: name + value-spec-type: any + mandatory-attributes: all +spec-name: Interface Register Block Item Type +spec-type: interface-register-block +type: spec |