diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 88ae95c..7b5b796 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,13 +19,21 @@ jobs: strategy: matrix: python-version: + - 3.6 - 3.7 - 3.8 - 3.9 - "3.10" - "3.11" + - "3.12" + include: + - os: ubuntu-latest - runs-on: ubuntu-latest + # older versions need older OS + - python-version: 3.6 + os: ubuntu-20.04 + + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 @@ -37,10 +45,41 @@ jobs: - name: Install dependencies run: | - python -m pip install pytest + python -m pip install -U -r tests/requirements.txt + + - name: Install + run: | + python -m pip install . + - name: Test + run: | + cd tests + pytest --cov=peakrdl_cheader - # TODO: Add actual testcase + - name: Coveralls + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_PARALLEL: true + run: | + cd tests + coveralls --service=github + + finish_coveralls: + needs: test + runs-on: ubuntu-latest + steps: + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: 3.x + + - name: Coveralls + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_PARALLEL: true + run: | + python -m pip install -U coveralls>=3.0.0 + coveralls --service=github --finish #------------------------------------------------------------------------------- lint: diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 5b13f32..354c013 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -13,3 +13,5 @@ sphinx: python: install: - requirements: docs/requirements.txt + - method: pip + path: . diff --git a/docs/output.rst b/docs/output.rst index 8c56a10..137c796 100644 --- a/docs/output.rst +++ b/docs/output.rst @@ -3,6 +3,7 @@ Generated Header Contents Block Structs ------------- +TODO: Add details. possible unions, anon unions Bit-fiddling Field Macros ------------------------- @@ -46,3 +47,7 @@ Register bitfield structs If enabled, each register is represented by a nested union & struct definition. This allows a register value to be accessed in aggregate, or by its individual bit-fields. + +TODO: Add details. union structure. Overlapping fields + +TODO: Add section on wide regs? diff --git a/src/peakrdl_cheader/header_generator.py b/src/peakrdl_cheader/header_generator.py index 96ebadc..b785913 100644 --- a/src/peakrdl_cheader/header_generator.py +++ b/src/peakrdl_cheader/header_generator.py @@ -124,8 +124,8 @@ def write_bitfields(self, grp_name: str, regwidth: int, fields: List[FieldNode]) self.write(f"uint{regwidth}_t {kwf(field.inst_name)} :{field.width:d};\n") current_offset -= field.width - if current_offset > 0: - self.write(f"uint{regwidth}_t :{current_offset:d};\n") + if current_offset > -1: + self.write(f"uint{regwidth}_t :{current_offset + 1:d};\n") self.pop_indent() self.write(f"}} {grp_name};\n") diff --git a/src/peakrdl_cheader/testcase_generator.py b/src/peakrdl_cheader/testcase_generator.py index 2fc0c91..11ad96e 100644 --- a/src/peakrdl_cheader/testcase_generator.py +++ b/src/peakrdl_cheader/testcase_generator.py @@ -1,11 +1,13 @@ -from typing import List, TextIO, Set +from typing import List, TextIO, Set, Match import os +import re -from systemrdl.walker import RDLListener, RDLWalker, WalkerAction +from systemrdl.walker import RDLListener, RDLWalker from systemrdl.node import AddrmapNode, RegNode, AddressableNode from .design_state import DesignState from . import utils +from .identifier_filter import kw_filter as kwf class TestcaseGenerator: def __init__(self, ds: DesignState) -> None: @@ -57,6 +59,7 @@ def run(self, f: TextIO, top_nodes: List[AddrmapNode]) -> None: f.write("static void test_offsets(void){\n") self.push_indent() for node in top_nodes: + node.zero_lineage_index() self.root_node = node self.root_struct_name = utils.get_struct_name(self.ds, node, node) RDLWalker(unroll=True).walk(node, self) @@ -111,20 +114,26 @@ def enter_Reg(self, node: RegNode) -> None: else: member = node.get_rel_path(self.root_node) + # Sanitize keywords + def kwrepl(m: Match) -> str: + return kwf(m.group(0)) + member = re.sub(r"\w+", kwrepl, member) + if self.ds.generate_bitfields: # Reg is defined as a bitfield union. Access entire word member member += ".w" regwidth = node.get_property('regwidth') + node_addr = node.absolute_address - self.root_node.absolute_address if regwidth > 64: # Reg is split into an array of subwords n_subwords = regwidth // self.ds.wide_reg_subword_size stride = self.ds.wide_reg_subword_size // 8 for i in range(n_subwords): - addr = f"{node.absolute_address + i * stride:#x}UL" + addr = f"{node_addr + i * stride:#x}UL" self.write(f"assert(offsetof({self.root_struct_name}, {member}[{i}]) == {addr});\n") else: - self.write(f"assert(offsetof({self.root_struct_name}, {member}) == {node.absolute_address:#x}UL);\n") + self.write(f"assert(offsetof({self.root_struct_name}, {member}) == {node_addr:#x}UL);\n") @@ -202,7 +211,7 @@ def enter_Reg(self, node: RegNode) -> None: for field in fields: field_prefix = prefix + "__" + field.inst_name.upper() self.write("reg.w = 0;\n") - self.write(f"reg.{grp_name}.{field.inst_name} = {(1 << field.width) - 1:#x};\n") + self.write(f"reg.{grp_name}.{kwf(field.inst_name)} = {(1 << field.width) - 1:#x};\n") self.write(f"assert(reg.w == {field_prefix}_bm);\n") self.pop_indent() self.write("}\n") diff --git a/src/peakrdl_cheader/utils.py b/src/peakrdl_cheader/utils.py index f3595c1..aabf29e 100644 --- a/src/peakrdl_cheader/utils.py +++ b/src/peakrdl_cheader/utils.py @@ -3,14 +3,8 @@ def get_node_prefix(ds: DesignState, root_node: AddrmapNode, node: AddressableNode) -> str: if ds.reuse_typedefs: - prefix = node.inst.get_scope_path("__") - if prefix is not None: - # Complete the scope path - if prefix == "": - prefix = node.type_name - else: - prefix += "__" + node.type_name - else: + prefix = node.get_global_type_name("__") + if prefix is None: # Unable to determine a reusable type name. Fall back to hierarchical path # Add prefix to prevent collision when mixing namespace methods prefix = "xtern__" + node.get_rel_path( @@ -46,14 +40,9 @@ def get_friendly_name(ds: DesignState, root_node: AddrmapNode, node: Node) -> st a comment """ if ds.reuse_typedefs: - scope_path = node.inst.get_scope_path() + friendly_name = node.get_global_type_name("::") - if scope_path is not None and node.type_name is not None: - if scope_path == "": - friendly_name = node.type_name - else: - friendly_name = scope_path + "::" + node.type_name - else: + if friendly_name is None: # Unable to determine a reusable type name. Fall back to hierarchical path friendly_name = node.get_rel_path(root_node.parent) else: diff --git a/tests/.coveragerc b/tests/.coveragerc new file mode 100644 index 0000000..b19d92d --- /dev/null +++ b/tests/.coveragerc @@ -0,0 +1,20 @@ +[run] +branch = True +#relative_files = True + +omit = + # to be covered elsewhere + */__peakrdl__.py + +[paths] +source = + ../src/peakrdl_cheader/ + */site-packages/*/peakrdl_cheader + */site-packages/peakrdl_cheader + +[report] +exclude_lines = + pragma: no cover + if TYPE_CHECKING: + +precision = 1 diff --git a/tests/base.py b/tests/base.py index 30af137..1ec665c 100644 --- a/tests/base.py +++ b/tests/base.py @@ -15,7 +15,7 @@ def get_permutations(spec): return param_list class BaseHeaderTestcase(TestCase): - rdl_file = "testcases/basic.rdl" + rdl_file = "" # Export parameters std = CStandard.latest @@ -24,20 +24,34 @@ class BaseHeaderTestcase(TestCase): reuse_typedefs = True wide_reg_subword_size = 32 explode_top = False + instantiate = False @classmethod def get_run_dir(cls) -> str: this_dir = os.path.dirname(__file__) - run_dir = os.path.join(this_dir, cls.__name__ + ".out") + run_dir = os.path.join(this_dir, "test.out", cls.__name__) return run_dir @property def output_dir(self) -> str: return self.get_run_dir() + @classmethod + def _write_params(cls) -> None: + """ + Write out the class parameters to a file so that it is easier to debug + how a testcase was parameterized + """ + path = os.path.join(cls.get_run_dir(), "params.txt") + + with open(path, 'w') as f: + for k, v in cls.__dict__.items(): + if k.startswith("_") or callable(v): + continue + f.write(f"{k}: {repr(v)}\n") + def do_export(self): - if not os.path.exists(self.output_dir): - os.mkdir(self.output_dir) + os.makedirs(self.output_dir, exist_ok=True) rdl_path = os.path.join(os.path.dirname(__file__), self.rdl_file) @@ -55,11 +69,13 @@ def do_export(self): reuse_typedefs=self.reuse_typedefs, wide_reg_subword_size=self.wide_reg_subword_size, explode_top=self.explode_top, - instantiate=False, + instantiate=self.instantiate, inst_offset=0, testcase=True, ) + self._write_params() + def do_compile(self): args = [ "gcc", @@ -81,7 +97,11 @@ def do_run(self): print(ret.stderr.decode('utf-8')) self.assertEqual(ret.returncode, 0) - def test_me(self): + def do_test(self): self.do_export() self.do_compile() self.do_run() + + +ALL_CSTDS = set(CStandard) +ALL_CSTDS.remove(CStandard.gnu23) # Not available yet diff --git a/tests/pylint.rc b/tests/pylint.rc index 6fcafcf..b3eb902 100644 --- a/tests/pylint.rc +++ b/tests/pylint.rc @@ -20,7 +20,7 @@ fail-on= fail-under=10.0 # Files or directories to be skipped. They should be base names, not paths. -ignore=CVS +ignore=CVS, parser, docs, test # Add files or directories matching the regex patterns to the ignore-list. The # regex matches against paths. @@ -50,10 +50,6 @@ load-plugins= # Pickle collected data for later comparisons. persistent=yes -# Min Python version to use for version dependend checks. Will default to the -# version used to run pylint. -py-version=3.9 - # When enabled, pylint would attempt to guess common misconfiguration and emit # user-friendly hints instead of false-positive error messages. suggestion-mode=yes @@ -78,15 +74,32 @@ confidence= # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". -disable=raw-checker-failed, - bad-inline-option, - locally-disabled, - file-ignored, - suppressed-message, - useless-suppression, - deprecated-pragma, - use-symbolic-message-instead, - fixme +disable= + # Disable for now during development + fixme, + + # User ignored limits + too-many-lines, + too-many-locals, + too-many-branches, + too-many-return-statements, + too-few-public-methods, + too-many-public-methods, + too-many-statements, + too-many-instance-attributes, + too-many-function-args, + line-too-long, + + # Noise / Don't care + no-else-return, + unused-variable, + invalid-name, + missing-docstring, + abstract-method, + protected-access, + duplicate-code, + unused-argument, + consider-using-f-string # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option @@ -117,7 +130,7 @@ output-format=text reports=no # Activate the evaluation score. -score=yes +score=no [REFACTORING] @@ -132,6 +145,196 @@ max-nested-blocks=5 never-returning-functions=sys.exit,argparse.parse_error +[STRING] + +# This flag controls whether inconsistent-quotes generates a warning when the +# character used as a quote delimiter is used inconsistently within a module. +check-quote-consistency=no + +# This flag controls whether the implicit-str-concat should generate a warning +# on implicit string concatenation in sequences defined over several lines. +check-str-concat-over-line-jumps=no + + +[SPELLING] + +# Limits count of emitted suggestions for spelling mistakes. +max-spelling-suggestions=4 + +# Spelling dictionary name. Available dictionaries: en_GB (aspell), en_AU +# (aspell), en_US (hunspell), en (aspell), en_CA (aspell). +spelling-dict= + +# List of comma separated words that should be considered directives if they +# appear and the beginning of a comment and should not be checked. +spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains the private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to the private dictionary (see the +# --spelling-private-dict-file option) instead of raising a message. +spelling-store-unknown-words=no + + +[LOGGING] + +# The type of string formatting that logging methods do. `old` means using % +# formatting, `new` is for `{}` formatting. +logging-format-style=old + +# Logging modules to check that the string format arguments are in logging +# function parameter format. +logging-modules=logging + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid defining new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of names allowed to shadow builtins +allowed-redefined-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_, + _cb + +# A regular expression matching the name of dummy variables (i.e. expected to +# not be used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore. +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io + + +[SIMILARITIES] + +# Comments are removed from the similarity computation +ignore-comments=yes + +# Docstrings are removed from the similarity computation +ignore-docstrings=yes + +# Imports are removed from the similarity computation +ignore-imports=no + +# Signatures are removed from the similarity computation +ignore-signatures=no + +# Minimum lines number of a similarity. +min-similarity-lines=10 + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# Tells whether to warn about missing members when the owner of the attribute +# is inferred to be None. +ignore-none=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis). It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + +# List of decorators that change the signature of a decorated function. +signature-mutators= + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=110 + +# Maximum number of lines in a module. +max-module-lines=2000 + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME, + XXX, + TODO + + [BASIC] # Naming style matching correct argument names. @@ -256,199 +459,6 @@ variable-naming-style=snake_case #variable-rgx= -[FORMAT] - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Maximum number of characters on a single line. -max-line-length=100 - -# Maximum number of lines in a module. -max-module-lines=1000 - -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - - -[LOGGING] - -# The type of string formatting that logging methods do. `old` means using % -# formatting, `new` is for `{}` formatting. -logging-format-style=old - -# Logging modules to check that the string format arguments are in logging -# function parameter format. -logging-modules=logging - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME, - XXX, - TODO - -# Regular expression of note tags to take in consideration. -#notes-rgx= - - -[SIMILARITIES] - -# Comments are removed from the similarity computation -ignore-comments=yes - -# Docstrings are removed from the similarity computation -ignore-docstrings=yes - -# Imports are removed from the similarity computation -ignore-imports=no - -# Signatures are removed from the similarity computation -ignore-signatures=no - -# Minimum lines number of a similarity. -min-similarity-lines=4 - - -[SPELLING] - -# Limits count of emitted suggestions for spelling mistakes. -max-spelling-suggestions=4 - -# Spelling dictionary name. Available dictionaries: none. To make it work, -# install the 'python-enchant' package. -spelling-dict= - -# List of comma separated words that should be considered directives if they -# appear and the beginning of a comment and should not be checked. -spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains the private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to the private dictionary (see the -# --spelling-private-dict-file option) instead of raising a message. -spelling-store-unknown-words=no - - -[STRING] - -# This flag controls whether inconsistent-quotes generates a warning when the -# character used as a quote delimiter is used inconsistently within a module. -check-quote-consistency=no - -# This flag controls whether the implicit-str-concat should generate a warning -# on implicit string concatenation in sequences defined over several lines. -check-str-concat-over-line-jumps=no - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# Tells whether to warn about missing members when the owner of the attribute -# is inferred to be None. -ignore-none=yes - -# This flag controls whether pylint should warn about no-member and similar -# checks whenever an opaque object is returned when inferring. The inference -# can return multiple potential results while evaluating a Python object, but -# some branches might not be evaluated, which results in partial inference. In -# that case, it might be useful to still emit no-member and other checks for -# the rest of the inferred objects. -ignore-on-opaque-inference=yes - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis). It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= - -# Show a hint with possible names when a member name was not found. The aspect -# of finding the hint is based on edit distance. -missing-member-hint=yes - -# The minimum edit distance a name should have in order to be considered a -# similar match for a missing member name. -missing-member-hint-distance=1 - -# The total number of similar names that should be taken in consideration when -# showing a hint for a missing member. -missing-member-max-choices=1 - -# List of decorators that change the signature of a decorated function. -signature-mutators= - - -[VARIABLES] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid defining new builtins when possible. -additional-builtins= - -# Tells whether unused global variables should be treated as a violation. -allow-global-unused-variables=yes - -# List of names allowed to shadow builtins -allowed-redefined-builtins= - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_, - _cb - -# A regular expression matching the name of dummy variables (i.e. expected to -# not be used). -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore. -ignored-argument-names=_.*|^ignored_|^unused_ - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io - - [CLASSES] # Warn about protected attribute access inside special methods @@ -477,12 +487,12 @@ valid-metaclass-classmethod-first-arg=cls [DESIGN] -# List of qualified class names to ignore when counting class parents (see +# List of qualified class names to ignore when countint class parents (see # R0901) ignored-parents= # Maximum number of arguments for function / method. -max-args=5 +max-args=16 # Maximum number of attributes for a class (see R0902). max-attributes=7 @@ -557,5 +567,5 @@ preferred-modules= # Exceptions that will emit a warning when being caught. Defaults to # "BaseException, Exception". -overgeneral-exceptions=builtins.BaseException, - builtins.Exception +overgeneral-exceptions=builtin.BaseException, + builtin.Exception diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 0000000..2414cc7 --- /dev/null +++ b/tests/requirements.txt @@ -0,0 +1,7 @@ +pytest +pytest-xdist +parameterized +pylint +mypy +pytest-cov +coveralls>=3.0.0 diff --git a/tests/run.sh b/tests/run.sh new file mode 100755 index 0000000..326ca96 --- /dev/null +++ b/tests/run.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +set -e + +cd "$(dirname "$0")" + +# Initialize venv +python3 -m venv .venv +source .venv/bin/activate + +# Install test dependencies +pip install -U pip setuptools wheel +pip install -r requirements.txt + +# Install dut +pip install -U ../ + +# Run unit tests +export SKIP_SYNTH_TESTS=1 +#export STUB_SIMULATOR=1 +export NO_XSIM=1 +pytest -n auto --cov=peakrdl_cheader + +# Generate coverage report +coverage html -i -d htmlcov + +# Run lint +pylint --rcfile pylint.rc ../src/peakrdl_cheader + +# Run static type checking +#mypy ../src/peakrdl_cheader diff --git a/tests/test_all.py b/tests/test_all.py index b1a0bf7..56d79b6 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -1,17 +1,23 @@ +import glob + import base from parameterized import parameterized_class -from peakrdl_cheader.c_standards import CStandard -cstds = set(CStandard) -cstds.remove(CStandard.gnu23) # Not available yet +exceptions = [ + "testcases/wide_regs.rdl", +] +files = glob.glob("testcases/*.rdl") +files = [file for file in files if not file in exceptions] @parameterized_class(base.get_permutations({ - "rdl_file": ["testcases/basic.rdl"], - "std": cstds, + "rdl_file": files, + "std": base.ALL_CSTDS, "generate_bitfields": [True, False], "reuse_typedefs": [True, False], "explode_top": [True, False], + "instantiate": [True, False], })) class TestAll(base.BaseHeaderTestcase): - pass + def test_all(self) -> None: + self.do_test() diff --git a/tests/test_htol.py b/tests/test_htol.py new file mode 100644 index 0000000..ca622f9 --- /dev/null +++ b/tests/test_htol.py @@ -0,0 +1,11 @@ +import base + +class TestHTOL(base.BaseHeaderTestcase): + rdl_file = "testcases/widths_and_mem.rdl" + generate_bitfields = True + explode_top = False + bitfield_order_ltoh = False + def test_htol(self) -> None: + # Can't actually run test because gcc on this arch is ltoh + self.do_export() + self.do_compile() diff --git a/tests/test_wide_regs.py b/tests/test_wide_regs.py new file mode 100644 index 0000000..709e405 --- /dev/null +++ b/tests/test_wide_regs.py @@ -0,0 +1,15 @@ +import base + +from parameterized import parameterized_class + +@parameterized_class(base.get_permutations({ + "std": base.ALL_CSTDS, + "reuse_typedefs": [True, False], + "instantiate": [True, False], +})) +class TestWideRegs(base.BaseHeaderTestcase): + rdl_file = "testcases/wide_regs.rdl" + generate_bitfields = False # Do not support bitfields for wide regs yet + explode_top = False + def test_wide_regs(self) -> None: + self.do_test() diff --git a/tests/testcases/basic.rdl b/tests/testcases/basic.rdl index d23016e..38e3fa6 100644 --- a/tests/testcases/basic.rdl +++ b/tests/testcases/basic.rdl @@ -26,6 +26,7 @@ addrmap basic { default hw = r; field { fieldwidth=1; } basicfield_f[0:0]; field { } basicfield_g[2:1]; + field { } case[5:4]; // collide with C keyword } basicreg_d; @@ -54,4 +55,4 @@ addrmap basic { field { fieldwidth=8; sw = r; } basicfield_r; } basicreg_g; -}; \ No newline at end of file +}; diff --git a/tests/testcases/global_type_names.rdl b/tests/testcases/global_type_names.rdl new file mode 100644 index 0000000..39d5ab2 --- /dev/null +++ b/tests/testcases/global_type_names.rdl @@ -0,0 +1,29 @@ +reg r_global { + field {} f1[4]; + + field myfield {}; + myfield f2; + myfield f3[4]; + field myfield2 {fieldwidth = 4;}; + myfield2 f4; +}; + +regfile rf_global #( + longint unsigned NUM = 4 +){ + reg r_local { + field {} f_param[NUM]; + field myfield {}; + myfield f_param2[NUM]; + } r1; + r_global r2; + + signal {} xyz; +}; + +addrmap top { + rf_global rf1; + rf_global #(.NUM (8)) rf2; + rf_global rf3[4] @ 0x1000 += 0x100; + rf_global rf4[4] @ 0x2000 += 0x200; +}; diff --git a/tests/testcases/overlapping.rdl b/tests/testcases/overlapping.rdl new file mode 100644 index 0000000..1749340 --- /dev/null +++ b/tests/testcases/overlapping.rdl @@ -0,0 +1,24 @@ +addrmap top { + reg { + field f_rw {sw=rw; hw=r;}; + field f_r {sw=r; hw=w;}; + field f_w {sw=w; hw=r;}; + + f_rw f1[0:0] = 0; + f_r f2[1:1]; + f_w f3[1:1]; + } overlap_fields; + + reg r_rw { + field {sw=rw; hw=r;} f[8]; + }; + reg r_r { + field {sw=r; hw=w;} f[8]; + }; + reg r_w { + field {sw=w; hw=r;} f[8]; + }; + r_rw r1 @ 0x10; + r_r r2 @ 0x14; + r_w r3 @ 0x14; +}; diff --git a/tests/testcases/wide_regs.rdl b/tests/testcases/wide_regs.rdl new file mode 100644 index 0000000..ee50b66 --- /dev/null +++ b/tests/testcases/wide_regs.rdl @@ -0,0 +1,23 @@ +mem mem_empty #( + longint WIDTH = 32 +){ + memwidth = WIDTH; + mementries = 16; +}; + + +addrmap top { + reg wide_reg { + regwidth = 128; + field {} f1[32]; + field {} f2[32]; + field {} f3[32]; + field {} f4[32]; + }; + + wide_reg r1; + wide_reg r2[4]; + wide_reg r3; + + external mem_empty #(.WIDTH(128)) mem_empty_128; +}; diff --git a/tests/testcases/widths_and_mem.rdl b/tests/testcases/widths_and_mem.rdl new file mode 100644 index 0000000..4a9879c --- /dev/null +++ b/tests/testcases/widths_and_mem.rdl @@ -0,0 +1,77 @@ +regfile rf1 #( + longint WIDTH = 32 +){ + default regwidth = WIDTH; + reg myreg1 { + field {} f1[WIDTH/4]; + field {} f2[WIDTH/4]; + field {} f3[WIDTH/4]; + field {} f4[WIDTH/4]; + }; + reg myreg2 { + field {} f1[WIDTH/8]; + field {} f2[WIDTH/8]; + field {} f3[WIDTH/8]; + field {} f4[WIDTH/8]; + }; + reg myreg3 { + field {} f1[1:1]; + field {} f2[4:3] = 3; + field {} f3[6:5] = 2; + }; + + myreg1 r1 @ 0x100; + myreg2 r2[3]; + myreg3 r3[5]; + + myreg1 r4 @ 0x200; + myreg2 r5; + myreg3 r6; +}; + + +mem mem_empty #( + longint WIDTH = 32 +){ + memwidth = WIDTH; + mementries = 16; +}; + +mem mem_vregs #( + longint WIDTH = 32 +){ + memwidth = WIDTH; + mementries = 16; + reg myreg { + regwidth = WIDTH; + field {} f1[WIDTH/2]; + field {} f2[WIDTH/4]; + field {} f3[WIDTH/8]; + field {} f4[WIDTH/8]; + }; + myreg r1[8]; + myreg r2; + myreg r3[6]; + myreg r4; +}; + + +addrmap top { + rf1 #(.WIDTH(8)) rf1_8; + rf1 #(.WIDTH(16)) rf1_16; + rf1 #(.WIDTH(32)) rf1_32; + rf1 #(.WIDTH(64)) rf1_64; + rf1 #(.WIDTH(8)) rf1_8_again; + + external mem_empty #(.WIDTH(8)) mem_empty_8; + external mem_empty #(.WIDTH(16)) mem_empty_16; + external mem_empty #(.WIDTH(32)) mem_empty_32; + external mem_empty #(.WIDTH(64)) mem_empty_64; + external mem_empty #(.WIDTH(8)) mem_empty_8_again; + + external mem_vregs #(.WIDTH(8)) mem_vregs_8; + external mem_vregs #(.WIDTH(16)) mem_vregs_16; + external mem_vregs #(.WIDTH(32)) mem_vregs_32; + external mem_vregs #(.WIDTH(64)) mem_vregs_64; + external mem_vregs #(.WIDTH(8)) mem_vregs_8_again; +};