diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b8dc74d..d541818 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + name: Publish Python Package on: @@ -15,7 +22,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: # just fetching 1 commit is not enough for setuptools-scm, so we fetch all fetch-depth: 0 diff --git a/.github/workflows/run_tests.yaml b/.github/workflows/run_tests.yaml index 7d78dbe..72f6917 100644 --- a/.github/workflows/run_tests.yaml +++ b/.github/workflows/run_tests.yaml @@ -1,12 +1,19 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + name: CI on: [push, pull_request] jobs: tests: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 1 @@ -34,26 +41,38 @@ jobs: git describe --always --tags make submodules make - export PATH=$PATH:$PWD - echo "::set-output name=bin_dir::$PWD" + export OUTDIR=$PWD/build-standard + export PATH=$PATH:$OUTDIR + echo "bin_dir=$OUTDIR" >> $GITHUB_OUTPUT test $(micropython -c 'print("test")') = "test" popd - - name: Build binutils-esp32ulp - id: build_binutils + - name: Fetch binutils-esp32ulp + id: fetch_binutils run: | - echo "Building binutils-esp32ulp" - git clone --depth 1 https://github.com/espressif/binutils-esp32ulp.git - pushd binutils-esp32ulp - git describe --always --tags - ./configure --target=esp32ulp-elf --prefix=$PWD/dist --disable-doc --disable-gdb --disable-libdecnumber --disable-readline --disable-sim - echo "MAKEINFO = :" >> Makefile - make - make install-strip - export PATH=$PATH:$PWD/dist/bin - echo "::set-output name=bin_dir::$PWD/dist/bin" + echo "Fetching URL of pre-built esp32ulp-elf binaries" + ## URL to pre-built binaries is published in esp-idf + IDFVER=v5.0.1 + curl -s \ + -o tools.json \ + https://raw.githubusercontent.com/espressif/esp-idf/$IDFVER/tools/tools.json + URL=$(> $GITHUB_OUTPUT esp32ulp-elf-as --version | grep 'esp32ulp-elf' > /dev/null - popd ###### Run tests ###### @@ -68,7 +87,7 @@ jobs: id: compat_tests run: | export PATH=$PATH:${{ steps.build_micropython.outputs.bin_dir }} - export PATH=$PATH:${{ steps.build_binutils.outputs.bin_dir }} + export PATH=$PATH:${{ steps.fetch_binutils.outputs.bin_dir }} cd tests ./01_compat_tests.sh @@ -76,7 +95,13 @@ jobs: id: compat_rtc_tests run: | export PATH=$PATH:${{ steps.build_micropython.outputs.bin_dir }} - export PATH=$PATH:${{ steps.build_binutils.outputs.bin_dir }} + export PATH=$PATH:${{ steps.fetch_binutils.outputs.bin_dir }} cd tests - ln -s ../binutils-esp32ulp # already cloned earlier. reuse. ./02_compat_rtc_tests.sh + + - name: Run disassembler tests + id: disassembler_tests + run: | + export PATH=$PATH:${{ steps.build_micropython.outputs.bin_dir }} + cd tests + ./03_disassembler_tests.sh diff --git a/.gitignore b/.gitignore index ac78360..0b7309c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,16 @@ -tests/compat/*.bin -tests/compat/*.elf -tests/compat/*.o -tests/compat/*.ulp -tests/compat/*.log +tests/binutils-gdb +tests/esp-idf +tests/ulptool +tests/**/*.bin +tests/**/*.elf +tests/**/*.o +tests/**/*.ulp +tests/**/*.log +tests/**/*.pre +tests/log +tests/*.lst +tests/*.log +tests/defines*.db demo.ulp *.pyc *.pyo diff --git a/LICENSE b/LICENSE index d3c5838..7fa80dd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright 2018-2022 by the micropython-esp32-ulp authors, see AUTHORS file +Copyright 2018-2023 by the micropython-esp32-ulp authors, see AUTHORS file Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.rst b/README.rst index f923c27..6b650fa 100644 --- a/README.rst +++ b/README.rst @@ -15,12 +15,13 @@ micropython-esp32-ulp is an assembler toolchain for the ESP32 ULP (Ultra Low-Pow Co-Processor, written in MicroPython. It can translate small assembly language programs to a loadable/executable -ULP machine code binary, directly on the ESP32 microcontroller. +ULP-FSM (not RISC-V) machine code binary, directly on a ESP32 microcontroller. This is intended as an alternative approach to assembling such programs using -the binutils-esp32ulp toolchain from Espressif on a development machine. +the `binutils-gdb toolchain `_ +(esp32-elf-as) from Espressif on a development machine. -It can also be useful in cases where binutils-esp32ulp is not available. +It can also be useful in cases where esp32-elf-as is not available. Features @@ -28,12 +29,30 @@ Features The following features are supported: -* the entire `ESP32 ULP instruction set `_ +* the entire `ESP32 ULP instruction set `_ +* the entire `ESP32-S2 ULP instruction set `_ + (this also covers the ESP32-S3) [#f1]_ [#f2]_ * constants defined with ``.set`` * constants defined with ``#define`` * expressions in assembly code and constant definitions * RTC convenience macros (e.g. ``WRITE_RTC_REG``) * many ESP32 ULP code examples found on the web will work unmodified +* a simple disassembler is also provided + +.. [#f1] Note: the ESP32-S2 and ESP32-S3 have the same ULP binary format between each other + but the binary format is different than that of the original ESP32 ULP. You need to + select the ``esp32s2`` cpu (`see docs `_) when assembling code for + use on an ESP32-S2/S3. + +.. [#f2] Note: The ESP32-S2 and ESP32-S3 have the same ULP binary format, but the peripheral + register addresses (those accessed with REG_RD and REG_WR) are different. For best + results, use the correct peripheral register addresses for the specific variant you + are working with. The assembler (when used with ``cpu=esp32s2``) will accept + addresses for any of the 3 variants, because they are translated into relative + offsets anyway and many registers live at the same relative offset on all 3 variants. + This conveniently means that the same assembly code can assembled unmodified for each + variant and produce a correctly working binary - as long as only peripheral registers + are used, which have the same relative offset across the variants. Use with care! Quick start @@ -43,8 +62,13 @@ To get going run the following directly on the ESP32: .. code-block:: python - # Step 1: Install micropython-esp32-ulp # IMPORTANT: Ensure the ESP32 is connected to a network with internet connectivity. + + # Step 1: Install micropython-esp32-ulp (for MicroPython v1.20 or newer) + import mip + mip.install('github:micropython/micropython-esp32-ulp') + + # Step 1: Install micropython-esp32-ulp (for MicroPython older than v1.20) import upip upip.install('micropython-esp32-ulp') @@ -64,10 +88,12 @@ See `docs/index.rst `_. Requirements ------------ -The minimum supported version of MicroPython is v1.12. +The minimum supported version of MicroPython is v1.12. (For ESP32-S2 and S3 +devices, a version greater than v1.20 is required as versions before that +did not enable the ``esp32.ULP`` class). -An ESP32 is required to run the ULP machine code binary produced by micropython-esp32-ulp -(the ESP32-S2 will not work as it is not binary compatible with the ESP32). +An ESP32 device is required to run the ULP machine code binary produced by +micropython-esp32-ulp. License diff --git a/demo.S b/demo.S index e2feac5..0522924 100644 --- a/demo.S +++ b/demo.S @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + # comment-only line .text diff --git a/demo.sh b/demo.sh index 4694862..6dea9b6 100644 --- a/demo.sh +++ b/demo.sh @@ -1 +1,8 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + micropython -m esp32_ulp demo.S diff --git a/docs/disassembler.rst b/docs/disassembler.rst new file mode 100644 index 0000000..ee733e9 --- /dev/null +++ b/docs/disassembler.rst @@ -0,0 +1,163 @@ +===================== +Disassembler +===================== + +micropython-esp32-ulp contains a disassembler for disassembling code for the +ESP32 ULP (Ultra Low-Power) Co-Processor. + +The main purpose of this tool is to inspect what instructions our assembler +created, what value each field is set to, and to compare this with the output +created by the assembler from Espressif (part of their `binutils-gdb fork `_), +which we use as our reference implementation. + + +Usage +------------------------ + +To disassemble a ULP binary, simply run: + +.. code-block:: bash + + micropython -m tools.disassemble path/to/binary.ulp + +You can also specify additional options to ``disassemble.py`` as follows: + ++--------------------------+----------------------------------------------------------------+ +| Option | Description | ++==========================+================================================================+ +| ``-c`` or ``--mcpu`` | Choose ULP variant: either esp32 or esp32s2 | ++--------------------------+----------------------------------------------------------------+ +| ``-h`` | Show help text | ++--------------------------+----------------------------------------------------------------+ +|| ``-m `` || Disassemble a provided sequence of hex bytes | +|| || (in this case any filename specified is ignored) | ++--------------------------+----------------------------------------------------------------+ +| ``-v`` | Verbose mode (shows ULP header and fields of each instruction) | ++--------------------------+----------------------------------------------------------------+ + + +Disassembling a file +------------------------ + +The simplest and default mode of the disassembler is to disassemble the +specified file. + +Note that the ULP header is validates and files with unknown magic bytes will be +rejected. The correct 4 magic bytes at the start of a ULP binary are ``ulp\x00``. + +Example disassembling an ESP32 ULP binary: + +.. code-block:: shell + + $ micropython -m tools.disassemble path/to/binary.ulp + .text + 0000 040000d0 LD r0, r1, 0 + 0004 0e0000d0 LD r2, r3, 0 + 0008 04000068 ST r0, r1, 0 + 000c 0b000068 ST r3, r2, 0 + .data + 0010 00000000 + +Example disassembling an ESP32-S2 ULP binary: + +.. code-block:: shell + + $ micropython -m tools.disassemble -c esp32s2 path/to/binary.ulp + .text + 0000 040000d0 LD r0, r1, 0 + 0004 0e0000d0 LD r2, r3, 0 + 0008 84010068 ST r0, r1, 0 + 000c 8b010068 ST r3, r2, 0 + .data + 0010 00000000 + + +Disassembling a byte sequence +----------------------------- + +The ``-m`` option allows disassembling a sequences hex letters representing +ULP instructions. + +This option expects the actual instructions directly, without any ULP header. + +The sequence must contain a number of hex letters exactly divisible by 8, i.e. +8, 16, 24, etc, because each 32-bit word is made up of 8 hex letters. Spaces +can be included in the sequence and they are ignored. + +The typical use case for this feature is to copy/paste some instructions from +a hexdump (e.g. xxd output) for analysis. + +Example: + +.. code-block:: shell + + # hexdump binary.ulp + $ xxd path/to/binary.ulp + 00000000: 756c 7000 0c00 2400 0400 0000 9300 8074 ulp...$........t + 00000010: 2a80 0488 2004 8074 1c00 0084 0000 0040 *... ..t.......@ + (...) + + # analyse the last 2 instructions + $ micropython -m tools.disassemble -m "1c00 0084 0000 0040" + 0000 1c000084 JUMPS 0, 28, LT + 0004 00000040 NOP + + +Verbose mode +------------------------ + +In verbose mode the following extra outputs are enabled: + +* ULP header (except when using ``-m``) +* The fields of each instruction and their values + +For example: + +.. code-block:: + + header + ULP magic : b'ulp\x00' (0x00706c75) + .text offset : 12 (0x0c) + .text size : 36 (0x24) + .data offset : 48 (0x30) + .data size : 4 (0x04) + .bss size : 0 (0x00) + ---------------------------------------- + .text + 0000 93008072 MOVE r3, 9 + dreg = 3 + imm = 9 + opcode = 7 + sel = 4 (MOV) + sreg = 0 + sub_opcode = 1 + unused = 0 + (...detail truncated...) + 0020 000000b0 HALT + opcode = 11 (0x0b) + unused = 0 + ---------------------------------------- + .data + 0000 00000000 + + +Disassembling on device +----------------------------- + +The disassembler also works when used on an ESP32 device. + +To use the disassembler on a real device: + +* ensure ``micropython-esp32-ulp`` is installed on the device (see `docs/index.rst `_). +* upload ``tools/disassemble.py`` ``tools/decode.py`` and ``tools/decode_s2.py`` to the device + (any directory will do, as long as those 3 files are in the same directory) +* the following example code assumes you placed the 3 files into the device's "root" directory +* run the following (note, we must specify which the cpu the binary is for): + + .. code-block:: python + + from disassemble import disassemble_file + # then either: + disassemble_file('path/to/file.ulp', cpu='esp32s2') # normal mode + # or: + disassemble_file('path/to/file.ulp', cpu='esp32s2', verbose=True) # verbose mode diff --git a/docs/index.rst b/docs/index.rst index 401bdc5..77906dc 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,11 +13,17 @@ Overview Installation ------------ -On the ESP32, install using upip: +On the ESP32, install using mip (or upip on older MicroPythons): .. code-block:: python - # ensure the ESP32 is connected to a network with internet connectivity + # step 1: ensure the ESP32 is connected to a network with internet connectivity + + # step 2 (for MicroPython 1.20 or newer) + import mip + mip.install('github:micropython/micropython-esp32-ulp') + + # step 2 (for MicroPython older than 1.20) import upip upip.install('micropython-esp32-ulp') @@ -58,6 +64,13 @@ follows: cd micropython-esp32-ulp micropython -m esp32_ulp path/to/code.S # this results in path/to/code.ulp +The assembler supports selecting a CPU to assemble for using the ``-c`` option +(valid cpu's are ``esp32`` and ``esp32s2``): + +.. code-block:: shell + + micropython -m esp32_ulp -c esp32s2 path/to/code.S # assemble for an ESP32-S2 + More examples +++++++++++++ @@ -86,12 +99,13 @@ assembly source file into a machine code binary file with a ``.ulp`` extension. That file can then be loaded directly without assembling the source again. 1. Create/upload an assembly source file and run the following to get a - loadable ULP binary as a ``.ulp`` file: + loadable ULP binary as a ``.ulp`` file (specify ``cpu='esp32s2'`` if you + have an ESP32-S2 or ESP32-S3 device): .. code-block:: python import esp32_ulp - esp32_ulp.assemble_file('code.S') # this results in code.ulp + esp32_ulp.assemble_file('code.S', cpu='esp32') # this results in code.ulp 2. The above prints out the offsets of all global symbols/labels. For the next step, you will need to note down the offset of the label, which represents @@ -104,7 +118,7 @@ That file can then be loaded directly without assembling the source again. from esp32 import ULP ulp = ULP() - with open('test.ulp', 'r') as f: + with open('test.ulp', 'rb') as f: # load the binary into RTC memory ulp.load_binary(0, f.read()) @@ -136,6 +150,15 @@ found as part of Arduino/ESP-IDF projects. The preprocessor and how to use it is documented here: `Preprocessor support `_. +Disassembler +------------ +There is a disassembler for disassembling ULP binary code. This is mainly used to +inspect what instructions our assembler created, however it can be used to analyse +any ULP binaries. + +The disassembler and how to use it is documented here: `Disassembler `_. + + Limitations ----------- @@ -144,17 +167,18 @@ Currently the following are not supported: * assembler macros using ``.macro`` * preprocessor macros using ``#define A(x,y) ...`` * including files using ``#include`` -* ESP32-S2 (not binary compatible with the ESP32) Testing ------- There are unit tests and also compatibility tests that check whether the binary -output is identical with what binutils-esp32ulp produces. +output is identical with what Espressif's esp32-elf-as (from their `binutils-gdb fork +`_) produces. micropython-esp32-ulp has been tested on the Unix port of MicroPython and on real ESP32 -devices with the chip type ESP32D0WDQ6 (revision 1) without SPIRAM. +devices with the chip type ESP32D0WDQ6 (revision 1) without SPIRAM as well as ESP32-S2 +(ESP32-S2FH4) and ESP32-S3 (ESP32-S3R8) devices. Consult the Github Actions `workflow definition file `_ for how to run the different tests. diff --git a/docs/preprocess.rst b/docs/preprocess.rst index 4aa3c7b..45569a5 100644 --- a/docs/preprocess.rst +++ b/docs/preprocess.rst @@ -95,6 +95,21 @@ are not needed on the device either.) micropython -m esp32_ulp.parse_to_db \ esp-idf/components/soc/esp32/include/soc/{soc,soc_ulp,rtc_cntl_reg,rtc_io_reg,sens_reg}.h + + .. warning:: + + `:warning:` Ensure that you include the header files for the correct + variant you are working with. In the example code above, simply switch + ``esp32`` to ``esp32s2`` or ``esp32s3`` in the path to the include files. + + There are subtle differences across the ESP32 variants such as which + constants are available or the value of certain constants. For example, + peripheral register addresses differ between the 3 variants even though + many constants for peripheral registers are available on all 3 variants. + Other constants such as those relating to the HOLD functionality of touch + pads are only available on the original ESP32. + + 2. Using the defines database during preprocessing The preprocessor will automatically use a defines database, when using the @@ -108,6 +123,7 @@ are not needed on the device either.) or instantiate the ``Preprocessor`` class directly, without passing it a DefinesDB instance via ``use_db``. + Design choices -------------- diff --git a/esp32_ulp/__init__.py b/esp32_ulp/__init__.py index dddafc8..e14cca3 100644 --- a/esp32_ulp/__init__.py +++ b/esp32_ulp/__init__.py @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + from .util import garbage_collect from .preprocess import preprocess @@ -6,24 +13,28 @@ garbage_collect('after import') -def src_to_binary(src): - assembler = Assembler() +def src_to_binary_ext(src, cpu): + assembler = Assembler(cpu) src = preprocess(src) assembler.assemble(src, remove_comments=False) # comments already removed by preprocessor garbage_collect('before symbols export') addrs_syms = assembler.symbols.export() + text, data, bss_len = assembler.fetch() + return make_binary(text, data, bss_len), addrs_syms + + +def src_to_binary(src, cpu): + binary, addrs_syms = src_to_binary_ext(src, cpu) for addr, sym in addrs_syms: print('%04d %s' % (addr, sym)) - - text, data, bss_len = assembler.fetch() - return make_binary(text, data, bss_len) + return binary -def assemble_file(filename): +def assemble_file(filename, cpu): with open(filename) as f: src = f.read() - binary = src_to_binary(src) + binary = src_to_binary(src, cpu) if filename.endswith('.s') or filename.endswith('.S'): filename = filename[:-2] diff --git a/esp32_ulp/__main__.py b/esp32_ulp/__main__.py index 6f69bea..fc14320 100644 --- a/esp32_ulp/__main__.py +++ b/esp32_ulp/__main__.py @@ -1,11 +1,26 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + import sys from . import assemble_file -def main(fn): - assemble_file(fn) +def main(fn, cpu): + assemble_file(fn, cpu) if __name__ == '__main__': - main(sys.argv[1]) + cpu = 'esp32' + filename = sys.argv[1] + if len(sys.argv) > 3: + if sys.argv[1] in ('-c', '--mcpu'): + cpu = sys.argv[2].lower() + if cpu not in ('esp32', 'esp32s2'): + raise ValueError('Invalid cpu') + filename = sys.argv[3] + main(filename, cpu) diff --git a/esp32_ulp/assemble.py b/esp32_ulp/assemble.py index 0ec11ec..8b79071 100644 --- a/esp32_ulp/assemble.py +++ b/esp32_ulp/assemble.py @@ -1,9 +1,15 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + """ ESP32 ULP Co-Processor Assembler """ import re -from . import opcodes from .nocomment import remove_comments as do_remove_comments from .util import garbage_collect @@ -88,9 +94,19 @@ def set_global(self, symbol): class Assembler: - def __init__(self, symbols=None, bases=None, globals=None): + def __init__(self, cpu='esp32', symbols=None, bases=None, globals=None): + if cpu == 'esp32': + opcode_module = 'opcodes' + elif cpu == 'esp32s2': + opcode_module = 'opcodes_s2' + else: + raise ValueError('Invalid cpu') + + relative_import = 1 if '/' in __file__ else 0 + self.opcodes = __import__(opcode_module, None, None, [], relative_import) + self.symbols = SymbolTable(symbols or {}, bases or {}, globals or {}) - opcodes.symbols = self.symbols # XXX dirty hack + self.opcodes.symbols = self.symbols # XXX dirty hack # regex for parsing assembly lines # format: [[whitespace]label:][whitespace][opcode[whitespace arg[,arg...]]] @@ -223,7 +239,7 @@ def d_align(self, align=4, fill=None): self.fill(self.section, amount, fill) def d_set(self, symbol, expr): - value = int(opcodes.eval_arg(expr)) + value = int(self.opcodes.eval_arg(expr)) self.symbols.set_sym(symbol, ABS, None, value) def d_global(self, symbol): @@ -264,13 +280,13 @@ def assembler_pass(self, lines): else: # machine instruction opcode_lower = opcode.lower() - func = getattr(opcodes, 'i_' + opcode_lower, None) + func = getattr(self.opcodes, 'i_' + opcode_lower, None) if func is not None: if self.a_pass == 1: # during the first pass, symbols are not all known yet. # so we add empty instructions to the section, to determine # section sizes and symbol offsets for pass 2. - result = (0,) * opcodes.no_of_instr(opcode_lower, args) + result = (0,) * self.opcodes.no_of_instr(opcode_lower, args) else: result = func(*args) diff --git a/esp32_ulp/definesdb.py b/esp32_ulp/definesdb.py index 4a05459..6c49256 100644 --- a/esp32_ulp/definesdb.py +++ b/esp32_ulp/definesdb.py @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + import os import btree from .util import file_exists diff --git a/esp32_ulp/link.py b/esp32_ulp/link.py index fd9332b..f0d81df 100644 --- a/esp32_ulp/link.py +++ b/esp32_ulp/link.py @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + from uctypes import struct, addressof, LITTLE_ENDIAN, UINT16, UINT32 diff --git a/esp32_ulp/nocomment.py b/esp32_ulp/nocomment.py index 2ac9e8d..5845b34 100644 --- a/esp32_ulp/nocomment.py +++ b/esp32_ulp/nocomment.py @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + def remove_comments(s): """ Remove comments of these styles: diff --git a/esp32_ulp/opcodes.py b/esp32_ulp/opcodes.py index 6910081..10fc3d1 100644 --- a/esp32_ulp/opcodes.py +++ b/esp32_ulp/opcodes.py @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + """ ESP32 ULP Co-Processor Instructions """ @@ -379,7 +386,7 @@ def i_reg_wr(reg, high_bit, low_bit, val): _wr_reg.addr = reg & 0xff _wr_reg.periph_sel = (reg & 0x300) >> 8 else: - _wr_reg.addr = (reg & 0xff) >> 2 + _wr_reg.addr = (reg >> 2) & 0xff _wr_reg.periph_sel = _soc_reg_to_ulp_periph_sel(reg) _wr_reg.data = get_imm(val) _wr_reg.low = get_imm(low_bit) @@ -394,7 +401,7 @@ def i_reg_rd(reg, high_bit, low_bit): _rd_reg.addr = reg & 0xff _rd_reg.periph_sel = (reg & 0x300) >> 8 else: - _rd_reg.addr = (reg & 0xff) >> 2 + _rd_reg.addr = (reg >> 2) & 0xff _rd_reg.periph_sel = _soc_reg_to_ulp_periph_sel(reg) _rd_reg.unused = 0 _rd_reg.low = get_imm(low_bit) diff --git a/esp32_ulp/opcodes_s2.py b/esp32_ulp/opcodes_s2.py new file mode 100644 index 0000000..91549af --- /dev/null +++ b/esp32_ulp/opcodes_s2.py @@ -0,0 +1,863 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +""" +ESP32 ULP Co-Processor Instructions +""" + +from ucollections import namedtuple +from uctypes import struct, addressof, LITTLE_ENDIAN, UINT32, BFUINT32, BF_POS, BF_LEN + +from .util import split_tokens, validate_expression + +# XXX dirty hack: use a global for the symbol table +symbols = None + +# Opcodes, Sub-Opcodes, Modes, ... + +OPCODE_WR_REG = 1 +OPCODE_RD_REG = 2 + +DR_REG_MAX_DIRECT = 0x3ff +RD_REG_PERIPH_RTC_CNTL = 0 +RD_REG_PERIPH_RTC_IO = 1 +RD_REG_PERIPH_SENS = 2 +RD_REG_PERIPH_RTC_I2C = 3 + +OPCODE_I2C = 3 +SUB_OPCODE_I2C_RD = 0 +SUB_OPCODE_I2C_WR = 1 + +OPCODE_DELAY = 4 + +OPCODE_ADC = 5 + +OPCODE_ST = 6 +SUB_OPCODE_ST_AUTO = 1 +# Note: SUB_OPCODE_ST_OFFSET should be 3 +# But in binutils-gdb they hardcoded the value to 2 +# This appears to be a bug, if one looks at the Technical +# Reference Manual of the ESP32-S2. +# +# This issue is reported as a pull-request with fix: +# https://github.com/espressif/binutils-gdb/pull/2 +# +# We'll hard code this to 2 for now, until this is resolved in +# binutils-gdb or the Technical Reference Manual is updated. +SUB_OPCODE_ST_OFFSET = 2 # should be 3 +SUB_OPCODE_ST = 4 + +OPCODE_ALU = 7 +SUB_OPCODE_ALU_REG = 0 +SUB_OPCODE_ALU_IMM = 1 +ALU_SEL_ADD = 0 +ALU_SEL_SUB = 1 +ALU_SEL_AND = 2 +ALU_SEL_OR = 3 +ALU_SEL_MOV = 4 +ALU_SEL_LSH = 5 +ALU_SEL_RSH = 6 +SUB_OPCODE_ALU_CNT = 2 +ALU_SEL_STAGE_INC = 0 +ALU_SEL_STAGE_DEC = 1 +ALU_SEL_STAGE_RST = 2 + +OPCODE_BRANCH = 8 +# https://github.com/espressif/binutils-esp32ulp/blob/d61f86f97eda43fc118df30d019fc062aaa6bc8d/include/opcode/esp32ulp_esp32.h#L85 +SUB_OPCODE_B = 0 +SUB_OPCODE_BX = 1 +SUB_OPCODE_BS = 2 +BX_JUMP_TYPE_DIRECT = 0 +BX_JUMP_TYPE_ZERO = 1 +BX_JUMP_TYPE_OVF = 2 +# https://github.com/espressif/binutils-esp32ulp/blob/d61f86f97eda43fc118df30d019fc062aaa6bc8d/gas/config/tc-esp32ulp.h#L91 +B_CMP_L = 0 +B_CMP_G = 1 +B_CMP_E = 2 +JUMPS_EQ = 4 +JUMPS_GT = 3 +JUMPS_LT = 1 +JUMPS_LE = 5 +JUMPS_GE = 7 + +OPCODE_END = 9 +SUB_OPCODE_END = 0 +SUB_OPCODE_SLEEP = 1 + +OPCODE_TSENS = 10 + +OPCODE_HALT = 11 + +OPCODE_LD = 13 + + +def make_ins_struct_def(layout): + lines = layout.strip().splitlines() + pos = 0 # bitfield definitions start from lsb + struct_def = {} + for line in lines: + bitfield = line.split('#', 1)[0] # get rid of comment + name, width = bitfield.split(':', 1) + name = name.strip() + width = int(width.strip()) + struct_def[name] = BFUINT32 | pos << BF_POS | width << BF_LEN + pos += width + if pos != 32: + raise ValueError('make_ins: bit field widths must sum up to 32. [%s]' % layout) + struct_def['all'] = UINT32 + return struct_def + + +def make_ins(layout): + """ + transform textual instruction layout description into a ready-to-use uctypes struct + """ + struct_def = make_ins_struct_def(layout) + instruction = bytearray(4) + return struct(addressof(instruction), struct_def, LITTLE_ENDIAN) + + +# instruction structure definitions + +_wr_reg = make_ins(""" + addr : 8 # Address within either RTC_CNTL, RTC_IO, or SARADC + periph_sel : 2 # Select peripheral: RTC_CNTL (0), RTC_IO(1), SARADC(2) + data : 8 # 8 bits of data to write + low : 5 # Low bit + high : 5 # High bit + opcode : 4 # Opcode (OPCODE_WR_REG) +""") + + +_rd_reg = make_ins(""" + addr : 8 # Address within either RTC_CNTL, RTC_IO, or SARADC + periph_sel : 2 # Select peripheral: RTC_CNTL (0), RTC_IO(1), SARADC(2) + unused : 8 # Unused + low : 5 # Low bit + high : 5 # High bit + opcode : 4 # Opcode (OPCODE_RD_REG) +""") + + +_i2c = make_ins(""" + sub_addr : 8 # address within I2C slave + data : 8 # Data to write (not used for read) + low : 3 # low bit + high : 3 # high bit + i2c_sel : 4 # select i2c slave via SENS_I2C_SLAVE_ADDRx + unused : 1 # Unused + rw : 1 # Write (1) or read (0) + opcode : 4 # Opcode (OPCODE_I2C) +""") + + +_delay = make_ins(""" + cycles : 16 # Number of cycles to sleep + unused : 12 # Unused + opcode : 4 # Opcode (OPCODE_DELAY) +""") + + +_tsens = make_ins(""" + dreg : 2 # Register where to store TSENS result + delay : 14 # Number of cycles needed to obtain a measurement + unused : 12 # Unused + opcode : 4 # Opcode (OPCODE_TSENS) +""") + + +_adc = make_ins(""" + dreg : 2 # Register where to store ADC result + mux : 4 # Select SARADC pad (mux + 1) + sar_sel : 1 # Select SARADC0 (0) or SARADC1 (1) + unused1 : 1 # Unused + cycles : 16 # TBD, cycles used for measurement + unused2 : 4 # Unused + opcode: 4 # Opcode (OPCODE_ADC) +""") + + +_st = make_ins(""" + sreg : 2 # Register which contains data to store + dreg : 2 # Register which contains address in RTC memory (expressed in words) + label : 2 # Data label + upper : 1 # Write low (0) or high (1) half-word + wr_way : 2 # Write the (0) full-word or with label (1) or without label (3) + unused1 : 1 # Unused + offset : 11 # Offset to add to dreg + unused2 : 4 # Unused + sub_opcode : 3 # Sub opcode (SUB_OPCODE_ST) + opcode : 4 # Opcode (OPCODE_ST) +""") + + +_alu_reg = make_ins(""" + dreg : 2 # Destination register + sreg : 2 # Register with operand A + treg : 2 # Register with operand B + unused1 : 15 # Unused + sel : 4 # Operation to perform, one of ALU_SEL_xxx + unused2 : 1 # Unused + sub_opcode : 2 # Sub opcode (SUB_OPCODE_ALU_REG) + opcode : 4 # Opcode (OPCODE_ALU) +""") + + +_alu_imm = make_ins(""" + dreg : 2 # Destination register + sreg : 2 # Register with operand A + imm : 16 # Immediate value of operand B + unused1 : 1 # Unused + sel : 4 # Operation to perform, one of ALU_SEL_xxx + unused2 : 1 # Unused + sub_opcode : 2 # Sub opcode (SUB_OPCODE_ALU_IMM) + opcode : 4 # Opcode (OPCODE_ALU) +""") + + +_alu_cnt = make_ins(""" + unused1 : 4 # Unused + imm : 8 # Immediate value (to inc / dec stage counter) + unused2 : 9 # Unused + sel : 4 # Operation to perform, one of ALU_SEL_xxx + unused3 : 1 # Unused + sub_opcode : 2 # Sub opcode (SUB_OPCODE_ALU_CNT) + opcode : 4 # Opcode (OPCODE_ALU) +""") + + +_bx = make_ins(""" + dreg : 2 # Register which contains target PC, expressed in words (used if .reg == 1) + addr : 11 # Target PC, expressed in words (used if .reg == 0) + unused1 : 8 # Unused + reg : 1 # Target PC in register (1) or immediate (0) + type : 3 # Jump condition (BX_JUMP_TYPE_xxx) + unused2 : 1 # Unused + sub_opcode : 2 # Sub opcode (SUB_OPCODE_BX) + opcode : 4 # Opcode (OPCODE_BRANCH) +""") + + +_b = make_ins(""" + imm : 16 # Immediate value to compare against + cmp : 2 # Comparison to perform: BRCOND_LT or BRCOND_GE + offset : 7 # Absolute value of target PC offset w.r.t. current PC, expressed in words + sign : 1 # Sign of target PC offset: 0: positive, 1: negative + sub_opcode : 2 # Sub opcode (SUB_OPCODE_B) + opcode : 4 # Opcode (OPCODE_BRANCH) +""") + + +_bs = make_ins(""" + imm : 8 # Immediate value to compare against + unused : 7 # Unused + cmp : 3 # Comparison to perform: BRCOND_LT, GT or EQ + offset : 7 # Absolute value of target PC offset w.r.t. current PC, expressed in words + sign : 1 # Sign of target PC offset: 0: positive, 1: negative + sub_opcode : 2 # Sub opcode (SUB_OPCODE_BS) + opcode : 4 # Opcode (OPCODE_BRANCH) +""") + + +_end = make_ins(""" + wakeup : 1 # Set to 1 to wake up chip + unused : 25 # Unused + sub_opcode : 2 # Sub opcode (SUB_OPCODE_END) + opcode : 4 # Opcode (OPCODE_END) +""") + + +_halt = make_ins(""" + unused : 28 # Unused + opcode : 4 # Opcode (OPCODE_HALT) +""") + + +_ld = make_ins(""" + dreg : 2 # Register where the data should be loaded to + sreg : 2 # Register which contains address in RTC memory (expressed in words) + unused1 : 6 # Unused + offset : 11 # Offset to add to sreg + unused2 : 6 # Unused + rd_upper : 1 # Read low (0) or high (1) half-word + opcode : 4 # Opcode (OPCODE_LD) +""") + + +# assembler opcode definitions + +REG, IMM, COND, SYM = 0, 1, 2, 3 +ARG = namedtuple('ARG', ('type', 'value', 'raw')) + + +def eval_arg(arg): + parts = [] + for token in split_tokens(arg): + if symbols.has_sym(token): + _, _, sym_value = symbols.get_sym(token) + parts.append(str(sym_value)) + else: + parts.append(token) + parts = "".join(parts) + if not validate_expression(parts): + raise ValueError('Unsupported expression: %s' % parts) + return eval(parts) + + +def arg_qualify(arg): + """ + look at arg and qualify its type: + REG(ister), IMM(ediate) value + + then convert arg into a int value, e.g. 'R1' -> 1 or '0x20' -> 32. + + return result as ARG namedtuple + """ + arg_lower = arg.lower() + if len(arg) == 2: + if arg_lower[0] == 'r' and arg[1] in '0123456789': + reg = int(arg[1]) + if 0 <= reg <= 3: + return ARG(REG, reg, arg) + raise ValueError('arg_qualify: valid registers are r0, r1, r2, r3. Given: %s' % arg) + if arg_lower in ['--', 'eq', 'ov', 'lt', 'gt', 'ge', 'le']: + return ARG(COND, arg_lower, arg) + try: + return ARG(IMM, int(arg), arg) + except ValueError: + pass + try: + entry = symbols.get_sym(arg) + except KeyError: + return ARG(IMM, int(eval_arg(arg)), arg) + else: + return ARG(SYM, entry, arg) + + +def get_reg(arg): + if isinstance(arg, str): + arg = arg_qualify(arg) + if arg.type == REG: + return arg.value + raise TypeError('wanted: register, got: %s' % arg.raw) + + +def get_imm(arg): + if isinstance(arg, str): + arg = arg_qualify(arg) + if arg.type == IMM: + return arg.value + if arg.type == SYM: + return symbols.resolve_absolute(arg.value) + raise TypeError('wanted: immediate, got: %s' % arg.raw) + + +get_abs = get_imm + + +def get_rel(arg): + if isinstance(arg, str): + arg = arg_qualify(arg) + if arg.type == IMM: + if arg.value & 3 != 0: # bitwise version of: arg.value % 4 != 0 + raise ValueError('Relative offset must be a multiple of 4') + return IMM, arg.value >> 2 # bitwise version of: arg.value // 4 + if arg.type == SYM: + return SYM, symbols.resolve_relative(arg.value) + raise TypeError('wanted: immediate, got: %s' % arg.raw) + + +def get_cond(arg): + if isinstance(arg, str): + arg = arg_qualify(arg) + if arg.type == COND: + return arg.value + raise TypeError('wanted: condition, got: %s' % arg.raw) + + +def _soc_reg_to_ulp_periph_sel(reg): + # Accept peripheral register addresses of either the S2 or S3 + # Since the address in the reg_rd or reg_wr instruction is an + # offset and not the actual address, and since the range of + # peripheral register addresses is the same for both the S2 + # and S3, we will accept addresses in either address range. + # This allows us to avoid intruducing an additional cpu type + # for the S3, which is otherwise identical (binary format) to + # the S2. + if 0x3f408000 <= reg <= 0x3f40afff: # ESP32-S2 address range + socmod = 'soc_s2' + elif 0x60008000 <= reg <= 0x6000afff: # ESP32-S3 address range + socmod = 'soc_s3' + # Accept original ESP32 range too + # because binutils-gdb, when using cpu esp32s2 is broken + # and does not accept the address ranges of the esp32s2. + # As a nice side-effect some assembly written for an ESP32 + # would work as-is when re-assembled for an ESP32-S2, + # because many (not all!) peripheral registers live at the + # same offset on all 3 ESP32s. + elif 0x3ff48000 <= reg <= 0x3ff4afff: # original ESP32 address range + socmod = 'soc' + else: + raise ValueError("invalid register base") + + relative_import = 1 if '/' in __file__ else 0 + soc = __import__(socmod, None, None, [], relative_import) + + # Map SoC peripheral register to periph_sel field of RD_REG and WR_REG instructions. + if reg < soc.DR_REG_RTCCNTL_BASE: + raise ValueError("invalid register base") + elif reg < soc.DR_REG_RTCIO_BASE: + ret = RD_REG_PERIPH_RTC_CNTL + elif reg < soc.DR_REG_SENS_BASE: + ret = RD_REG_PERIPH_RTC_IO + elif reg < soc.DR_REG_RTC_I2C_BASE: + ret = RD_REG_PERIPH_SENS + elif reg < soc.DR_REG_IO_MUX_BASE: + ret = RD_REG_PERIPH_RTC_I2C + else: + raise ValueError("invalid register base") + return ret + + +def i_reg_wr(reg, high_bit, low_bit, val): + reg = get_imm(reg) + if reg <= DR_REG_MAX_DIRECT: # see https://github.com/espressif/binutils-esp32ulp/blob/master/gas/config/tc-esp32ulp_esp32.c + _wr_reg.addr = reg & 0xff + _wr_reg.periph_sel = (reg & 0x300) >> 8 + else: + _wr_reg.addr = (reg >> 2) & 0xff + _wr_reg.periph_sel = _soc_reg_to_ulp_periph_sel(reg) + _wr_reg.data = get_imm(val) + _wr_reg.low = get_imm(low_bit) + _wr_reg.high = get_imm(high_bit) + _wr_reg.opcode = OPCODE_WR_REG + return _wr_reg.all + + +def i_reg_rd(reg, high_bit, low_bit): + reg = get_imm(reg) + if reg <= DR_REG_MAX_DIRECT: # see https://github.com/espressif/binutils-esp32ulp/blob/master/gas/config/tc-esp32ulp_esp32.c + _rd_reg.addr = reg & 0xff + _rd_reg.periph_sel = (reg & 0x300) >> 8 + else: + _rd_reg.addr = (reg >> 2) & 0xff + _rd_reg.periph_sel = _soc_reg_to_ulp_periph_sel(reg) + _rd_reg.unused = 0 + _rd_reg.low = get_imm(low_bit) + _rd_reg.high = get_imm(high_bit) + _rd_reg.opcode = OPCODE_RD_REG + return _rd_reg.all + + +def i_i2c_rd(sub_addr, high_bit, low_bit, slave_sel): + _i2c.sub_addr = get_imm(sub_addr) + _i2c.data = 0 + _i2c.low = get_imm(low_bit) + _i2c.high = get_imm(high_bit) + _i2c.i2c_sel = get_imm(slave_sel) + _i2c.unused = 0 + _i2c.rw = 0 + _i2c.opcode = OPCODE_I2C + return _i2c.all + + +def i_i2c_wr(sub_addr, value, high_bit, low_bit, slave_sel): + _i2c.sub_addr = get_imm(sub_addr) + _i2c.data = get_imm(value) + _i2c.low = get_imm(low_bit) + _i2c.high = get_imm(high_bit) + _i2c.i2c_sel = get_imm(slave_sel) + _i2c.unused = 0 + _i2c.rw = 1 + _i2c.opcode = OPCODE_I2C + return _i2c.all + + +def i_nop(): + _delay.cycles = 0 + _delay.unused = 0 + _delay.opcode = OPCODE_DELAY + return _delay.all + + +def i_wait(cycles): + _delay.cycles = get_imm(cycles) + _delay.unused = 0 + _delay.opcode = OPCODE_DELAY + return _delay.all + + +def i_tsens(reg_dest, delay): + _tsens.dreg = get_reg(reg_dest) + _tsens.delay = get_imm(delay) + _tsens.unused = 0 + _tsens.opcode = OPCODE_TSENS + return _tsens.all + + +def i_adc(reg_dest, adc_idx, mux, _not_used=None): + _adc.dreg = get_reg(reg_dest) + _adc.mux = get_imm(mux) + _adc.sar_sel = get_imm(adc_idx) + _adc.unused1 = 0 + _adc.cycles = 0 + _adc.unused2 = 0 + _adc.opcode = OPCODE_ADC + return _adc.all + + +def i_st_manual(reg_val, reg_addr, offset, label, upper, wr_way): + _st.dreg = get_reg(reg_addr) + _st.sreg = get_reg(reg_val) + _st.label = get_imm(label) + _st.upper = upper + _st.wr_way = wr_way + _st.unused1 = 0 + _st.offset = get_imm(offset) // 4 + _st.unused2 = 0 + _st.sub_opcode = SUB_OPCODE_ST + _st.opcode = OPCODE_ST + return _st.all + + +def i_stl(reg_val, reg_addr, offset, label=None): + return i_st_manual(reg_val, reg_addr, offset, label if label else "0", 0, 1 if label else 3) + + +def i_sth(reg_val, reg_addr, offset, label=None): + return i_st_manual(reg_val, reg_addr, offset, label if label else "0", 1, 1 if label else 3) + + +def i_st(reg_val, reg_addr, offset): + return i_stl(reg_val, reg_addr, offset) + + +def i_st32(reg_val, reg_addr, offset, label): + return i_st_manual(reg_val, reg_addr, offset, label, 0, 0) + + +def i_st_auto(reg_val, reg_addr, label, wr_way): + _st.dreg = get_reg(reg_addr) + _st.sreg = get_reg(reg_val) + _st.label = get_imm(label) + _st.upper = 0 + _st.wr_way = wr_way + _st.unused1 = 0 + _st.offset = 0 + _st.unused2 = 0 + _st.sub_opcode = SUB_OPCODE_ST_AUTO + _st.opcode = OPCODE_ST + return _st.all + + +def i_sto(offset): + _st.dreg = 0 + _st.sreg = 0 + _st.label = 0 + _st.upper = 0 + _st.wr_way = 0 + _st.unused1 = 0 + _st.offset = get_imm(offset) // 4 + _st.unused2 = 0 + _st.sub_opcode = SUB_OPCODE_ST_OFFSET + _st.opcode = OPCODE_ST + return _st.all + + +def i_sti(reg_val, reg_addr, label=None): + return i_st_auto(reg_val, reg_addr, label if label else "0", 1 if label else 3) + + +def i_sti32(reg_val, reg_addr, label): + return i_st_auto(reg_val, reg_addr, label, 0) + + +def i_halt(): + _halt.unused = 0 + _halt.opcode = OPCODE_HALT + return _halt.all + + +def i_ld_manual(reg_dest, reg_addr, offset, rd_upper): + _ld.dreg = get_reg(reg_dest) + _ld.sreg = get_reg(reg_addr) + _ld.unused1 = 0 + _ld.offset = get_imm(offset) // 4 + _ld.unused2 = 0 + _ld.rd_upper = rd_upper + _ld.opcode = OPCODE_LD + return _ld.all + + +def i_ldl(reg_dest, reg_addr, offset): + return i_ld_manual(reg_dest, reg_addr, offset, 0) + + +def i_ldh(reg_dest, reg_addr, offset): + return i_ld_manual(reg_dest, reg_addr, offset, 1) + + +def i_ld(reg_dest, reg_addr, offset): + return i_ldl(reg_dest, reg_addr, offset) + + +def i_move(reg_dest, reg_imm_src): + # this is the only ALU instruction with 2 args: move r0, r1 + dest = get_reg(reg_dest) + src = arg_qualify(reg_imm_src) + if src.type == REG: + _alu_reg.dreg = dest + _alu_reg.sreg = src.value + _alu_reg.treg = src.value # XXX undocumented, this is the value binutils-esp32 uses + _alu_reg.unused1 = 0 + _alu_reg.sel = ALU_SEL_MOV + _alu_reg.unused2 = 0 + _alu_reg.sub_opcode = SUB_OPCODE_ALU_REG + _alu_reg.opcode = OPCODE_ALU + return _alu_reg.all + if src.type == IMM or src.type == SYM: + _alu_imm.dreg = dest + _alu_imm.sreg = 0 + _alu_imm.imm = get_abs(src) + _alu_imm.unused1 = 0 + _alu_imm.sel = ALU_SEL_MOV + _alu_imm.unused2 = 0 + _alu_imm.sub_opcode = SUB_OPCODE_ALU_IMM + _alu_imm.opcode = OPCODE_ALU + return _alu_imm.all + raise TypeError('unsupported operand: %s' % src.raw) + + +def _alu3(reg_dest, reg_src1, reg_imm_src2, alu_sel): + """ + ALU instructions with 3 args, like e.g. add r1, r2, r3 + """ + dest = get_reg(reg_dest) + src1 = get_reg(reg_src1) + src2 = arg_qualify(reg_imm_src2) + if src2.type == REG: + _alu_reg.dreg = dest + _alu_reg.sreg = src1 + _alu_reg.treg = src2.value + _alu_reg.unused1 = 0 + _alu_reg.sel = alu_sel + _alu_reg.unused2 = 0 + _alu_reg.sub_opcode = SUB_OPCODE_ALU_REG + _alu_reg.opcode = OPCODE_ALU + return _alu_reg.all + if src2.type == IMM or src2.type == SYM: + _alu_imm.dreg = dest + _alu_imm.sreg = src1 + _alu_imm.imm = get_abs(src2) + _alu_imm.unused1 = 0 + _alu_imm.sel = alu_sel + _alu_imm.unused2 = 0 + _alu_imm.sub_opcode = SUB_OPCODE_ALU_IMM + _alu_imm.opcode = OPCODE_ALU + return _alu_imm.all + raise TypeError('unsupported operand: %s' % src2.raw) + + +def i_add(reg_dest, reg_src1, reg_imm_src2): + return _alu3(reg_dest, reg_src1, reg_imm_src2, ALU_SEL_ADD) + + +def i_sub(reg_dest, reg_src1, reg_imm_src2): + return _alu3(reg_dest, reg_src1, reg_imm_src2, ALU_SEL_SUB) + + +def i_and(reg_dest, reg_src1, reg_imm_src2): + return _alu3(reg_dest, reg_src1, reg_imm_src2, ALU_SEL_AND) + + +def i_or(reg_dest, reg_src1, reg_imm_src2): + return _alu3(reg_dest, reg_src1, reg_imm_src2, ALU_SEL_OR) + + +def i_lsh(reg_dest, reg_src1, reg_imm_src2): + return _alu3(reg_dest, reg_src1, reg_imm_src2, ALU_SEL_LSH) + + +def i_rsh(reg_dest, reg_src1, reg_imm_src2): + return _alu3(reg_dest, reg_src1, reg_imm_src2, ALU_SEL_RSH) + + +def _alu_stage(imm, alu_sel): + """ + Stage counter instructions with 1 arg: stage_inc / stage_dec + """ + imm = get_imm(imm) + _alu_cnt.unused1 = 0 + _alu_cnt.imm = imm + _alu_cnt.unused2 = 0 + _alu_cnt.sel = alu_sel + _alu_cnt.sub_opcode = SUB_OPCODE_ALU_CNT + _alu_cnt.opcode = OPCODE_ALU + return _alu_cnt.all + + +def i_stage_inc(imm): + return _alu_stage(imm, ALU_SEL_STAGE_INC) + + +def i_stage_dec(imm): + return _alu_stage(imm, ALU_SEL_STAGE_DEC) + + +def i_stage_rst(): + return _alu_stage('0', ALU_SEL_STAGE_RST) + + +def i_wake(): + _end.wakeup = 1 + _end.unused = 0 + _end.sub_opcode = SUB_OPCODE_END + _end.opcode = OPCODE_END + return _end.all + + +# NOTE: Technically the S2 no longer has the SLEEP instruction, but +# we're keeping it, since esp32ulp-elf-as happily assembles it. +# It's now emitted as a WAIT so we'll do the same. +def i_sleep(cycles): + return i_wait(cycles) + + +def i_jump(target, condition='--'): + target = arg_qualify(target) + condition = get_cond(condition) + if condition == 'eq': + jump_type = BX_JUMP_TYPE_ZERO + elif condition == 'ov': + jump_type = BX_JUMP_TYPE_OVF + elif condition == '--': # means unconditional + jump_type = BX_JUMP_TYPE_DIRECT + else: + raise ValueError("invalid flags condition") + if target.type == IMM or target.type == SYM: + _bx.dreg = 0 + # we track label addresses in 32bit words, but immediate values are in bytes and need to get divided by 4. + _bx.addr = get_abs(target) if target.type == SYM else get_abs(target) >> 2 # bitwise version of "// 4" + _bx.unused1 = 0 + _bx.reg = 0 + _bx.type = jump_type + _bx.sub_opcode = SUB_OPCODE_BX + _bx.unused2 = 0 + _bx.opcode = OPCODE_BRANCH + return _bx.all + if target.type == REG: + _bx.dreg = target.value + _bx.addr = 0 + _bx.unused1 = 0 + _bx.reg = 1 + _bx.type = jump_type + _bx.unused2 = 0 + _bx.sub_opcode = SUB_OPCODE_BX + _bx.opcode = OPCODE_BRANCH + return _bx.all + raise TypeError('unsupported operand: %s' % target.raw) + + +def _jump_relr(threshold, cond, offset): + """ + Equivalent of I_JUMP_RELR macro in binutils-gdb esp32ulp + """ + _b.imm = threshold + _b.cmp = cond + _b.offset = abs(offset) + _b.sign = 0 if offset >= 0 else 1 + _b.sub_opcode = SUB_OPCODE_B + _b.opcode = OPCODE_BRANCH + return _b.all + + +def i_jumpr(offset, threshold, condition): + offset_type, offset = get_rel(offset) + threshold = get_imm(threshold) + condition = get_cond(condition) + if condition in ('le', 'ge'): + if condition == 'le': + cmp_op = B_CMP_L + elif condition == 'ge': + cmp_op = B_CMP_G + + # jump to target + first_ins = _jump_relr(threshold, cmp_op, offset) + + # jump over prev JUMPR + if (offset_type == IMM and offset < 0) or offset_type == SYM: + # adjust for the additional JUMPR instruction + # for IMM offsets, the offset is relative to the 2nd instruction, so only backwards jumps need adjusting + # for SYM offsets, label offsets already include the extra instruction, so both directions need adjusting + offset -= 1 + second_ins = _jump_relr(threshold, B_CMP_E, offset) + return (first_ins, second_ins) + + elif condition == 'lt': + cmp_op = B_CMP_L + elif condition == 'gt': + cmp_op = B_CMP_G + elif condition == 'eq': + cmp_op = B_CMP_E + else: + raise ValueError("invalid comparison condition") + return _jump_relr(threshold, cmp_op, offset) + + +def _jump_rels(threshold, cond, offset): + """ + Equivalent of I_JUMP_RELS macro in binutils-gdb esp32ulp + """ + _bs.imm = threshold + _bs.cmp = cond + _bs.offset = abs(offset) + _bs.sign = 0 if offset >= 0 else 1 + _bs.sub_opcode = SUB_OPCODE_BS + _bs.opcode = OPCODE_BRANCH + return _bs.all + + +def i_jumps(offset, threshold, condition): + offset_type, offset = get_rel(offset) + if (offset_type == IMM): + # This makes our assembler behave exactly like binutils-gdb, even + # though its behaviour is incorrect. binutils-gdb does not divide + # immediate offsets by 4 (i.e. it does not convert bytes to words) + # for JUMPS instructions, even though it does so for all other JUMP + # instructions, and even though the assembler for the original + # ESP32 divides immediate offsets by 4 for JUMPS instructions. + # + # The issue is reported as a pull-request with a fix here: + # https://github.com/espressif/binutils-gdb/pull/1 + # + # Once the issue is fixed in binutils-gdb, this code here should be + # removed. + offset = offset << 2 # bug in binutils-gdb + + threshold = get_imm(threshold) + condition = get_cond(condition) + if condition == 'lt': + cmp_op = JUMPS_LT + elif condition == 'le': + cmp_op = JUMPS_LE + elif condition == 'ge': + cmp_op = JUMPS_GE + elif condition == 'eq': + cmp_op = JUMPS_EQ + elif condition == 'gt': + cmp_op = JUMPS_GT + else: + raise ValueError("invalid comparison condition") + + return _jump_rels(threshold, cmp_op, offset) + + +def no_of_instr(opcode, args): + if opcode == 'jumpr' and get_cond(args[2]) in ('le', 'ge'): + return 2 + + return 1 diff --git a/esp32_ulp/parse_to_db.py b/esp32_ulp/parse_to_db.py index ac61f98..7b76d9f 100644 --- a/esp32_ulp/parse_to_db.py +++ b/esp32_ulp/parse_to_db.py @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + import sys from .preprocess import Preprocessor diff --git a/esp32_ulp/preprocess.py b/esp32_ulp/preprocess.py index 03a9317..4b08749 100644 --- a/esp32_ulp/preprocess.py +++ b/esp32_ulp/preprocess.py @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + from . import nocomment from .util import split_tokens from .definesdb import DefinesDB diff --git a/esp32_ulp/soc.py b/esp32_ulp/soc.py index c6072e6..a0af189 100644 --- a/esp32_ulp/soc.py +++ b/esp32_ulp/soc.py @@ -1,7 +1,17 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + """ Address / Register definitions for the ESP32 SoC """ +# Reference: +# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32/include/soc/reg_base.h + DR_REG_DPORT_BASE = 0x3ff00000 DR_REG_AES_BASE = 0x3ff01000 DR_REG_RSA_BASE = 0x3ff02000 @@ -38,7 +48,7 @@ DR_REG_SPI_ENCRYPT_BASE = 0x3ff5B000 DR_REG_NRX_BASE = 0x3ff5CC00 DR_REG_BB_BASE = 0x3ff5D000 -DR_REG_PWM_BASE = 0x3ff5E000 +DR_REG_PWM0_BASE = 0x3ff5E000 DR_REG_TIMERGROUP0_BASE = 0x3ff5F000 DR_REG_TIMERGROUP1_BASE = 0x3ff60000 DR_REG_RTCMEM0_BASE = 0x3ff61000 @@ -47,13 +57,12 @@ DR_REG_SPI2_BASE = 0x3ff64000 DR_REG_SPI3_BASE = 0x3ff65000 DR_REG_SYSCON_BASE = 0x3ff66000 -DR_REG_APB_CTRL_BASE = 0x3ff66000 +DR_REG_APB_CTRL_BASE = 0x3ff66000 # Old name for SYSCON, to be removed DR_REG_I2C1_EXT_BASE = 0x3ff67000 DR_REG_SDMMC_BASE = 0x3ff68000 DR_REG_EMAC_BASE = 0x3ff69000 +DR_REG_CAN_BASE = 0x3ff6B000 DR_REG_PWM1_BASE = 0x3ff6C000 DR_REG_I2S1_BASE = 0x3ff6D000 DR_REG_UART2_BASE = 0x3ff6E000 -DR_REG_PWM2_BASE = 0x3ff6F000 -DR_REG_PWM3_BASE = 0x3ff70000 - +PERIPHS_SPI_ENCRYPT_BASEADDR = DR_REG_SPI_ENCRYPT_BASE diff --git a/esp32_ulp/soc_s2.py b/esp32_ulp/soc_s2.py new file mode 100644 index 0000000..bcaca53 --- /dev/null +++ b/esp32_ulp/soc_s2.py @@ -0,0 +1,71 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +""" +Address / Register definitions for the ESP32-S2 SoC +""" + +# Reference: +# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s2/include/soc/reg_base.h + +DR_REG_SYSTEM_BASE = 0x3f4c0000 +DR_REG_SENSITIVE_BASE = 0x3f4c1000 +DR_REG_INTERRUPT_BASE = 0x3f4c2000 +DR_REG_DMA_COPY_BASE = 0x3f4c3000 +DR_REG_EXTMEM_BASE = 0x61800000 +DR_REG_MMU_TABLE = 0x61801000 +DR_REG_ITAG_TABLE = 0x61802000 +DR_REG_DTAG_TABLE = 0x61803000 +DR_REG_AES_BASE = 0x6003a000 +DR_REG_SHA_BASE = 0x6003b000 +DR_REG_RSA_BASE = 0x6003c000 +DR_REG_HMAC_BASE = 0x6003e000 +DR_REG_DIGITAL_SIGNATURE_BASE = 0x6003d000 +DR_REG_CRYPTO_DMA_BASE = 0x6003f000 +DR_REG_ASSIST_DEBUG_BASE = 0x3f4ce000 +DR_REG_DEDICATED_GPIO_BASE = 0x3f4cf000 +DR_REG_INTRUSION_BASE = 0x3f4d0000 +DR_REG_UART_BASE = 0x3f400000 +DR_REG_SPI1_BASE = 0x3f402000 +DR_REG_SPI0_BASE = 0x3f403000 +DR_REG_GPIO_BASE = 0x3f404000 +DR_REG_GPIO_SD_BASE = 0x3f404f00 +DR_REG_FE2_BASE = 0x3f405000 +DR_REG_FE_BASE = 0x3f406000 +DR_REG_FRC_TIMER_BASE = 0x3f407000 +DR_REG_RTCCNTL_BASE = 0x3f408000 +DR_REG_RTCIO_BASE = 0x3f408400 +DR_REG_SENS_BASE = 0x3f408800 +DR_REG_RTC_I2C_BASE = 0x3f408C00 +DR_REG_IO_MUX_BASE = 0x3f409000 +DR_REG_HINF_BASE = 0x3f40B000 +DR_REG_I2S_BASE = 0x3f40F000 +DR_REG_UART1_BASE = 0x3f410000 +DR_REG_I2C_EXT_BASE = 0x3f413000 +DR_REG_UHCI0_BASE = 0x3f414000 +DR_REG_SLCHOST_BASE = 0x3f415000 +DR_REG_RMT_BASE = 0x3f416000 +DR_REG_PCNT_BASE = 0x3f417000 +DR_REG_SLC_BASE = 0x3f418000 +DR_REG_LEDC_BASE = 0x3f419000 +DR_REG_CP_BASE = 0x3f4c3000 +DR_REG_EFUSE_BASE = 0x3f41A000 +DR_REG_NRX_BASE = 0x3f41CC00 +DR_REG_BB_BASE = 0x3f41D000 +DR_REG_TIMERGROUP0_BASE = 0x3f41F000 +DR_REG_TIMERGROUP1_BASE = 0x3f420000 +DR_REG_RTC_SLOWMEM_BASE = 0x3f421000 +DR_REG_SYSTIMER_BASE = 0x3f423000 +DR_REG_SPI2_BASE = 0x3f424000 +DR_REG_SPI3_BASE = 0x3f425000 +DR_REG_SYSCON_BASE = 0x3f426000 +DR_REG_APB_CTRL_BASE = 0x3f426000 # Old name for SYSCON, to be removed +DR_REG_I2C1_EXT_BASE = 0x3f427000 +DR_REG_SPI4_BASE = 0x3f437000 +DR_REG_USB_WRAP_BASE = 0x3f439000 +DR_REG_APB_SARADC_BASE = 0x3f440000 +DR_REG_USB_BASE = 0x60080000 diff --git a/esp32_ulp/soc_s3.py b/esp32_ulp/soc_s3.py new file mode 100644 index 0000000..d1d95fd --- /dev/null +++ b/esp32_ulp/soc_s3.py @@ -0,0 +1,73 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +""" +Address / Register definitions for the ESP32-S3 SoC +""" + +# Reference: +# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s3/include/soc/reg_base.h + +DR_REG_UART_BASE = 0x60000000 +DR_REG_SPI1_BASE = 0x60002000 +DR_REG_SPI0_BASE = 0x60003000 +DR_REG_GPIO_BASE = 0x60004000 +DR_REG_GPIO_SD_BASE = 0x60004f00 +DR_REG_FE2_BASE = 0x60005000 +DR_REG_FE_BASE = 0x60006000 +DR_REG_EFUSE_BASE = 0x60007000 +DR_REG_RTCCNTL_BASE = 0x60008000 +DR_REG_RTCIO_BASE = 0x60008400 +DR_REG_SENS_BASE = 0x60008800 +DR_REG_RTC_I2C_BASE = 0x60008C00 +DR_REG_IO_MUX_BASE = 0x60009000 +DR_REG_HINF_BASE = 0x6000B000 +DR_REG_UHCI1_BASE = 0x6000C000 +DR_REG_I2S_BASE = 0x6000F000 +DR_REG_UART1_BASE = 0x60010000 +DR_REG_BT_BASE = 0x60011000 +DR_REG_I2C_EXT_BASE = 0x60013000 +DR_REG_UHCI0_BASE = 0x60014000 +DR_REG_SLCHOST_BASE = 0x60015000 +DR_REG_RMT_BASE = 0x60016000 +DR_REG_PCNT_BASE = 0x60017000 +DR_REG_SLC_BASE = 0x60018000 +DR_REG_LEDC_BASE = 0x60019000 +DR_REG_NRX_BASE = 0x6001CC00 +DR_REG_BB_BASE = 0x6001D000 +DR_REG_PWM0_BASE = 0x6001E000 +DR_REG_TIMERGROUP0_BASE = 0x6001F000 +DR_REG_TIMERGROUP1_BASE = 0x60020000 +DR_REG_RTC_SLOWMEM_BASE = 0x60021000 +DR_REG_SYSTIMER_BASE = 0x60023000 +DR_REG_SPI2_BASE = 0x60024000 +DR_REG_SPI3_BASE = 0x60025000 +DR_REG_SYSCON_BASE = 0x60026000 +DR_REG_APB_CTRL_BASE = 0x60026000 # Old name for SYSCON, to be removed +DR_REG_I2C1_EXT_BASE = 0x60027000 +DR_REG_SDMMC_BASE = 0x60028000 +DR_REG_PERI_BACKUP_BASE = 0x6002A000 +DR_REG_TWAI_BASE = 0x6002B000 +DR_REG_PWM1_BASE = 0x6002C000 +DR_REG_I2S1_BASE = 0x6002D000 +DR_REG_UART2_BASE = 0x6002E000 +DR_REG_USB_SERIAL_JTAG_BASE = 0x60038000 +DR_REG_USB_WRAP_BASE = 0x60039000 +DR_REG_AES_BASE = 0x6003A000 +DR_REG_SHA_BASE = 0x6003B000 +DR_REG_RSA_BASE = 0x6003C000 +DR_REG_HMAC_BASE = 0x6003E000 +DR_REG_DIGITAL_SIGNATURE_BASE = 0x6003D000 +DR_REG_GDMA_BASE = 0x6003F000 +DR_REG_APB_SARADC_BASE = 0x60040000 +DR_REG_LCD_CAM_BASE = 0x60041000 +DR_REG_SYSTEM_BASE = 0x600C0000 +DR_REG_SENSITIVE_BASE = 0x600C1000 +DR_REG_INTERRUPT_BASE = 0x600C2000 +DR_REG_EXTMEM_BASE = 0x600C4000 +DR_REG_ASSIST_DEBUG_BASE = 0x600CE000 +DR_REG_WCL_BASE = 0x600D0000 diff --git a/esp32_ulp/util.py b/esp32_ulp/util.py index d79c538..78e7c85 100644 --- a/esp32_ulp/util.py +++ b/esp32_ulp/util.py @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + DEBUG = False import gc @@ -49,23 +56,23 @@ def validate_expression(param): for token in split_tokens(param): state = 0 for c in token: - if c not in ' \t+-*/%()<>&|~x0123456789abcdef': + if c not in ' \t+-*/%()<>&|~xX0123456789abcdefABCDEF': return False # the following allows hex digits a-f after 0x but not otherwise if state == 0: - if c in 'abcdef': + if c in 'abcdefABCDEF': return False if c == '0': state = 1 continue if state == 1: - state = 2 if c == 'x' else 0 + state = 2 if c in 'xX' else 0 continue if state == 2: - if c not in '0123456789abcdef': + if c not in '0123456789abcdefABCDEF': state = 0 return True diff --git a/examples/blink.py b/examples/blink.py index 8ab0df0..3c43883 100644 --- a/examples/blink.py +++ b/examples/blink.py @@ -1,4 +1,13 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + """ +Example for: ESP32 + Simple example showing how to control a GPIO pin from the ULP coprocessor. The GPIO port is configured to be attached to the RTC module, and then set @@ -22,22 +31,20 @@ source = """\ # constants from: -# https://github.com/espressif/esp-idf/blob/1cb31e5/components/soc/esp32/include/soc/soc.h +# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32/include/soc/reg_base.h #define DR_REG_RTCIO_BASE 0x3ff48400 # constants from: -# https://github.com/espressif/esp-idf/blob/1cb31e5/components/soc/esp32/include/soc/rtc_io_reg.h +# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32/include/soc/rtc_io_reg.h #define RTC_IO_TOUCH_PAD2_REG (DR_REG_RTCIO_BASE + 0x9c) #define RTC_IO_TOUCH_PAD2_MUX_SEL_M (BIT(19)) #define RTC_GPIO_OUT_REG (DR_REG_RTCIO_BASE + 0x0) -#define RTC_GPIO_ENABLE_W1TS_REG (DR_REG_RTCIO_BASE + 0x10) -#define RTC_GPIO_ENABLE_W1TC_REG (DR_REG_RTCIO_BASE + 0x14) -#define RTC_GPIO_ENABLE_W1TS_S 14 -#define RTC_GPIO_ENABLE_W1TC_S 14 +#define RTC_GPIO_ENABLE_REG (DR_REG_RTCIO_BASE + 0xc) +#define RTC_GPIO_ENABLE_S 14 #define RTC_GPIO_OUT_DATA_S 14 # constants from: -# https://github.com/espressif/esp-idf/blob/1cb31e5/components/soc/esp32/include/soc/rtc_io_channel.h +# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32/include/soc/rtc_io_channel.h #define RTCIO_GPIO2_CHANNEL 12 # When accessed from the RTC module (ULP) GPIOs need to be addressed by their channel number @@ -62,8 +69,8 @@ # connect GPIO to ULP (0: GPIO connected to digital GPIO module, 1: GPIO connected to analog RTC module) WRITE_RTC_REG(RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_MUX_SEL_M, 1, 1); - # GPIO shall be output, not input - WRITE_RTC_REG(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + gpio, 1, 1); + # GPIO shall be output, not input (this also enables a pull-down by default) + WRITE_RTC_REG(RTC_GPIO_ENABLE_REG, RTC_GPIO_ENABLE_S + gpio, 1, 1) # store that we're done with initialisation move r0, magic @@ -83,19 +90,19 @@ on: # turn on led (set GPIO) - WRITE_RTC_REG(RTC_GPIO_ENABLE_W1TS_REG, RTC_GPIO_ENABLE_W1TS_S + gpio, 1, 1) + WRITE_RTC_REG(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + gpio, 1, 1) jump exit off: # turn off led (clear GPIO) - WRITE_RTC_REG(RTC_GPIO_ENABLE_W1TC_REG, RTC_GPIO_ENABLE_W1TC_S + gpio, 1, 1) + WRITE_RTC_REG(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + gpio, 1, 0) jump exit exit: halt # go back to sleep until next wakeup period """ -binary = src_to_binary(source) +binary = src_to_binary(source, cpu="esp32") # cpu is esp32 or esp32s2 load_addr, entry_addr = 0, 8 diff --git a/examples/blink_s2.py b/examples/blink_s2.py new file mode 100644 index 0000000..2bdeccc --- /dev/null +++ b/examples/blink_s2.py @@ -0,0 +1,119 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +""" +Example for: ESP32-S2 and ESP32-S3 + +The GPIO port is configured to be attached to the RTC module, and then set +to OUTPUT mode. To avoid re-initializing the GPIO on every wakeup, a magic +token gets set in memory. + +After every change of state, the ULP is put back to sleep again until the +next wakeup. The ULP wakes up every 500ms to change the state of the GPIO +pin. An LED attached to the GPIO pin would toggle on and off every 500ms. + +The end of the python script has a loop to show the value of the magic token +and the current state, so you can confirm the magic token gets set and watch +the state value changing. If the loop is stopped (Ctrl-C), the LED attached +to the GPIO pin continues to blink, because the ULP runs independently from +the main processor. +""" + +from esp32 import ULP +from machine import mem32 +from esp32_ulp import src_to_binary + +source = """\ +# constants from: +# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s2/include/soc/reg_base.h +#define DR_REG_RTCIO_BASE 0x3f408400 + +# constants from: +# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s2/include/soc/rtc_io_reg.h +#define RTC_IO_TOUCH_PAD2_REG (DR_REG_RTCIO_BASE + 0x8c) +#define RTC_IO_TOUCH_PAD2_MUX_SEL_M (BIT(19)) +#define RTC_GPIO_OUT_REG (DR_REG_RTCIO_BASE + 0x0) +#define RTC_GPIO_ENABLE_REG (DR_REG_RTCIO_BASE + 0xc) +#define RTC_GPIO_ENABLE_S 10 +#define RTC_GPIO_OUT_DATA_S 10 + +# constants from: +# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s2/include/soc/rtc_io_channel.h +#define RTCIO_GPIO2_CHANNEL 2 + +# When accessed from the RTC module (ULP) GPIOs need to be addressed by their channel number +.set gpio, RTCIO_GPIO2_CHANNEL +.set token, 0xcafe # magic token + +.text +magic: .long 0 +state: .long 0 + +.global entry +entry: + # load magic flag + move r0, magic + ld r1, r0, 0 + + # test if we have initialised already + sub r1, r1, token + jump after_init, eq # jump if magic == token (note: "eq" means the last instruction (sub) resulted in 0) + +init: + # connect GPIO to ULP (0: GPIO connected to digital GPIO module, 1: GPIO connected to analog RTC module) + WRITE_RTC_REG(RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_MUX_SEL_M, 1, 1); + + # GPIO shall be output, not input (this also enables a pull-down by default) + WRITE_RTC_REG(RTC_GPIO_ENABLE_REG, RTC_GPIO_ENABLE_S + gpio, 1, 1) + + # store that we're done with initialisation + move r0, magic + move r1, token + st r1, r0, 0 + +after_init: + move r1, state + ld r0, r1, 0 + + move r2, 1 + sub r0, r2, r0 # toggle state + st r0, r1, 0 # store updated state + + jumpr on, 0, gt # if r0 (state) > 0, jump to 'on' + jump off # else jump to 'off' + +on: + # turn on led (set GPIO) + WRITE_RTC_REG(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + gpio, 1, 1) + jump exit + +off: + # turn off led (clear GPIO) + WRITE_RTC_REG(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + gpio, 1, 0) + jump exit + +exit: + halt # go back to sleep until next wakeup period +""" + +binary = src_to_binary(source, cpu="esp32s2") # cpu is esp32 or esp32s2 + +load_addr, entry_addr = 0, 8 + +ULP_MEM_BASE = 0x50000000 +ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits + +ulp = ULP() +ulp.set_wakeup_period(0, 500000) # use timer0, wakeup after 500000usec (0.5s) +ulp.load_binary(load_addr, binary) + +ulp.run(entry_addr) + +while True: + print(hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK), # magic token + hex(mem32[ULP_MEM_BASE + load_addr + 4] & ULP_DATA_MASK) # current state + ) diff --git a/examples/counter.py b/examples/counter.py index 77fb146..6029bbd 100644 --- a/examples/counter.py +++ b/examples/counter.py @@ -1,4 +1,13 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + """ +Example for: ESP32 + Very basic example showing data exchange main CPU <--> ULP coprocessor. To show that the ULP is doing something, it just increments the value . @@ -25,7 +34,7 @@ halt # halt ULP co-prozessor (until it gets waked up again) """ -binary = src_to_binary(source) +binary = src_to_binary(source, cpu="esp32") # cpu is esp32 or esp32s2 load_addr, entry_addr = 0, 4 diff --git a/examples/counter_s2.py b/examples/counter_s2.py new file mode 100644 index 0000000..a5abbf9 --- /dev/null +++ b/examples/counter_s2.py @@ -0,0 +1,53 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +""" +Example for: ESP32-S2 and ESP32-S3 + +Very basic example showing data exchange main CPU <--> ULP coprocessor. + +To show that the ULP is doing something, it just increments the value . +It does that once per ulp timer wakeup (and then the ULP halts until it gets +waked up via the timer again). + +The timer is set to a rather long period, so you can watch the data value +incrementing (see loop at the end). +""" + +from esp32 import ULP +from machine import mem32 + +from esp32_ulp import src_to_binary + +source = """\ +data: .long 0 + +entry: move r3, data # load address of data into r3 + ld r2, r3, 0 # load data contents ([r3+0]) into r2 + add r2, r2, 1 # increment r2 + st r2, r3, 0 # store r2 contents into data ([r3+0]) + + halt # halt ULP co-prozessor (until it gets waked up again) +""" + +binary = src_to_binary(source, cpu="esp32s2") # cpu is esp32 or esp32s2 + +load_addr, entry_addr = 0, 4 + +ULP_MEM_BASE = 0x50000000 +ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits + +ulp = ULP() +ulp.set_wakeup_period(0, 50000) # use timer0, wakeup after 50.000 cycles +ulp.load_binary(load_addr, binary) + +mem32[ULP_MEM_BASE + load_addr] = 0x1000 +ulp.run(entry_addr) + +while True: + print(hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK)) + diff --git a/examples/readgpio.py b/examples/readgpio.py new file mode 100644 index 0000000..944f1c2 --- /dev/null +++ b/examples/readgpio.py @@ -0,0 +1,80 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +""" +Example for: ESP32 + +Very basic example showing how to read a GPIO pin from the ULP and access +that data from the main CPU. + +In this case GPIO4 is being read. Note that the ULP needs to refer to GPIOs +via their RTC channel number. You can see the mapping in this file: +https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32/include/soc/rtc_io_channel.h#L51 + +If you change to a different GPIO number, make sure to modify both the channel +number and also the RTC_IO_TOUCH_PAD0_* references appropriately. The best place +to see the mappings might be this table here (notice the "real GPIO numbers" as +comments to each line): +https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32/rtc_io_periph.c#L53 + +The timer is set to a rather long period, so you can watch the data value +change as you change the GPIO input (see loop at the end). +""" + +from esp32 import ULP +from machine import mem32 + +from esp32_ulp import src_to_binary + +source = """\ +#define DR_REG_RTCIO_BASE 0x3ff48400 +#define RTC_IO_TOUCH_PAD0_REG (DR_REG_RTCIO_BASE + 0x94) +#define RTC_IO_TOUCH_PAD0_MUX_SEL_M (BIT(19)) +#define RTC_IO_TOUCH_PAD0_FUN_IE_M (BIT(13)) +#define RTC_GPIO_IN_REG (DR_REG_RTCIO_BASE + 0x24) +#define RTC_GPIO_IN_NEXT_S 14 +.set channel, 10 # 10 is the channel no. of gpio4 + +state: .long 0 + +entry: + # connect GPIO to the RTC subsystem so the ULP can read it + WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_MUX_SEL_M, 1, 1) + + # switch the GPIO into input mode + WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, RTC_IO_TOUCH_PAD0_FUN_IE_M, 1, 1) + + # read the GPIO's current state into r0 + READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + channel, 1) + + # set r3 to the memory address of "state" + move r3, state + + # store what was read into r0 into the "state" variable + st r0, r3, 0 + + # halt ULP co-processor (until it gets woken up again) + halt +""" + +binary = src_to_binary(source, cpu="esp32") # cpu is esp32 or esp32s2 + +load_addr, entry_addr = 0, 4 + +ULP_MEM_BASE = 0x50000000 +ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits + +ulp = ULP() +ulp.set_wakeup_period(0, 50000) # use timer0, wakeup after 50.000 cycles +ulp.load_binary(load_addr, binary) + +mem32[ULP_MEM_BASE + load_addr] = 0x0 # initialise state to 0 +ulp.run(entry_addr) + +while True: + print(hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK)) + diff --git a/examples/readgpio_s2.py b/examples/readgpio_s2.py new file mode 100644 index 0000000..91614e3 --- /dev/null +++ b/examples/readgpio_s2.py @@ -0,0 +1,86 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +""" +Example for: ESP32-S2 + +Very basic example showing how to read a GPIO pin from the ULP and access +that data from the main CPU. + +In this case GPIO4 is being read. Note that the ULP needs to refer to GPIOs +via their RTC channel number. You can see the mapping in this file: +https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s2/include/soc/rtc_io_channel.h#L33 + +If you change to a different GPIO number, make sure to modify both the channel +number and also the RTC_IO_TOUCH_PAD4_* references appropriately. The best place +to see the mappings might be this table here (notice the "real GPIO numbers" as +comments to each line): +https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s2/rtc_io_periph.c#L60 + +The timer is set to a rather long period, so you can watch the data value +change as you change the GPIO input (see loop at the end). +""" + +from esp32 import ULP +from machine import mem32 + +from esp32_ulp import src_to_binary + +source = """\ +#define DR_REG_RTCIO_BASE 0x3f408400 +#define RTC_IO_TOUCH_PAD4_REG (DR_REG_RTCIO_BASE + 0x94) +#define RTC_IO_TOUCH_PAD4_MUX_SEL_M (BIT(19)) +#define RTC_IO_TOUCH_PAD4_FUN_IE_M (BIT(13)) +#define RTC_GPIO_IN_REG (DR_REG_RTCIO_BASE + 0x24) +#define RTC_GPIO_IN_NEXT_S 10 +#define DR_REG_SENS_BASE 0x3f408800 +#define SENS_SAR_IO_MUX_CONF_REG (DR_REG_SENS_BASE + 0x0144) +#define SENS_IOMUX_CLK_GATE_EN (BIT(31)) +.set channel, 4 + +state: .long 0 + +entry: + # enable IOMUX clock + WRITE_RTC_FIELD(SENS_SAR_IO_MUX_CONF_REG, SENS_IOMUX_CLK_GATE_EN, 1) + + # connect GPIO to the RTC subsystem so the ULP can read it + WRITE_RTC_REG(RTC_IO_TOUCH_PAD4_REG, RTC_IO_TOUCH_PAD4_MUX_SEL_M, 1, 1) + + # switch the GPIO into input mode + WRITE_RTC_REG(RTC_IO_TOUCH_PAD4_REG, RTC_IO_TOUCH_PAD4_FUN_IE_M, 1, 1) + + # read the GPIO's current state into r0 + READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + channel, 1) + + # set r3 to the memory address of "state" + move r3, state + + # store what was read into r0 into the "state" variable + st r0, r3, 0 + + # halt ULP co-processor (until it gets woken up again) + halt +""" + +binary = src_to_binary(source, cpu="esp32s2") # cpu is esp32 or esp32s2 + +load_addr, entry_addr = 0, 4 + +ULP_MEM_BASE = 0x50000000 +ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits + +ulp = ULP() +ulp.set_wakeup_period(0, 50000) # use timer0, wakeup after 50.000 cycles +ulp.load_binary(load_addr, binary) + +mem32[ULP_MEM_BASE + load_addr] = 0x0 # initialise state to 0 +ulp.run(entry_addr) + +while True: + print(hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK)) + diff --git a/examples/readgpio_s3.py b/examples/readgpio_s3.py new file mode 100644 index 0000000..d5645cd --- /dev/null +++ b/examples/readgpio_s3.py @@ -0,0 +1,86 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +""" +Example for: ESP32-S3 + +Very basic example showing how to read a GPIO pin from the ULP and access +that data from the main CPU. + +In this case GPIO4 is being read. Note that the ULP needs to refer to GPIOs +via their RTC channel number. You can see the mapping in this file: +https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s3/include/soc/rtc_io_channel.h#L33 + +If you change to a different GPIO number, make sure to modify both the channel +number and also the RTC_IO_TOUCH_PAD2_* references appropriately. The best place +to see the mappings might be this table here (notice the "real GPIO numbers" as +comments to each line): +https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s3/rtc_io_periph.c#L60 + +The timer is set to a rather long period, so you can watch the data value +change as you change the GPIO input (see loop at the end). +""" + +from esp32 import ULP +from machine import mem32 + +from esp32_ulp import src_to_binary + +source = """\ +#define DR_REG_RTCIO_BASE 0x60008400 +#define RTC_IO_TOUCH_PAD2_REG (DR_REG_RTCIO_BASE + 0x8c) +#define RTC_IO_TOUCH_PAD2_MUX_SEL_M (BIT(19)) +#define RTC_IO_TOUCH_PAD2_FUN_IE_M (BIT(13)) +#define RTC_GPIO_IN_REG (DR_REG_RTCIO_BASE + 0x24) +#define RTC_GPIO_IN_NEXT_S 10 +#define DR_REG_SENS_BASE 0x60008800 +#define SENS_SAR_PERI_CLK_GATE_CONF_REG (DR_REG_SENS_BASE + 0x104) +#define SENS_IOMUX_CLK_EN (BIT(31)) +.set channel, 2 + +state: .long 0 + +entry: + # enable IOMUX clock + WRITE_RTC_FIELD(SENS_SAR_PERI_CLK_GATE_CONF_REG, SENS_IOMUX_CLK_EN, 1) + + # connect GPIO to the RTC subsystem so the ULP can read it + WRITE_RTC_REG(RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_MUX_SEL_M, 1, 1) + + # switch the GPIO into input mode + WRITE_RTC_REG(RTC_IO_TOUCH_PAD2_REG, RTC_IO_TOUCH_PAD2_FUN_IE_M, 1, 1) + + # read the GPIO's current state into r0 + READ_RTC_REG(RTC_GPIO_IN_REG, RTC_GPIO_IN_NEXT_S + channel, 1) + + # set r3 to the memory address of "state" + move r3, state + + # store what was read into r0 into the "state" variable + st r0, r3, 0 + + # halt ULP co-processor (until it gets woken up again) + halt +""" + +binary = src_to_binary(source, cpu="esp32s2") # cpu is esp32 or esp32s2 + +load_addr, entry_addr = 0, 4 + +ULP_MEM_BASE = 0x50000000 +ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits + +ulp = ULP() +ulp.set_wakeup_period(0, 50000) # use timer0, wakeup after 50.000 cycles +ulp.load_binary(load_addr, binary) + +mem32[ULP_MEM_BASE + load_addr] = 0x0 # initialise state to 0 +ulp.run(entry_addr) + +while True: + print(hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK)) + diff --git a/package.json b/package.json new file mode 100644 index 0000000..7290bdf --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "v":1, + "urls":[ + ["esp32_ulp/__init__.py", "github:micropython/micropython-esp32-ulp/esp32_ulp/__init__.py"], + ["esp32_ulp/__main__.py", "github:micropython/micropython-esp32-ulp/esp32_ulp/__main__.py"], + ["esp32_ulp/assemble.py", "github:micropython/micropython-esp32-ulp/esp32_ulp/assemble.py"], + ["esp32_ulp/definesdb.py", "github:micropython/micropython-esp32-ulp/esp32_ulp/definesdb.py"], + ["esp32_ulp/link.py", "github:micropython/micropython-esp32-ulp/esp32_ulp/link.py"], + ["esp32_ulp/nocomment.py", "github:micropython/micropython-esp32-ulp/esp32_ulp/nocomment.py"], + ["esp32_ulp/opcodes.py", "github:micropython/micropython-esp32-ulp/esp32_ulp/opcodes.py"], + ["esp32_ulp/opcodes_s2.py", "github:micropython/micropython-esp32-ulp/esp32_ulp/opcodes_s2.py"], + ["esp32_ulp/parse_to_db.py", "github:micropython/micropython-esp32-ulp/esp32_ulp/parse_to_db.py"], + ["esp32_ulp/preprocess.py", "github:micropython/micropython-esp32-ulp/esp32_ulp/preprocess.py"], + ["esp32_ulp/soc.py", "github:micropython/micropython-esp32-ulp/esp32_ulp/soc.py"], + ["esp32_ulp/soc_s2.py", "github:micropython/micropython-esp32-ulp/esp32_ulp/soc_s2.py"], + ["esp32_ulp/soc_s3.py", "github:micropython/micropython-esp32-ulp/esp32_ulp/soc_s3.py"], + ["esp32_ulp/util.py", "github:micropython/micropython-esp32-ulp/esp32_ulp/util.py"] + ] +} diff --git a/setup.py b/setup.py index 02fc461..4eab8ff 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + import re from setuptools import setup import sdist_upip diff --git a/tests/00_unit_tests.sh b/tests/00_unit_tests.sh index ee1a239..0a01933 100755 --- a/tests/00_unit_tests.sh +++ b/tests/00_unit_tests.sh @@ -1,10 +1,18 @@ #!/bin/bash +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT # export PYTHONPATH=.:$PYTHONPATH set -e -for file in opcodes assemble link util preprocess definesdb; do +LIST=${1:-opcodes opcodes_s2 assemble link util preprocess definesdb decode decode_s2} + +for file in $LIST; do echo testing $file... micropython $file.py done diff --git a/tests/01_compat_tests.sh b/tests/01_compat_tests.sh index 7aabe3c..a6fc2d1 100755 --- a/tests/01_compat_tests.sh +++ b/tests/01_compat_tests.sh @@ -1,4 +1,10 @@ #!/bin/bash +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT # export PYTHONPATH=.:$PYTHONPATH @@ -10,38 +16,69 @@ calc_file_hash() { shasum < $1 | cut -d' ' -f1 } -for src_file in $(ls -1 compat/*.S); do - src_name="${src_file%.S}" - - echo "Testing $src_file" - echo -e "\tBuilding using micropython-esp32-ulp" - ulp_file="${src_name}.ulp" - log_file="${src_name}.log" - micropython -m esp32_ulp $src_file 1>$log_file # generates $ulp_file - - pre_file="${src_name}.pre" - obj_file="${src_name}.o" - elf_file="${src_name}.elf" - bin_file="${src_name}.bin" - - echo -e "\tBuilding using binutils" - gcc -E -o ${pre_file} $src_file - esp32ulp-elf-as -o $obj_file ${pre_file} - esp32ulp-elf-ld -T esp32.ulp.ld -o $elf_file $obj_file - esp32ulp-elf-objcopy -O binary $elf_file $bin_file - - if ! diff $ulp_file $bin_file 1>/dev/null; then - echo -e "\tBuild outputs differ!" - echo "" - echo "Compatibility test failed for $src_file" - echo "micropython-esp32-ulp log:" - cat $log_file - echo "micropython-esp32-ulp output:" - xxd $ulp_file - echo "binutils output:" - xxd $bin_file - exit 1 - else - echo -e "\tBuild outputs match (sha1: $(calc_file_hash $ulp_file))" - fi -done +make_log_dir() { + mkdir -p log +} + +fetch_esp_idf() { + [ -d esp-idf ] && return + + echo "Fetching esp-idf" + log_file=log/fetch-esp-idf.log + git clone --depth 1 \ + https://github.com/espressif/esp-idf.git 1>$log_file 2>&1 +} + +run_tests_for_cpu() { + local cpu=$1 + echo "Testing for CPU: $cpu" + + for src_file in $(ls -1 compat/*.S fixtures/*.S); do + src_name="${src_file%.S}" + + # files with a cpu encoded into their name are only run for that cpu + if [[ $src_file =~ \.esp32\. && $cpu != esp32 ]] || [[ $src_file =~ \.esp32s2?\. && $cpu != esp32s2 ]]; then + continue + fi + echo "Testing $src_file" + echo -e "\tBuilding using micropython-esp32-ulp ($cpu)" + ulp_file="${src_name}.ulp" + log_file="${src_name}.log" + micropython -m esp32_ulp -c $cpu $src_file 1>$log_file # generates $ulp_file + + pre_file="${src_name}.pre" + obj_file="${src_name}.o" + elf_file="${src_name}.elf" + bin_file="${src_name}.bin" + + echo -e "\tBuilding using binutils ($cpu)" + gcc -I esp-idf/components/soc/$cpu/include -I esp-idf/components/esp_common/include \ + -I esp-idf/components/soc/$cpu/register \ + -x assembler-with-cpp \ + -E -o ${pre_file} $src_file + esp32ulp-elf-as --mcpu=$cpu -o $obj_file ${pre_file} + esp32ulp-elf-ld -T esp32.ulp.ld -o $elf_file $obj_file + esp32ulp-elf-objcopy -O binary $elf_file $bin_file + + if ! diff $ulp_file $bin_file 1>/dev/null; then + echo -e "\tBuild outputs differ!" + echo "" + echo "Compatibility test failed for $src_file" + echo "micropython-esp32-ulp log:" + cat $log_file + echo "micropython-esp32-ulp output:" + xxd $ulp_file + echo "binutils output:" + xxd $bin_file + exit 1 + else + echo -e "\tBuild outputs match (sha1: $(calc_file_hash $ulp_file))" + fi + done + echo "" +} + +make_log_dir +fetch_esp_idf +run_tests_for_cpu esp32 +run_tests_for_cpu esp32s2 diff --git a/tests/02_compat_rtc_tests.sh b/tests/02_compat_rtc_tests.sh index de89aaf..be2a98d 100755 --- a/tests/02_compat_rtc_tests.sh +++ b/tests/02_compat_rtc_tests.sh @@ -1,4 +1,10 @@ #!/bin/bash +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT # export PYTHONPATH=.:$PYTHONPATH @@ -27,28 +33,39 @@ fetch_ulptool_examples() { } fetch_binutils_esp32ulp_examples() { - [ -d binutils-esp32ulp ] && return + [ -d binutils-gdb ] && return - echo "Fetching binutils-esp32ulp examples" + echo "Fetching binutils-gdb (esp32ulp) examples" log_file=log/fetch-binutils.log git clone --depth 1 \ - https://github.com/espressif/binutils-esp32ulp.git 1>$log_file 2>&1 + -b esp32ulp-elf-v2.35_20220830 \ + https://github.com/espressif/binutils-gdb.git 1>$log_file 2>&1 } +REUSE_DEFINES_DB=0 + build_defines_db() { + local cpu=$1 local defines_db=defines.db + local defines_db_cpu=defines.$cpu.db - if [ "$1" = "-r" ] && [ -s "${defines_db}" ]; then + if [ "$REUSE_DEFINES_DB" = 1 ] && [ -s "${defines_db_cpu}" ]; then # reuse existing defines.db + echo "Reusing existing defines DB for cpu $cpu" + cp ${defines_db_cpu} ${defines_db} return fi - echo "Building defines DB from include files" - log_file=log/build_defines_db.log + echo "Building defines DB from $cpu include files" + log_file=log/build_defines_db.$cpu.log rm -f "${defines_db}" micropython -m esp32_ulp.parse_to_db \ - esp-idf/components/soc/esp32/include/soc/*.h \ + esp-idf/components/soc/$cpu/include/soc/*.h \ + esp-idf/components/soc/$cpu/register/soc/*.h \ esp-idf/components/esp_common/include/*.h 1>$log_file + + # cache defines.db + cp ${defines_db} ${defines_db_cpu} } calc_file_hash() { @@ -61,9 +78,9 @@ patch_test() { local test_name=$1 local out_file="${test_name}.tmp" - if [ "${test_name}" = esp32ulp_jumpr ]; then + if [[ "${test_name}" =~ ^(esp32ulp_jumpr|esp32s2ulp_jumpr|esp32s2ulp_jump)$ ]]; then ( - cd binutils-esp32ulp/gas/testsuite/gas/esp32ulp/esp32 + cd binutils-gdb/gas/testsuite/gas/esp32ulp/$cpu cp ${test_name}.s ${out_file} echo -e "\tPatching test to work around binutils-esp32ulp .global bug" cat >> ${out_file} < ${out_file} - echo -e "\tPatching test to work around binutils-esp32ulp .global bug" + echo -e "\tPatching test to work around binutils-gdb (esp32ulp) .global bug" cat >> ${out_file} <> ${out_file} + ) + return 0 fi return 1 # nothing was patched @@ -97,65 +122,97 @@ make_log_dir fetch_esp_idf fetch_ulptool_examples fetch_binutils_esp32ulp_examples -build_defines_db $1 -for src_file in ulptool/src/ulp_examples/*/*.s binutils-esp32ulp/gas/testsuite/gas/esp32ulp/esp32/*.s; do +run_tests_for_cpu() { + local cpu=$1 + echo "Testing for CPU: $cpu" + build_defines_db $cpu + + LIST=$(echo binutils-gdb/gas/testsuite/gas/esp32ulp/$cpu/*.s) + if [ $cpu = esp32 ]; then + # append extra tests to test preprocessor + # examples have constants specific to ESP32 (original) + # so we only run these tests with cpu = esp32 + # these tests primarily test our preprocessor, which is + # cpu independent, so we do not need to run them + # per each cpu. + LIST=$(echo ulptool/src/ulp_examples/*/*.s $LIST) + fi + + for src_file in $LIST; do + + src_name="${src_file%.s}" + src_dir="${src_name%/*}" + + echo "Testing $src_file" - src_name="${src_file%.s}" - src_dir="${src_name%/*}" + test_name="${src_name##*/}" - echo "Testing $src_file" + # for now, skip files that contain unsupported things (macros) + for I in i2c i2c_dev stack i2c_wr test1 test_jumpr test_macro; do + if [ "${test_name}" = "$I" ]; then + echo -e "\tSkipping... not yet supported" + continue 2 + fi + done + + if [ "$cpu" = esp32s2 ]; then + if [ "${test_name}" = "hall_sensor" ]; then + echo -e "\tSkipping... not supported on $cpu" + continue 1 + fi + fi - test_name="${src_name##*/}" + # BEGIN: work around known issues with binutils-gdb (esp32ulp) + ulp_file="${src_name}.ulp" - # for now, skip files that contain unsupported things (macros) - for I in i2c i2c_dev stack i2c_wr test1 test_jumpr test_macro; do - if [ "${test_name}" = "$I" ]; then - echo -e "\tSkipping... not yet supported" - continue 2 + if patch_test ${test_name}; then + # switch to the patched file instead of original one + src_file="${src_dir}/${test_name}.tmp" + src_name="${src_file%.tmp}" + ulp_file="${src_name}.tmp.ulp" # when extension is not .s, micropython-esp32-ulp doesn't remove original extension + fi + # END: work around known issues with binutils-gdb (esp32ulp) + + echo -e "\tBuilding using micropython-esp32-ulp ($cpu)" + log_file="${src_name}.log" + micropython -m esp32_ulp -c $cpu $src_file 1>$log_file # generates $ulp_file + + pre_file="${src_name}.pre" + obj_file="${src_name}.o" + elf_file="${src_name}.elf" + bin_file="${src_name}.bin" + + echo -e "\tBuilding using binutils ($cpu)" + gcc -I esp-idf/components/soc/$cpu/include -I esp-idf/components/esp_common/include \ + -I esp-idf/components/soc/$cpu/register \ + -x assembler-with-cpp \ + -E -o ${pre_file} $src_file + esp32ulp-elf-as --mcpu=$cpu -o $obj_file ${pre_file} + esp32ulp-elf-ld -T esp32.ulp.ld -o $elf_file $obj_file + esp32ulp-elf-objcopy -O binary $elf_file $bin_file + + if ! diff $ulp_file $bin_file 1>/dev/null; then + echo -e "\tBuild outputs differ!" + echo "" + echo "Compatibility test failed for $src_file" + echo "micropython-esp32-ulp log:" + cat $log_file + echo "micropython-esp32-ulp output:" + xxd $ulp_file + echo "binutils output:" + xxd $bin_file + exit 1 + else + echo -e "\tBuild outputs match (sha1: $(calc_file_hash $ulp_file))" fi done + echo "" +} - # BEGIN: work around known issues with binutils-esp32ulp - ulp_file="${src_name}.ulp" +if [ "$1" = -r ]; then + REUSE_DEFINES_DB=1 +fi - if patch_test ${test_name}; then - # switch to the patched file instead of original one - src_file="${src_dir}/${test_name}.tmp" - src_name="${src_file%.tmp}" - ulp_file="${src_name}.tmp.ulp" # when extension is not .s, micropython-esp32-ulp doesn't remove original extension - fi - # END: work around known issues with binutils-esp32ulp - - echo -e "\tBuilding using micropython-esp32-ulp" - log_file="${src_name}.log" - micropython -m esp32_ulp $src_file 1>$log_file # generates $ulp_file - - pre_file="${src_name}.pre" - obj_file="${src_name}.o" - elf_file="${src_name}.elf" - bin_file="${src_name}.bin" - - echo -e "\tBuilding using binutils" - gcc -I esp-idf/components/soc/esp32/include -I esp-idf/components/esp_common/include \ - -x assembler-with-cpp \ - -E -o ${pre_file} $src_file - esp32ulp-elf-as -o $obj_file ${pre_file} - esp32ulp-elf-ld -T esp32.ulp.ld -o $elf_file $obj_file - esp32ulp-elf-objcopy -O binary $elf_file $bin_file - - if ! diff $ulp_file $bin_file 1>/dev/null; then - echo -e "\tBuild outputs differ!" - echo "" - echo "Compatibility test failed for $src_file" - echo "micropython-esp32-ulp log:" - cat $log_file - echo "micropython-esp32-ulp output:" - xxd $ulp_file - echo "binutils output:" - xxd $bin_file - exit 1 - else - echo -e "\tBuild outputs match (sha1: $(calc_file_hash $ulp_file))" - fi -done +run_tests_for_cpu esp32 +run_tests_for_cpu esp32s2 diff --git a/tests/03_disassembler_tests.sh b/tests/03_disassembler_tests.sh new file mode 100755 index 0000000..c32a201 --- /dev/null +++ b/tests/03_disassembler_tests.sh @@ -0,0 +1,91 @@ +#!/bin/bash +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +set -e + +test_disassembling_a_file() { + local cpu=$1 + local verbose + if [ "$2" == verbose ]; then + verbose=-v + echo -e "Testing disassembling a file in VERBOSE mode" + else + echo -e "Testing disassembling a file in NORMAL mode" + fi + + testname=all_opcodes + fixture=fixtures/${testname}.${cpu}.S + echo -e "\tBuilding $fixture using micropython-esp32-ulp ($cpu)" + + log_file="${testname}.log" + ulp_file="fixtures/${testname}.${cpu}.ulp" + micropython -m esp32_ulp -c $cpu $fixture 1>$log_file # generates $ulp_file + + lst_file="${testname}.$cpu.lst" + lst_file_fixture=fixtures/${testname}${verbose}.$cpu.lst + echo -e "\tDisassembling $ulp_file using micropython-esp32-ulp disassembler ($cpu)" + micropython -m tools.disassemble -c $cpu $verbose $ulp_file > $lst_file + + if ! diff $lst_file_fixture $lst_file 1>/dev/null; then + echo -e "\tDisassembled output differs from expected output!" + echo "" + echo "Disassembly test failed for $fixture" + echo "micropython-esp32-ulp log:" + cat $log_file + echo "Diff of disassembly: expected vs actual" + diff -u $lst_file_fixture $lst_file + fi +} + +test_disassembling_a_manual_sequence() { + local cpu=$1 + local verbose + if [ "$2" == verbose ]; then + verbose=-v + echo -e "Testing disassembling a manual byte sequence in VERBOSE mode" + else + echo -e "Testing disassembling a manual byte sequence in NORMAL mode" + fi + + if [ "$cpu" == "esp32s2" ]; then + sequence="e1af 8c74 8101 0068 2705 cc19 0005 681d 0000 00a0 0000 0078" + else + sequence="e1af 8c72 0100 0068 2705 cc19 0005 681d 0000 00a0 0000 0074" + fi + + lst_file="manual_bytes.$cpu.lst" + lst_file_fixture=fixtures/manual_bytes${verbose}.$cpu.lst + echo -e "\tDisassembling manual byte sequence using micropython-esp32-ulp disassembler ($cpu)" + micropython -m tools.disassemble -c $cpu $verbose -m $sequence> $lst_file + + if ! diff $lst_file_fixture $lst_file 1>/dev/null; then + echo -e "\tDisassembled output differs from expected output!" + echo "" + echo "Disassembly test failed for manual byte sequence" + echo "Diff of disassembly: expected vs actual" + diff -u $lst_file_fixture $lst_file + fi +} + +# esp32 +echo "Testing for CPU: esp32" +test_disassembling_a_file esp32 +test_disassembling_a_file esp32 verbose + +test_disassembling_a_manual_sequence esp32 +test_disassembling_a_manual_sequence esp32 verbose + +echo "" + +# esp32s2 +echo "Testing for CPU: esp32s2" +test_disassembling_a_file esp32s2 +test_disassembling_a_file esp32s2 verbose + +test_disassembling_a_manual_sequence esp32s2 +test_disassembling_a_manual_sequence esp32s2 verbose diff --git a/tests/assemble.py b/tests/assemble.py index c17bbce..cfd1baa 100644 --- a/tests/assemble.py +++ b/tests/assemble.py @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + from esp32_ulp.assemble import Assembler, TEXT, DATA, BSS, REL, ABS from esp32_ulp.assemble import SymbolTable from esp32_ulp.nocomment import remove_comments diff --git a/tests/compat/alu.S b/tests/compat/alu.S index 02f873e..ddb5110 100644 --- a/tests/compat/alu.S +++ b/tests/compat/alu.S @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + .text and r1, r2, r3 diff --git a/tests/compat/expr.S b/tests/compat/expr.S index 3650623..a92b1c6 100644 --- a/tests/compat/expr.S +++ b/tests/compat/expr.S @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + # common example of real world code using expressions .set adc_channel, 6 @@ -46,3 +53,11 @@ exit: move r3, 0x1234 & ~2 move r3, 42|4&0xf # 46 (4&0xf is evaluated first) move r3, (42|4)&0xf # 14 (42|4 is evaluated first) + +# --- +# test that expressions accept hex characters in either upper or lower case + move r3, 0xaa - 1 + move r3, 0xBB - 1 + move r3, 0xCc - 1 + move r3, 0Xdd - 1 + move r3, 0XEF - 1 diff --git a/tests/compat/fixes.S b/tests/compat/fixes.S index 3b33a78..b3dc648 100644 --- a/tests/compat/fixes.S +++ b/tests/compat/fixes.S @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + # This file tests various fixes to the assembler, # to ensure the binary output matches that of binutils. # a) support for left-aligned directives (e.g. .set without preceding whitespace) diff --git a/tests/compat/io.S b/tests/compat/io.S index ea66a87..47cab33 100644 --- a/tests/compat/io.S +++ b/tests/compat/io.S @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + .text reg_rd 0x3ff48000, 7, 0 diff --git a/tests/compat/jumps.S b/tests/compat/jumps.S index ebf0543..27283e1 100644 --- a/tests/compat/jumps.S +++ b/tests/compat/jumps.S @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + .text .set const, 3 .global const # exporting symbol is required for binutils, not important for micropython-esp32-ulp diff --git a/tests/compat/loadstore.esp32s2.S b/tests/compat/loadstore.esp32s2.S new file mode 100644 index 0000000..cb45c42 --- /dev/null +++ b/tests/compat/loadstore.esp32s2.S @@ -0,0 +1,38 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +.set offs, 0x20 +.set lab1, 0x01 + +.text +LDL R1, R2, 0x20 +LDL R1, R2, offs +LDH R1, R2, 0x20 +LDH R1, R2, offs + +STL R1, R2, 0x20 +STL R1, R2, offs +STL R1, R2, offs, 1 +STL R1, R2, offs, lab1 + +STH R1, R2, 0x20 +STH R1, R2, offs +STH R1, R2, offs, 1 +STH R1, R2, offs, lab1 + +ST32 R1, R2, 0x10, 1 +ST32 R1, R2, offs, lab1 + +STI32 R1, R2, 1 +STI32 R1, R2, lab1 + +STI R1, R2 +STI R1, R2, 1 +STI R1, R2, lab1 + +STO 0x20 +STO offs diff --git a/tests/compat/memory.S b/tests/compat/memory.S index d8d57b8..d7ec2a2 100644 --- a/tests/compat/memory.S +++ b/tests/compat/memory.S @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + .text ld r0, r1, 0 diff --git a/tests/compat/preprocess_simple.S b/tests/compat/preprocess_simple.S index b6a61e8..17ea020 100644 --- a/tests/compat/preprocess_simple.S +++ b/tests/compat/preprocess_simple.S @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + #define GPIO 2 #define BASE 0x100 #define ADDR (BASE + GPIO) diff --git a/tests/compat/reg.esp32.S b/tests/compat/reg.esp32.S new file mode 100644 index 0000000..39ce461 --- /dev/null +++ b/tests/compat/reg.esp32.S @@ -0,0 +1,22 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +#include "soc/rtc_cntl_reg.h" +#include "soc/soc_ulp.h" + + reg_rd 0x012, 1, 2 + reg_rd 0x234, 3, 4 + reg_rd 0x345, 5, 6 + + reg_wr 0x012, 1, 2, 1 + reg_wr 0x234, 3, 4, 1 + reg_wr 0x345, 5, 6, 1 + + WRITE_RTC_REG(0x3ff484a8, 1, 2, 3) + READ_RTC_REG(0x3ff484a8, 1, 2) + WRITE_RTC_REG(0x3ff48904, 1, 2, 3) + READ_RTC_REG(0x3ff48904, 1, 2) diff --git a/tests/compat/reg.esp32s2.S b/tests/compat/reg.esp32s2.S new file mode 100644 index 0000000..cd6b072 --- /dev/null +++ b/tests/compat/reg.esp32s2.S @@ -0,0 +1,22 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +#include "soc/rtc_cntl_reg.h" +#include "soc/soc_ulp.h" + + reg_rd 0x012, 1, 2 + reg_rd 0x234, 3, 4 + reg_rd 0x345, 5, 6 + + reg_wr 0x012, 1, 2, 1 + reg_wr 0x234, 3, 4, 1 + reg_wr 0x345, 5, 6, 1 + + WRITE_RTC_REG(0x3f4084a8, 1, 2, 3) + READ_RTC_REG(0x3f4084a8, 1, 2) + WRITE_RTC_REG(0x3f408904, 1, 2, 3) + READ_RTC_REG(0x3f408904, 1, 2) diff --git a/tests/compat/sections.S b/tests/compat/sections.S index ca448b8..50d41cc 100644 --- a/tests/compat/sections.S +++ b/tests/compat/sections.S @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + .text nop diff --git a/tests/compat/sleep.S b/tests/compat/sleep.S index 7638e59..132e3bf 100644 --- a/tests/compat/sleep.S +++ b/tests/compat/sleep.S @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + .text nop diff --git a/tests/compat/symbols.S b/tests/compat/symbols.S index 359fa15..92fa34c 100644 --- a/tests/compat/symbols.S +++ b/tests/compat/symbols.S @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + .text .set constant42, 42 diff --git a/tests/decode.py b/tests/decode.py new file mode 100644 index 0000000..eb74b95 --- /dev/null +++ b/tests/decode.py @@ -0,0 +1,592 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +from tools.decode import decode_instruction, get_instruction_fields +import esp32_ulp.opcodes as opcodes +import ubinascii + +tests = [] + + +def test(param): + tests.append(param) + + +def hex_to_int(sequence): + byte_sequence = ubinascii.unhexlify(sequence) + return int.from_bytes(byte_sequence, 'little') + + +def assert_decode(sequence, expected_struct, expected_name): + i = hex_to_int(sequence) + + ins, name = decode_instruction(i) + + assert ins is expected_struct, 'incorrect instruction struct' + assert name == expected_name, '%s != %s' % (name, expected_name) + + +def assert_decode_exception(sequence, expected_message): + i = hex_to_int(sequence) + + try: + decode_instruction(i) + except Exception as e: + assert str(e) == expected_message, str(e) + raised = True + else: + raised = False + + assert raised, 'Exception not raised' + + +def assert_decode_fields(sequence, expected_field_details): + i = hex_to_int(sequence) + + ins, _ = decode_instruction(i) + + actual_field_details = get_instruction_fields(ins) + + assert actual_field_details == expected_field_details, '\n- %s \n+ %s' % (actual_field_details, expected_field_details) + + +@test +def test_unknown_instruction(): + assert_decode_exception("10000001", 'Unknown instruction') + + +@test +def test_empty_instruction(): + assert_decode_exception("00000000", '') + + +# All hex sequences were generated using our assembler. +# Note: disassembled instructions always show field values according +# to what is actually encoded into the binary instruction, not as per +# original assembly code. +# For example in JUMP instructions in the source code one would +# specify jump offsets in bytes (e.g. 4 bytes) but in the actual +# instruction offset encoded in the binary instruction will be in +# words (1 word = 4 bytes). +# The disassembled instructions would therefore show as "JUMP 1" +# for what was originally "JUMP 4" in the source code.@test +@test +def test_all_instructions(): + # OPCODE_WR_REG = 1 + assert_decode("00000010", opcodes._wr_reg, 'REG_WR 0x0, 0, 0, 0') + + # OPCODE_RD_REG = 2 + assert_decode("00000020", opcodes._rd_reg, 'REG_RD 0x0, 0, 0') + + # OPCODE_I2C = 3 + assert_decode("00000030", opcodes._i2c, 'I2C_RD 0, 0, 0, 0') + assert_decode("00000038", opcodes._i2c, 'I2C_WR 0, 0, 0, 0') + + # OPCODE_DELAY = 4 + assert_decode("00000040", opcodes._delay, 'NOP') + assert_decode("01000040", opcodes._delay, 'WAIT 1') + + # OPCODE_ADC = 5 + assert_decode("00000050", opcodes._adc, 'ADC r0, 0, 0') + + # OPCODE_ST = 6 + assert_decode("00000068", opcodes._st, 'ST r0, r0, 0') + + # OPCODE_ALU = 7, SUB_OPCODE_ALU_REG + assert_decode("00000070", opcodes._alu_reg, 'ADD r0, r0, r0') + assert_decode("00002070", opcodes._alu_reg, 'SUB r0, r0, r0') + assert_decode("00004070", opcodes._alu_reg, 'AND r0, r0, r0') + assert_decode("00006070", opcodes._alu_reg, 'OR r0, r0, r0') + assert_decode("00008070", opcodes._alu_reg, "MOVE r0, r0") + assert_decode("0000a070", opcodes._alu_reg, 'LSH r0, r0, r0') + assert_decode("0000c070", opcodes._alu_reg, 'RSH r0, r0, r0') + + # OPCODE_ALU = 7, SUB_OPCODE_ALU_IMM + assert_decode("00000072", opcodes._alu_imm, 'ADD r0, r0, 0') + assert_decode("00002072", opcodes._alu_imm, 'SUB r0, r0, 0') + assert_decode("00004072", opcodes._alu_imm, 'AND r0, r0, 0') + assert_decode("00006072", opcodes._alu_imm, 'OR r0, r0, 0') + assert_decode("00008072", opcodes._alu_imm, "MOVE r0, 0") + assert_decode("0000a072", opcodes._alu_imm, 'LSH r0, r0, 0') + assert_decode("0000c072", opcodes._alu_imm, 'RSH r0, r0, 0') + + # OPCODE_ALU = 7, SUB_OPCODE_ALU_CNT + assert_decode("00004074", opcodes._alu_cnt, 'STAGE_RST') + assert_decode("00000074", opcodes._alu_cnt, 'STAGE_INC 0') + assert_decode("00002074", opcodes._alu_cnt, 'STAGE_DEC 0') + + # OPCODE_BRANCH = 8, SUB_OPCODE_BX (IMM) + assert_decode("00000080", opcodes._bx, 'JUMP 0') + assert_decode("00004080", opcodes._bx, 'JUMP 0, EQ') + assert_decode("00008080", opcodes._bx, 'JUMP 0, OV') + + # OPCODE_BRANCH = 8, SUB_OPCODE_BX (REG) + assert_decode("00002080", opcodes._bx, 'JUMP r0') + assert_decode("00006080", opcodes._bx, 'JUMP r0, EQ') + assert_decode("0000a080", opcodes._bx, 'JUMP r0, OV') + + # OPCODE_BRANCH = 8, SUB_OPCODE_BR + assert_decode("00000082", opcodes._br, 'JUMPR 0, 0, LT') + assert_decode("00000182", opcodes._br, 'JUMPR 0, 0, GE') + + # OPCODE_BRANCH = 8, SUB_OPCODE_BX + assert_decode("00000084", opcodes._bs, 'JUMPS 0, 0, LT') + assert_decode("00800084", opcodes._bs, 'JUMPS 0, 0, GE') + assert_decode("00000184", opcodes._bs, 'JUMPS 0, 0, LE') + + # OPCODE_END = 9, SUB_OPCODE_END + assert_decode("01000090", opcodes._end, 'WAKE') + + # OPCODE_END = 9, SUB_OPCODE_SLEEP + assert_decode("00000092", opcodes._sleep, 'SLEEP 0') + + # OPCODE_TSENS = 10 + assert_decode("000000a0", opcodes._tsens, 'TSENS r0, 0') + + # OPCODE_HALT = 11 + assert_decode("000000b0", opcodes._halt, 'HALT') + + # OPCODE_LD = 13 + assert_decode("000000d0", opcodes._ld, 'LD r0, r0, 0') + + +@test +def test_instruction_field_decoding(): + # OPCODE_WR_REG = 1 + assert_decode_fields("230d8810", [ # REG_WR 0x123, 1, 2, 3 + ('addr' , 35, ' (0x23)'), + ('data' , 3, ''), + ('high' , 1, ''), + ('low' , 2, ''), + ('opcode' , 1, ''), + ('periph_sel', 1, ''), + ]) + + # OPCODE_RD_REG = 2 + assert_decode_fields("21030421", [ # REG_RD 0x321, 2, 1 + ('addr' , 33, ' (0x21)'), + ('high' , 2, ''), + ('low' , 1, ''), + ('opcode' , 2, ''), + ('periph_sel', 3, ''), + ('unused' , 0, ''), + ]) + + # OPCODE_I2C = 3 + assert_decode_fields("03001130", [ # I2C_RD 3, 2, 1, 0 + ('data' , 0, ''), + ('high' , 2, ''), + ('i2c_sel' , 0, ''), + ('low' , 1, ''), + ('opcode' , 3, ''), + ('rw' , 0, ''), + ('sub_addr' , 3, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("00011339", [ # I2C_WR 0, 2, 3, 4 + ('data' , 1, ''), + ('high' , 2, ''), + ('i2c_sel' , 4, ''), + ('low' , 3, ''), + ('opcode' , 3, ''), + ('rw' , 1, ''), + ('sub_addr' , 0, ''), + ('unused' , 0, ''), + ]) + + # OPCODE_DELAY = 4 + assert_decode_fields("00000040", [ # NOP + ('cycles' , 0, ''), + ('opcode' , 4, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("07000040", [ # WAIT 7 + ('cycles' , 7, ''), + ('opcode' , 4, ''), + ('unused' , 0, ''), + ]) + + # OPCODE_ADC = 5 + assert_decode_fields("07000050", [ # ADC r3, 1, 0 + ('cycles' , 0, ''), + ('dreg' , 3, ''), + ('mux' , 1, ''), + ('opcode' , 5, ''), + ('sar_sel' , 0, ''), + ('unused1' , 0, ''), + ('unused2' , 0, ''), + ]) + + # OPCODE_ST = 6 + assert_decode_fields("0b000068", [ # ST r3, r2, 0 + ('dreg' , 2, ''), + ('offset' , 0, ''), + ('opcode' , 6, ''), + ('sreg' , 3, ''), + ('sub_opcode', 4, ''), + ('unused1' , 0, ''), + ('unused2' , 0, ''), + ]) + + # OPCODE_ALU = 7, SUB_OPCODE_ALU_REG + assert_decode_fields("06000070", [ # ADD r2, r1, r0 + ('dreg' , 2, ''), + ('opcode' , 7, ''), + ('sel' , 0, ' (ADD)'), + ('sreg' , 1, ''), + ('sub_opcode', 0, ''), + ('treg' , 0, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("06002070", [ # SUB r2, r1, r0 + ('dreg' , 2, ''), + ('opcode' , 7, ''), + ('sel' , 1, ' (SUB)'), + ('sreg' , 1, ''), + ('sub_opcode', 0, ''), + ('treg' , 0, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("06004070", [ # AND r2, r1, r0 + ('dreg' , 2, ''), + ('opcode' , 7, ''), + ('sel' , 2, ' (AND)'), + ('sreg' , 1, ''), + ('sub_opcode', 0, ''), + ('treg' , 0, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("06006070", [ # OR r2, r1, r0 + ('dreg' , 2, ''), + ('opcode' , 7, ''), + ('sel' , 3, ' (OR)'), + ('sreg' , 1, ''), + ('sub_opcode', 0, ''), + ('treg' , 0, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("0600a070", [ # LSH r2, r1, r0 + ('dreg' , 2, ''), + ('opcode' , 7, ''), + ('sel' , 5, ' (LSH)'), + ('sreg' , 1, ''), + ('sub_opcode', 0, ''), + ('treg' , 0, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("0600c070", [ # RSH r2, r1, r0 + ('dreg' , 2, ''), + ('opcode' , 7, ''), + ('sel' , 6, ' (RSH)'), + ('sreg' , 1, ''), + ('sub_opcode', 0, ''), + ('treg' , 0, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("06000070", [ # ADD r2, r1, r0 + ('dreg' , 2, ''), + ('opcode' , 7, ''), + ('sel' , 0, ' (ADD)'), + ('sreg' , 1, ''), + ('sub_opcode', 0, ''), + ('treg' , 0, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("06002070", [ # SUB r2, r1, r0 + ('dreg' , 2, ''), + ('opcode' , 7, ''), + ('sel' , 1, ' (SUB)'), + ('sreg' , 1, ''), + ('sub_opcode', 0, ''), + ('treg' , 0, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("06004070", [ # AND r2, r1, r0 + ('dreg' , 2, ''), + ('opcode' , 7, ''), + ('sel' , 2, ' (AND)'), + ('sreg' , 1, ''), + ('sub_opcode', 0, ''), + ('treg' , 0, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("06006070", [ # OR r2, r1, r0 + ('dreg' , 2, ''), + ('opcode' , 7, ''), + ('sel' , 3, ' (OR)'), + ('sreg' , 1, ''), + ('sub_opcode', 0, ''), + ('treg' , 0, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("16008070", [ # MOVE r2, r1 + ('dreg' , 2, ''), + ('opcode' , 7, ''), + ('sel' , 4, ' (MOVE)'), + ('sreg' , 1, ''), + ('sub_opcode', 0, ''), + ('treg' , 1, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("0600a070", [ # LSH r2, r1, r0 + ('dreg' , 2, ''), + ('opcode' , 7, ''), + ('sel' , 5, ' (LSH)'), + ('sreg' , 1, ''), + ('sub_opcode', 0, ''), + ('treg' , 0, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("0600c070", [ # RSH r2, r1, r0 + ('dreg' , 2, ''), + ('opcode' , 7, ''), + ('sel' , 6, ' (RSH)'), + ('sreg' , 1, ''), + ('sub_opcode', 0, ''), + ('treg' , 0, ''), + ('unused' , 0, ''), + ]) + + # OPCODE_ALU = 7, SUB_OPCODE_ALU_IMM + assert_decode_fields("06000072", [ # ADD r2, r1, 0 + ('dreg' , 2, ''), + ('imm' , 0, ''), + ('opcode' , 7, ''), + ('sel' , 0, ' (ADD)'), + ('sreg' , 1, ''), + ('sub_opcode', 1, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("06002072", [ # SUB r2, r1, 0 + ('dreg' , 2, ''), + ('imm' , 0, ''), + ('opcode' , 7, ''), + ('sel' , 1, ' (SUB)'), + ('sreg' , 1, ''), + ('sub_opcode', 1, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("06004072", [ # AND r2, r1, 0 + ('dreg' , 2, ''), + ('imm' , 0, ''), + ('opcode' , 7, ''), + ('sel' , 2, ' (AND)'), + ('sreg' , 1, ''), + ('sub_opcode', 1, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("06006072", [ # OR r2, r1, 0 + ('dreg' , 2, ''), + ('imm' , 0, ''), + ('opcode' , 7, ''), + ('sel' , 3, ' (OR)'), + ('sreg' , 1, ''), + ('sub_opcode', 1, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("01008072", [ # MOVE r1, 0 + ('dreg' , 1, ''), + ('imm' , 0, ''), + ('opcode' , 7, ''), + ('sel' , 4, ' (MOVE)'), + ('sreg' , 0, ''), + ('sub_opcode', 1, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("0600a072", [ # LSH r2, r1, 0 + ('dreg' , 2, ''), + ('imm' , 0, ''), + ('opcode' , 7, ''), + ('sel' , 5, ' (LSH)'), + ('sreg' , 1, ''), + ('sub_opcode', 1, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("0600c072", [ # RSH r2, r1, 0 + ('dreg' , 2, ''), + ('imm' , 0, ''), + ('opcode' , 7, ''), + ('sel' , 6, ' (RSH)'), + ('sreg' , 1, ''), + ('sub_opcode', 1, ''), + ('unused' , 0, ''), + ]) + + # OPCODE_ALU = 7, SUB_OPCODE_ALU_CNT + assert_decode_fields("00004074", [ # STAGE_RST + ('imm' , 0, ''), + ('opcode' , 7, ''), + ('sel' , 2, ' (STAGE_RST)'), + ('sub_opcode', 2, ''), + ('unused1' , 0, ''), + ('unused2' , 0, ''), + ]) + assert_decode_fields("70000074", [ # STAGE_INC 7 + ('imm' , 7, ''), + ('opcode' , 7, ''), + ('sel' , 0, ' (STAGE_INC)'), + ('sub_opcode', 2, ''), + ('unused1' , 0, ''), + ('unused2' , 0, ''), + ]) + assert_decode_fields("30002074", [ # STAGE_DEC 3 + ('imm' , 3, ''), + ('opcode' , 7, ''), + ('sel' , 1, ' (STAGE_DEC)'), + ('sub_opcode', 2, ''), + ('unused1' , 0, ''), + ('unused2' , 0, ''), + ]) + + # OPCODE_BRANCH = 8, SUB_OPCODE_BX + assert_decode_fields("00002080", [ # JUMP r0 + ('addr' , 0, ''), + ('dreg' , 0, ''), + ('opcode' , 8, ''), + ('reg' , 1, ''), + ('sub_opcode', 0, ''), + ('type' , 0, ' (--)'), + ('unused' , 0, ''), + ]) + assert_decode_fields("01006080", [ # JUMP r1, EQ + ('addr' , 0, ''), + ('dreg' , 1, ''), + ('opcode' , 8, ''), + ('reg' , 1, ''), + ('sub_opcode', 0, ''), + ('type' , 1, ' (EQ)'), + ('unused' , 0, ''), + ]) + assert_decode_fields("0200a080", [ # JUMP r2, OV + ('addr' , 0, ''), + ('dreg' , 2, ''), + ('opcode' , 8, ''), + ('reg' , 1, ''), + ('sub_opcode', 0, ''), + ('type' , 2, ' (OV)'), + ('unused' , 0, ''), + ]) + assert_decode_fields("00000080", [ # JUMP 0 + ('addr' , 0, ''), + ('dreg' , 0, ''), + ('opcode' , 8, ''), + ('reg' , 0, ''), + ('sub_opcode', 0, ''), + ('type' , 0, ' (--)'), + ('unused' , 0, ''), + ]) + assert_decode_fields("04004080", [ # JUMP 1, EQ + ('addr' , 1, ''), + ('dreg' , 0, ''), + ('opcode' , 8, ''), + ('reg' , 0, ''), + ('sub_opcode', 0, ''), + ('type' , 1, ' (EQ)'), + ('unused' , 0, ''), + ]) + assert_decode_fields("08008080", [ # JUMP 2, OV + ('addr' , 2, ''), + ('dreg' , 0, ''), + ('opcode' , 8, ''), + ('reg' , 0, ''), + ('sub_opcode', 0, ''), + ('type' , 2, ' (OV)'), + ('unused' , 0, ''), + ]) + + # OPCODE_BRANCH = 8, SUB_OPCODE_BR + assert_decode_fields("01000082", [ # JUMPR 0, 1, LT + ('cmp' , 0, ' (LT)'), + ('imm' , 1, ''), + ('offset' , 0, ''), + ('opcode' , 8, ''), + ('sign' , 0, ''), + ('sub_opcode', 1, ''), + ]) + assert_decode_fields("05000382", [ # JUMPR 1, 5, GE + ('cmp' , 1, ' (GE)'), + ('imm' , 5, ''), + ('offset' , 1, ''), + ('opcode' , 8, ''), + ('sign' , 0, ''), + ('sub_opcode', 1, ''), + ]) + + # OPCODE_BRANCH = 8, SUB_OPCODE_BS + assert_decode_fields("01000084", [ # JUMPS 0, 1, LT + ('cmp' , 0, ' (LT)'), + ('imm' , 1, ''), + ('offset' , 0, ''), + ('opcode' , 8, ''), + ('sign' , 0, ''), + ('sub_opcode', 2, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("05800284", [ # JUMPS 1, 5, GE + ('cmp' , 1, ' (GE)'), + ('imm' , 5, ''), + ('offset' , 1, ''), + ('opcode' , 8, ''), + ('sign' , 0, ''), + ('sub_opcode', 2, ''), + ('unused' , 0, ''), + ]) + assert_decode_fields("09000584", [ # JUMPS 2, 9, LE + ('cmp' , 2, ' (LE)'), + ('imm' , 9, ''), + ('offset' , 2, ''), + ('opcode' , 8, ''), + ('sign' , 0, ''), + ('sub_opcode', 2, ''), + ('unused' , 0, ''), + ]) + + # OPCODE_END = 9, SUB_OPCODE_END + assert_decode_fields("01000090", [ # WAKE + ('opcode' , 9, ''), + ('sub_opcode', 0, ''), + ('unused' , 0, ''), + ('wakeup' , 1, ''), + ]) + + # OPCODE_END = 9, SUB_OPCODE_SLEEP + assert_decode_fields("07000092", [ # SLEEP 7 + ('cycle_sel' , 7, ''), + ('opcode' , 9, ''), + ('sub_opcode', 1, ''), + ('unused' , 0, ''), + ]) + + # OPCODE_TSENS = 10 + assert_decode_fields("090000a0", [ # TSENS r0, 0 + ('delay' , 2, ''), + ('dreg' , 1, ''), + ('opcode' , 10, ' (0x0a)'), + ('unused' , 0, ''), + ]) + + # OPCODE_HALT = 11 + assert_decode_fields("000000b0", [ # HALT + ('opcode' , 11, ' (0x0b)'), + ('unused' , 0, ''), + ]) + + # OPCODE_LD = 13 + assert_decode_fields("060000d0", [ # LD r2, r1, 0 + ('dreg' , 2, ''), + ('offset' , 0, ''), + ('opcode' , 13, ' (0x0d)'), + ('sreg' , 1, ''), + ('unused1' , 0, ''), + ('unused2' , 0, ''), + ]) + + +if __name__ == '__main__': + # run all methods marked with @test + for t in tests: + t() diff --git a/tests/decode_s2.py b/tests/decode_s2.py new file mode 100644 index 0000000..52838ca --- /dev/null +++ b/tests/decode_s2.py @@ -0,0 +1,178 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +from tools.decode_s2 import decode_instruction, get_instruction_fields +import esp32_ulp.opcodes_s2 as opcodes +import ubinascii + +tests = [] + + +def test(param): + tests.append(param) + + +def hex_to_int(sequence): + byte_sequence = ubinascii.unhexlify(sequence) + return int.from_bytes(byte_sequence, 'little') + + +def assert_decode(sequence, expected_struct, expected_name): + i = hex_to_int(sequence) + + ins, name = decode_instruction(i) + + assert name == expected_name, '%s != %s' % (name, expected_name) + assert ins is expected_struct, 'incorrect instruction struct (%s, %s)' % (sequence, name) + + +def assert_decode_exception(sequence, expected_message): + i = hex_to_int(sequence) + + try: + decode_instruction(i) + except Exception as e: + assert str(e) == expected_message, str(e) + raised = True + else: + raised = False + + assert raised, 'Exception not raised' + + +def assert_decode_fields(sequence, expected_field_details): + i = hex_to_int(sequence) + + ins, _ = decode_instruction(i) + + actual_field_details = get_instruction_fields(ins) + + assert actual_field_details == expected_field_details, '\n- %s \n+ %s' % (actual_field_details, expected_field_details) + + +@test +def test_unknown_instruction(): + assert_decode_exception("10000001", 'Unknown instruction') + + +@test +def test_empty_instruction(): + assert_decode_exception("00000000", '') + + +# All hex sequences were generated using our assembler. +# Note: disassembled instructions always show field values according +# to what is actually encoded into the binary instruction, not as per +# original assembly code. +# For example in JUMP instructions in the source code one would +# specify jump offsets in bytes (e.g. 4 bytes) but in the actual +# instruction offset encoded in the binary instruction will be in +# words (1 word = 4 bytes). +# The disassembled instructions would therefore show as "JUMP 1" +# for what was originally "JUMP 4" in the source code.@test +@test +def test_all_instructions(): + # OPCODE_WR_REG = 1 + assert_decode("00000010", opcodes._wr_reg, 'REG_WR 0x0, 0, 0, 0') + + # OPCODE_RD_REG = 2 + assert_decode("00000020", opcodes._rd_reg, 'REG_RD 0x0, 0, 0') + + # OPCODE_I2C = 3 + assert_decode("00000030", opcodes._i2c, 'I2C_RD 0, 0, 0, 0') + assert_decode("00000038", opcodes._i2c, 'I2C_WR 0, 0, 0, 0') + + # OPCODE_DELAY = 4 + assert_decode("00000040", opcodes._delay, 'NOP') + assert_decode("01000040", opcodes._delay, 'WAIT 1') + + # OPCODE_ADC = 5 + assert_decode("00000050", opcodes._adc, 'ADC r0, 0, 0') + + # OPCODE_ST = 6, SUB_OPCODE_ST + assert_decode("80010068", opcodes._st, 'ST r0, r0, 0') + assert_decode("c0010068", opcodes._st, 'STH r0, r0, 0') + assert_decode("90000068", opcodes._st, 'STL r0, r0, 0, 1') + assert_decode("d0000068", opcodes._st, 'STH r0, r0, 0, 1') + assert_decode("00000068", opcodes._st, 'ST32 r0, r0, 0, 0') + assert_decode("10000068", opcodes._st, 'ST32 r0, r0, 0, 1') + + # OPCODE_ST = 6, SUB_OPCODE_ST_AUTO + assert_decode("80010062", opcodes._st, 'STI r0, r0') + assert_decode("90000062", opcodes._st, 'STI r0, r0, 1') + assert_decode("00000062", opcodes._st, 'STI32 r0, r0, 0') + assert_decode("10000062", opcodes._st, 'STI32 r0, r0, 1') + + # OPCODE_ST = 6, SUB_OPCODE_ST_OFFSET + assert_decode("00000064", opcodes._st, 'STO 0') + assert_decode("00040064", opcodes._st, 'STO 1') + + # OPCODE_ALU = 7, SUB_OPCODE_ALU_REG + assert_decode("00000070", opcodes._alu_reg, 'ADD r0, r0, r0') + assert_decode("00002070", opcodes._alu_reg, 'SUB r0, r0, r0') + assert_decode("00004070", opcodes._alu_reg, 'AND r0, r0, r0') + assert_decode("00006070", opcodes._alu_reg, 'OR r0, r0, r0') + assert_decode("00008070", opcodes._alu_reg, "MOVE r0, r0") + assert_decode("0000a070", opcodes._alu_reg, 'LSH r0, r0, r0') + assert_decode("0000c070", opcodes._alu_reg, 'RSH r0, r0, r0') + + # OPCODE_ALU = 7, SUB_OPCODE_ALU_IMM + assert_decode("00000074", opcodes._alu_imm, 'ADD r0, r0, 0') + assert_decode("00002074", opcodes._alu_imm, 'SUB r0, r0, 0') + assert_decode("00004074", opcodes._alu_imm, 'AND r0, r0, 0') + assert_decode("00006074", opcodes._alu_imm, 'OR r0, r0, 0') + assert_decode("00008074", opcodes._alu_imm, "MOVE r0, 0") + assert_decode("0000a074", opcodes._alu_imm, 'LSH r0, r0, 0') + assert_decode("0000c074", opcodes._alu_imm, 'RSH r0, r0, 0') + + # OPCODE_ALU = 7, SUB_OPCODE_ALU_CNT + assert_decode("00004078", opcodes._alu_cnt, 'STAGE_RST') + assert_decode("00000078", opcodes._alu_cnt, 'STAGE_INC 0') + assert_decode("00002078", opcodes._alu_cnt, 'STAGE_DEC 0') + + # OPCODE_BRANCH = 8, SUB_OPCODE_BX (IMM) + assert_decode("00000084", opcodes._bx, 'JUMP 0') + assert_decode("00004084", opcodes._bx, 'JUMP 0, EQ') + assert_decode("00008084", opcodes._bx, 'JUMP 0, OV') + + # OPCODE_BRANCH = 8, SUB_OPCODE_BX (REG) + assert_decode("00002084", opcodes._bx, 'JUMP r0') + assert_decode("00006084", opcodes._bx, 'JUMP r0, EQ') + assert_decode("0000a084", opcodes._bx, 'JUMP r0, OV') + + # OPCODE_BRANCH = 8, SUB_OPCODE_BR + assert_decode("00000080", opcodes._b, 'JUMPR 0, 0, LT') + assert_decode("00000180", opcodes._b, 'JUMPR 0, 0, GT') + assert_decode("00000280", opcodes._b, 'JUMPR 0, 0, EQ') + + # OPCODE_BRANCH = 8, SUB_OPCODE_BX + assert_decode("00800088", opcodes._bs, 'JUMPS 0, 0, LT') + assert_decode("00800188", opcodes._bs, 'JUMPS 0, 0, GT') + assert_decode("00000288", opcodes._bs, 'JUMPS 0, 0, EQ') + assert_decode("00800288", opcodes._bs, 'JUMPS 0, 0, LE') + assert_decode("00800388", opcodes._bs, 'JUMPS 0, 0, GE') + + # OPCODE_END = 9, SUB_OPCODE_END + assert_decode("01000090", opcodes._end, 'WAKE') + + # OPCODE_END = 9, SUB_OPCODE_SLEEP + ###assert_decode("01000040", opcodes._end, 'SLEEP 1') ##TODO + + # OPCODE_TSENS = 10 + assert_decode("000000a0", opcodes._tsens, 'TSENS r0, 0') + + # OPCODE_HALT = 11 + assert_decode("000000b0", opcodes._halt, 'HALT') + + # OPCODE_LD = 13 + assert_decode("000000d0", opcodes._ld, 'LD r0, r0, 0') + + +if __name__ == '__main__': + # run all methods marked with @test + for t in tests: + t() diff --git a/tests/definesdb.py b/tests/definesdb.py index 5e2100c..975c288 100644 --- a/tests/definesdb.py +++ b/tests/definesdb.py @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + import os from esp32_ulp.definesdb import DefinesDB, DBNAME diff --git a/tests/fixtures/all_opcodes-v.esp32.lst b/tests/fixtures/all_opcodes-v.esp32.lst new file mode 100644 index 0000000..1e2ebf7 --- /dev/null +++ b/tests/fixtures/all_opcodes-v.esp32.lst @@ -0,0 +1,359 @@ +header +ULP magic : b'ulp\x00' (0x00706c75) +.text offset : 12 (0x0c) +.text size : 188 (0xbc) +.data offset : 200 (0xc8) +.data size : 8 (0x08) +.bss size : 0 (0x00) +---------------------------------------- +.text +0000 230d8810 REG_WR 0x123, 1, 2, 3 + addr = 35 (0x23) + data = 3 + high = 1 + low = 2 + opcode = 1 + periph_sel = 1 +0004 21030421 REG_RD 0x321, 2, 1 + addr = 33 (0x21) + high = 2 + low = 1 + opcode = 2 + periph_sel = 3 + unused = 0 +0008 03001130 I2C_RD 3, 2, 1, 0 + data = 0 + high = 2 + i2c_sel = 0 + low = 1 + opcode = 3 + rw = 0 + sub_addr = 3 + unused = 0 +000c 00011339 I2C_WR 0, 2, 3, 4 + data = 1 + high = 2 + i2c_sel = 4 + low = 3 + opcode = 3 + rw = 1 + sub_addr = 0 + unused = 0 +0010 00000040 NOP + cycles = 0 + opcode = 4 + unused = 0 +0014 07000040 WAIT 7 + cycles = 7 + opcode = 4 + unused = 0 +0018 07000050 ADC r3, 1, 0 + cycles = 0 + dreg = 3 + mux = 1 + opcode = 5 + sar_sel = 0 + unused1 = 0 + unused2 = 0 +001c 0b000068 ST r3, r2, 0 + dreg = 2 + offset = 0 + opcode = 6 + sreg = 3 + sub_opcode = 4 + unused1 = 0 + unused2 = 0 +0020 06000070 ADD r2, r1, r0 + dreg = 2 + opcode = 7 + sel = 0 (ADD) + sreg = 1 + sub_opcode = 0 + treg = 0 + unused = 0 +0024 06002070 SUB r2, r1, r0 + dreg = 2 + opcode = 7 + sel = 1 (SUB) + sreg = 1 + sub_opcode = 0 + treg = 0 + unused = 0 +0028 06004070 AND r2, r1, r0 + dreg = 2 + opcode = 7 + sel = 2 (AND) + sreg = 1 + sub_opcode = 0 + treg = 0 + unused = 0 +002c 06006070 OR r2, r1, r0 + dreg = 2 + opcode = 7 + sel = 3 (OR) + sreg = 1 + sub_opcode = 0 + treg = 0 + unused = 0 +0030 16008070 MOVE r2, r1 + dreg = 2 + opcode = 7 + sel = 4 (MOVE) + sreg = 1 + sub_opcode = 0 + treg = 1 + unused = 0 +0034 0600a070 LSH r2, r1, r0 + dreg = 2 + opcode = 7 + sel = 5 (LSH) + sreg = 1 + sub_opcode = 0 + treg = 0 + unused = 0 +0038 0600c070 RSH r2, r1, r0 + dreg = 2 + opcode = 7 + sel = 6 (RSH) + sreg = 1 + sub_opcode = 0 + treg = 0 + unused = 0 +003c 06000072 ADD r2, r1, 0 + dreg = 2 + imm = 0 + opcode = 7 + sel = 0 (ADD) + sreg = 1 + sub_opcode = 1 + unused = 0 +0040 06002072 SUB r2, r1, 0 + dreg = 2 + imm = 0 + opcode = 7 + sel = 1 (SUB) + sreg = 1 + sub_opcode = 1 + unused = 0 +0044 06004072 AND r2, r1, 0 + dreg = 2 + imm = 0 + opcode = 7 + sel = 2 (AND) + sreg = 1 + sub_opcode = 1 + unused = 0 +0048 06006072 OR r2, r1, 0 + dreg = 2 + imm = 0 + opcode = 7 + sel = 3 (OR) + sreg = 1 + sub_opcode = 1 + unused = 0 +004c 01008072 MOVE r1, 0 + dreg = 1 + imm = 0 + opcode = 7 + sel = 4 (MOVE) + sreg = 0 + sub_opcode = 1 + unused = 0 +0050 0600a072 LSH r2, r1, 0 + dreg = 2 + imm = 0 + opcode = 7 + sel = 5 (LSH) + sreg = 1 + sub_opcode = 1 + unused = 0 +0054 0600c072 RSH r2, r1, 0 + dreg = 2 + imm = 0 + opcode = 7 + sel = 6 (RSH) + sreg = 1 + sub_opcode = 1 + unused = 0 +0058 00004074 STAGE_RST + imm = 0 + opcode = 7 + sel = 2 (STAGE_RST) + sub_opcode = 2 + unused1 = 0 + unused2 = 0 +005c 70000074 STAGE_INC 7 + imm = 7 + opcode = 7 + sel = 0 (STAGE_INC) + sub_opcode = 2 + unused1 = 0 + unused2 = 0 +0060 30002074 STAGE_DEC 3 + imm = 3 + opcode = 7 + sel = 1 (STAGE_DEC) + sub_opcode = 2 + unused1 = 0 + unused2 = 0 +0064 00002080 JUMP r0 + addr = 0 + dreg = 0 + opcode = 8 + reg = 1 + sub_opcode = 0 + type = 0 (--) + unused = 0 +0068 01006080 JUMP r1, EQ + addr = 0 + dreg = 1 + opcode = 8 + reg = 1 + sub_opcode = 0 + type = 1 (EQ) + unused = 0 +006c 0200a080 JUMP r2, OV + addr = 0 + dreg = 2 + opcode = 8 + reg = 1 + sub_opcode = 0 + type = 2 (OV) + unused = 0 +0070 00000080 JUMP 0 + addr = 0 + dreg = 0 + opcode = 8 + reg = 0 + sub_opcode = 0 + type = 0 (--) + unused = 0 +0074 00004080 JUMP 0, EQ + addr = 0 + dreg = 0 + opcode = 8 + reg = 0 + sub_opcode = 0 + type = 1 (EQ) + unused = 0 +0078 00008080 JUMP 0, OV + addr = 0 + dreg = 0 + opcode = 8 + reg = 0 + sub_opcode = 0 + type = 2 (OV) + unused = 0 +007c 01000082 JUMPR 0, 1, LT + cmp = 0 (LT) + imm = 1 + offset = 0 + opcode = 8 + sign = 0 + sub_opcode = 1 +0080 06000382 JUMPR 1, 6, GE + cmp = 1 (GE) + imm = 6 + offset = 1 + opcode = 8 + sign = 0 + sub_opcode = 1 +0084 08000582 JUMPR 2, 8, GE + cmp = 1 (GE) + imm = 8 + offset = 2 + opcode = 8 + sign = 0 + sub_opcode = 1 +0088 07000582 JUMPR 2, 7, GE + cmp = 1 (GE) + imm = 7 + offset = 2 + opcode = 8 + sign = 0 + sub_opcode = 1 +008c 01000084 JUMPS 0, 1, LT + cmp = 0 (LT) + imm = 1 + offset = 0 + opcode = 8 + sign = 0 + sub_opcode = 2 + unused = 0 +0090 05000584 JUMPS 2, 5, LE + cmp = 2 (LE) + imm = 5 + offset = 2 + opcode = 8 + sign = 0 + sub_opcode = 2 + unused = 0 +0094 05800284 JUMPS 1, 5, GE + cmp = 1 (GE) + imm = 5 + offset = 1 + opcode = 8 + sign = 0 + sub_opcode = 2 + unused = 0 +0098 07000484 JUMPS 2, 7, LT + cmp = 0 (LT) + imm = 7 + offset = 2 + opcode = 8 + sign = 0 + sub_opcode = 2 + unused = 0 +009c 07000584 JUMPS 2, 7, LE + cmp = 2 (LE) + imm = 7 + offset = 2 + opcode = 8 + sign = 0 + sub_opcode = 2 + unused = 0 +00a0 09000784 JUMPS 3, 9, LE + cmp = 2 (LE) + imm = 9 + offset = 3 + opcode = 8 + sign = 0 + sub_opcode = 2 + unused = 0 +00a4 0b800884 JUMPS 4, 11, GE + cmp = 1 (GE) + imm = 11 (0x0b) + offset = 4 + opcode = 8 + sign = 0 + sub_opcode = 2 + unused = 0 +00a8 01000090 WAKE + opcode = 9 + sub_opcode = 0 + unused = 0 + wakeup = 1 +00ac 07000092 SLEEP 7 + cycle_sel = 7 + opcode = 9 + sub_opcode = 1 + unused = 0 +00b0 090000a0 TSENS r1, 2 + delay = 2 + dreg = 1 + opcode = 10 (0x0a) + unused = 0 +00b4 000000b0 HALT + opcode = 11 (0x0b) + unused = 0 +00b8 060000d0 LD r2, r1, 0 + dreg = 2 + offset = 0 + opcode = 13 (0x0d) + sreg = 1 + unused1 = 0 + unused2 = 0 +---------------------------------------- +.data +00bc 00000000 +00c0 fecadec0 diff --git a/tests/fixtures/all_opcodes-v.esp32s2.lst b/tests/fixtures/all_opcodes-v.esp32s2.lst new file mode 100644 index 0000000..a2ebb15 --- /dev/null +++ b/tests/fixtures/all_opcodes-v.esp32s2.lst @@ -0,0 +1,571 @@ +header +ULP magic : b'ulp\x00' (0x00706c75) +.text offset : 12 (0x0c) +.text size : 260 (0x104) +.data offset : 272 (0x110) +.data size : 8 (0x08) +.bss size : 0 (0x00) +---------------------------------------- +.text +0000 230d8810 REG_WR 0x123, 1, 2, 3 + addr = 35 (0x23) + data = 3 + high = 1 + low = 2 + opcode = 1 + periph_sel = 1 +0004 21030421 REG_RD 0x321, 2, 1 + addr = 33 (0x21) + high = 2 + low = 1 + opcode = 2 + periph_sel = 3 + unused = 0 +0008 03001130 I2C_RD 3, 2, 1, 0 + data = 0 + high = 2 + i2c_sel = 0 + low = 1 + opcode = 3 + rw = 0 + sub_addr = 3 + unused = 0 +000c 00011339 I2C_WR 0, 2, 3, 4 + data = 1 + high = 2 + i2c_sel = 4 + low = 3 + opcode = 3 + rw = 1 + sub_addr = 0 + unused = 0 +0010 00000040 NOP + cycles = 0 + opcode = 4 + unused = 0 +0014 07000040 WAIT 7 + cycles = 7 + opcode = 4 + unused = 0 +0018 07000050 ADC r3, 1, 0 + cycles = 0 + dreg = 3 + mux = 1 + opcode = 5 + sar_sel = 0 + unused1 = 0 + unused2 = 0 +001c 8b010068 ST r3, r2, 0 + dreg = 2 + offset = 0 + opcode = 6 + sreg = 3 + sub_opcode = 4 + unused1 = 0 + unused2 = 0 + label = 0 + upper = 0 + wr_way = 3 +0020 06000070 ADD r2, r1, r0 + dreg = 2 + opcode = 7 + sel = 0 (ADD) + sreg = 1 + sub_opcode = 0 + treg = 0 + unused1 = 0 + unused2 = 0 +0024 06002070 SUB r2, r1, r0 + dreg = 2 + opcode = 7 + sel = 1 (SUB) + sreg = 1 + sub_opcode = 0 + treg = 0 + unused1 = 0 + unused2 = 0 +0028 06004070 AND r2, r1, r0 + dreg = 2 + opcode = 7 + sel = 2 (AND) + sreg = 1 + sub_opcode = 0 + treg = 0 + unused1 = 0 + unused2 = 0 +002c 06006070 OR r2, r1, r0 + dreg = 2 + opcode = 7 + sel = 3 (OR) + sreg = 1 + sub_opcode = 0 + treg = 0 + unused1 = 0 + unused2 = 0 +0030 16008070 MOVE r2, r1 + dreg = 2 + opcode = 7 + sel = 4 (MOVE) + sreg = 1 + sub_opcode = 0 + treg = 1 + unused1 = 0 + unused2 = 0 +0034 0600a070 LSH r2, r1, r0 + dreg = 2 + opcode = 7 + sel = 5 (LSH) + sreg = 1 + sub_opcode = 0 + treg = 0 + unused1 = 0 + unused2 = 0 +0038 0600c070 RSH r2, r1, r0 + dreg = 2 + opcode = 7 + sel = 6 (RSH) + sreg = 1 + sub_opcode = 0 + treg = 0 + unused1 = 0 + unused2 = 0 +003c 06000074 ADD r2, r1, 0 + dreg = 2 + imm = 0 + opcode = 7 + sel = 0 (ADD) + sreg = 1 + sub_opcode = 1 + unused1 = 0 + unused2 = 0 +0040 06002074 SUB r2, r1, 0 + dreg = 2 + imm = 0 + opcode = 7 + sel = 1 (SUB) + sreg = 1 + sub_opcode = 1 + unused1 = 0 + unused2 = 0 +0044 06004074 AND r2, r1, 0 + dreg = 2 + imm = 0 + opcode = 7 + sel = 2 (AND) + sreg = 1 + sub_opcode = 1 + unused1 = 0 + unused2 = 0 +0048 06006074 OR r2, r1, 0 + dreg = 2 + imm = 0 + opcode = 7 + sel = 3 (OR) + sreg = 1 + sub_opcode = 1 + unused1 = 0 + unused2 = 0 +004c 01008074 MOVE r1, 0 + dreg = 1 + imm = 0 + opcode = 7 + sel = 4 (MOVE) + sreg = 0 + sub_opcode = 1 + unused1 = 0 + unused2 = 0 +0050 0600a074 LSH r2, r1, 0 + dreg = 2 + imm = 0 + opcode = 7 + sel = 5 (LSH) + sreg = 1 + sub_opcode = 1 + unused1 = 0 + unused2 = 0 +0054 0600c074 RSH r2, r1, 0 + dreg = 2 + imm = 0 + opcode = 7 + sel = 6 (RSH) + sreg = 1 + sub_opcode = 1 + unused1 = 0 + unused2 = 0 +0058 00004078 STAGE_RST + imm = 0 + opcode = 7 + sel = 2 (STAGE_RST) + sub_opcode = 2 + unused1 = 0 + unused2 = 0 +005c 70000078 STAGE_INC 7 + imm = 7 + opcode = 7 + sel = 0 (STAGE_INC) + sub_opcode = 2 + unused1 = 0 + unused2 = 0 +0060 30002078 STAGE_DEC 3 + imm = 3 + opcode = 7 + sel = 1 (STAGE_DEC) + sub_opcode = 2 + unused1 = 0 + unused2 = 0 +0064 00002084 JUMP r0 + addr = 0 + dreg = 0 + opcode = 8 + reg = 1 + sub_opcode = 1 + type = 0 (--) + unused1 = 0 + unused2 = 0 +0068 01006084 JUMP r1, EQ + addr = 0 + dreg = 1 + opcode = 8 + reg = 1 + sub_opcode = 1 + type = 1 (EQ) + unused1 = 0 + unused2 = 0 +006c 0200a084 JUMP r2, OV + addr = 0 + dreg = 2 + opcode = 8 + reg = 1 + sub_opcode = 1 + type = 2 (OV) + unused1 = 0 + unused2 = 0 +0070 00000084 JUMP 0 + addr = 0 + dreg = 0 + opcode = 8 + reg = 0 + sub_opcode = 1 + type = 0 (--) + unused1 = 0 + unused2 = 0 +0074 00004084 JUMP 0, EQ + addr = 0 + dreg = 0 + opcode = 8 + reg = 0 + sub_opcode = 1 + type = 1 (EQ) + unused1 = 0 + unused2 = 0 +0078 00008084 JUMP 0, OV + addr = 0 + dreg = 0 + opcode = 8 + reg = 0 + sub_opcode = 1 + type = 2 (OV) + unused1 = 0 + unused2 = 0 +007c 01000080 JUMPR 0, 1, LT + cmp = 0 (LT) + imm = 1 + offset = 0 + opcode = 8 + sign = 0 + sub_opcode = 0 +0080 05000580 JUMPR 1, 5, GT + cmp = 1 (GT) + imm = 5 + offset = 1 + opcode = 8 + sign = 0 + sub_opcode = 0 +0084 07000a80 JUMPR 2, 7, EQ + cmp = 2 (EQ) + imm = 7 + offset = 2 + opcode = 8 + sign = 0 + sub_opcode = 0 +0088 01800088 JUMPS 0, 1, LT + cmp = 1 (LT) + imm = 1 + offset = 0 + opcode = 8 + sign = 0 + sub_opcode = 2 + unused = 0 +008c 05801188 JUMPS 4, 5, GT + cmp = 3 (GT) + imm = 5 + offset = 4 + opcode = 8 + sign = 0 + sub_opcode = 2 + unused = 0 +0090 07002288 JUMPS 8, 7, EQ + cmp = 4 (EQ) + imm = 7 + offset = 8 + opcode = 8 + sign = 0 + sub_opcode = 2 + unused = 0 +0094 09803288 JUMPS 12, 9, LE + cmp = 5 (LE) + imm = 9 + offset = 12 (0x0c) + opcode = 8 + sign = 0 + sub_opcode = 2 + unused = 0 +0098 0b804388 JUMPS 16, 11, GE + cmp = 7 (GE) + imm = 11 (0x0b) + offset = 16 (0x10) + opcode = 8 + sign = 0 + sub_opcode = 2 + unused = 0 +009c 01000090 WAKE + opcode = 9 + sub_opcode = 0 + unused = 0 + wakeup = 1 +00a0 07000040 WAIT 7 + cycles = 7 + opcode = 4 + unused = 0 +00a4 090000a0 TSENS r1, 2 + delay = 2 + dreg = 1 + opcode = 10 (0x0a) + unused = 0 +00a8 000000b0 HALT + opcode = 11 (0x0b) + unused = 0 +00ac 060000d0 LD r2, r1, 0 + dreg = 2 + offset = 0 + opcode = 13 (0x0d) + sreg = 1 + unused1 = 0 + unused2 = 0 + rd_upper = 0 +00b0 00000040 NOP + cycles = 0 + opcode = 4 + unused = 0 +00b4 092000d0 LD r1, r2, 8 + dreg = 1 + offset = 8 + opcode = 13 (0x0d) + sreg = 2 + unused1 = 0 + unused2 = 0 + rd_upper = 0 +00b8 092000d8 LDH r1, r2, 8 + dreg = 1 + offset = 8 + opcode = 13 (0x0d) + sreg = 2 + unused1 = 0 + unused2 = 0 + rd_upper = 1 +00bc 89210068 ST r1, r2, 8 + dreg = 2 + offset = 8 + opcode = 6 + sreg = 1 + sub_opcode = 4 + unused1 = 0 + unused2 = 0 + label = 0 + upper = 0 + wr_way = 3 +00c0 89200068 ST r1, r2, 8 + dreg = 2 + offset = 8 + opcode = 6 + sreg = 1 + sub_opcode = 4 + unused1 = 0 + unused2 = 0 + label = 0 + upper = 0 + wr_way = 1 +00c4 99200068 STL r1, r2, 8, 1 + dreg = 2 + offset = 8 + opcode = 6 + sreg = 1 + sub_opcode = 4 + unused1 = 0 + unused2 = 0 + label = 1 + upper = 0 + wr_way = 1 +00c8 c9210068 STH r1, r2, 8 + dreg = 2 + offset = 8 + opcode = 6 + sreg = 1 + sub_opcode = 4 + unused1 = 0 + unused2 = 0 + label = 0 + upper = 1 + wr_way = 3 +00cc c9200068 STH r1, r2, 8 + dreg = 2 + offset = 8 + opcode = 6 + sreg = 1 + sub_opcode = 4 + unused1 = 0 + unused2 = 0 + label = 0 + upper = 1 + wr_way = 1 +00d0 d9200068 STH r1, r2, 8, 1 + dreg = 2 + offset = 8 + opcode = 6 + sreg = 1 + sub_opcode = 4 + unused1 = 0 + unused2 = 0 + label = 1 + upper = 1 + wr_way = 1 +00d4 09200068 ST32 r1, r2, 8, 0 + dreg = 2 + offset = 8 + opcode = 6 + sreg = 1 + sub_opcode = 4 + unused1 = 0 + unused2 = 0 + label = 0 + upper = 0 + wr_way = 0 +00d8 19200068 ST32 r1, r2, 8, 1 + dreg = 2 + offset = 8 + opcode = 6 + sreg = 1 + sub_opcode = 4 + unused1 = 0 + unused2 = 0 + label = 1 + upper = 0 + wr_way = 0 +00dc 89010062 STI r1, r2 + dreg = 2 + offset = 0 + opcode = 6 + sreg = 1 + sub_opcode = 1 + unused1 = 0 + unused2 = 0 + label = 0 + upper = 0 + wr_way = 3 +00e0 89000062 STI r1, r2 + dreg = 2 + offset = 0 + opcode = 6 + sreg = 1 + sub_opcode = 1 + unused1 = 0 + unused2 = 0 + label = 0 + upper = 0 + wr_way = 1 +00e4 99000062 STI r1, r2, 1 + dreg = 2 + offset = 0 + opcode = 6 + sreg = 1 + sub_opcode = 1 + unused1 = 0 + unused2 = 0 + label = 1 + upper = 0 + wr_way = 1 +00e8 09000062 STI32 r1, r2, 0 + dreg = 2 + offset = 0 + opcode = 6 + sreg = 1 + sub_opcode = 1 + unused1 = 0 + unused2 = 0 + label = 0 + upper = 0 + wr_way = 0 +00ec 19000062 STI32 r1, r2, 1 + dreg = 2 + offset = 0 + opcode = 6 + sreg = 1 + sub_opcode = 1 + unused1 = 0 + unused2 = 0 + label = 1 + upper = 0 + wr_way = 0 +00f0 00200064 STO 8 + dreg = 0 + offset = 8 + opcode = 6 + sreg = 0 + sub_opcode = 2 + unused1 = 0 + unused2 = 0 + label = 0 + upper = 0 + wr_way = 0 +00f4 09e01fd0 LD r1, r2, -8 + dreg = 1 + offset = -8 (0x7f8) + opcode = 13 (0x0d) + sreg = 2 + unused1 = 0 + unused2 = 0 + rd_upper = 0 +00f8 09e01fd8 LDH r1, r2, -8 + dreg = 1 + offset = -8 (0x7f8) + opcode = 13 (0x0d) + sreg = 2 + unused1 = 0 + unused2 = 0 + rd_upper = 1 +00fc 89e11f68 ST r1, r2, -8 + dreg = 2 + offset = -8 (0x7f8) + opcode = 6 + sreg = 1 + sub_opcode = 4 + unused1 = 0 + unused2 = 0 + label = 0 + upper = 0 + wr_way = 3 +0100 c9e11f68 STH r1, r2, -8 + dreg = 2 + offset = -8 (0x7f8) + opcode = 6 + sreg = 1 + sub_opcode = 4 + unused1 = 0 + unused2 = 0 + label = 0 + upper = 1 + wr_way = 3 +---------------------------------------- +.data +0104 00000000 +0108 fecadec0 diff --git a/tests/fixtures/all_opcodes.esp32.S b/tests/fixtures/all_opcodes.esp32.S new file mode 100644 index 0000000..66d7763 --- /dev/null +++ b/tests/fixtures/all_opcodes.esp32.S @@ -0,0 +1,72 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +.data +empty: .long 0 +magic: .long 0xc0decafe + +.text +REG_WR 0x123, 1, 2, 3 + +REG_RD 0x321, 2, 1 + +I2C_RD 3, 2, 1, 0 +I2C_WR 0, 1, 2, 3, 4 + +NOP +WAIT 7 + +ADC r3, 2, 1 + +ST r3, r2, 1 + +ADD r2, r1, r0 +SUB r2, r1, r0 +AND r2, r1, r0 +OR r2, r1, r0 +MOVE r2, r1 +LSH r2, r1, r0 +RSH r2, r1, r0 + +ADD r2, r1, 0 +SUB r2, r1, 0 +AND r2, r1, 0 +OR r2, r1, 0 +MOVE r1, 0 +LSH r2, r1, 0 +RSH r2, r1, 0 + +STAGE_RST +STAGE_INC 7 +STAGE_DEC 3 + +JUMP r0 +JUMP r1, EQ +JUMP r2, OV + +JUMP 0 +JUMP 0, EQ +JUMP 0, OV + +JUMPR 0, 1, LT +JUMPR 4, 5, GT +JUMPR 8, 7, EQ + +JUMPS 0, 1, LT +JUMPS 4, 5, GT +JUMPS 8, 7, EQ +JUMPS 12, 9, LE +JUMPS 16, 11, GE + +WAKE +SLEEP 7 + +TSENS r1, 2 + +HALT + +LD r2, r1, 0 diff --git a/tests/fixtures/all_opcodes.esp32.lst b/tests/fixtures/all_opcodes.esp32.lst new file mode 100644 index 0000000..b882a3e --- /dev/null +++ b/tests/fixtures/all_opcodes.esp32.lst @@ -0,0 +1,51 @@ +.text +0000 230d8810 REG_WR 0x123, 1, 2, 3 +0004 21030421 REG_RD 0x321, 2, 1 +0008 03001130 I2C_RD 3, 2, 1, 0 +000c 00011339 I2C_WR 0, 2, 3, 4 +0010 00000040 NOP +0014 07000040 WAIT 7 +0018 07000050 ADC r3, 1, 0 +001c 0b000068 ST r3, r2, 0 +0020 06000070 ADD r2, r1, r0 +0024 06002070 SUB r2, r1, r0 +0028 06004070 AND r2, r1, r0 +002c 06006070 OR r2, r1, r0 +0030 16008070 MOVE r2, r1 +0034 0600a070 LSH r2, r1, r0 +0038 0600c070 RSH r2, r1, r0 +003c 06000072 ADD r2, r1, 0 +0040 06002072 SUB r2, r1, 0 +0044 06004072 AND r2, r1, 0 +0048 06006072 OR r2, r1, 0 +004c 01008072 MOVE r1, 0 +0050 0600a072 LSH r2, r1, 0 +0054 0600c072 RSH r2, r1, 0 +0058 00004074 STAGE_RST +005c 70000074 STAGE_INC 7 +0060 30002074 STAGE_DEC 3 +0064 00002080 JUMP r0 +0068 01006080 JUMP r1, EQ +006c 0200a080 JUMP r2, OV +0070 00000080 JUMP 0 +0074 00004080 JUMP 0, EQ +0078 00008080 JUMP 0, OV +007c 01000082 JUMPR 0, 1, LT +0080 06000382 JUMPR 1, 6, GE +0084 08000582 JUMPR 2, 8, GE +0088 07000582 JUMPR 2, 7, GE +008c 01000084 JUMPS 0, 1, LT +0090 05000584 JUMPS 2, 5, LE +0094 05800284 JUMPS 1, 5, GE +0098 07000484 JUMPS 2, 7, LT +009c 07000584 JUMPS 2, 7, LE +00a0 09000784 JUMPS 3, 9, LE +00a4 0b800884 JUMPS 4, 11, GE +00a8 01000090 WAKE +00ac 07000092 SLEEP 7 +00b0 090000a0 TSENS r1, 2 +00b4 000000b0 HALT +00b8 060000d0 LD r2, r1, 0 +.data +00bc 00000000 +00c0 fecadec0 diff --git a/tests/fixtures/all_opcodes.esp32s2.S b/tests/fixtures/all_opcodes.esp32s2.S new file mode 100644 index 0000000..c53973c --- /dev/null +++ b/tests/fixtures/all_opcodes.esp32s2.S @@ -0,0 +1,104 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +.data +empty: .long 0 +magic: .long 0xc0decafe + +.text +REG_WR 0x123, 1, 2, 3 + +REG_RD 0x321, 2, 1 + +I2C_RD 3, 2, 1, 0 +I2C_WR 0, 1, 2, 3, 4 + +NOP +WAIT 7 + +ADC r3, 2, 1 + +ST r3, r2, 1 + +ADD r2, r1, r0 +SUB r2, r1, r0 +AND r2, r1, r0 +OR r2, r1, r0 +MOVE r2, r1 +LSH r2, r1, r0 +RSH r2, r1, r0 + +ADD r2, r1, 0 +SUB r2, r1, 0 +AND r2, r1, 0 +OR r2, r1, 0 +MOVE r1, 0 +LSH r2, r1, 0 +RSH r2, r1, 0 + +STAGE_RST +STAGE_INC 7 +STAGE_DEC 3 + +JUMP r0 +JUMP r1, EQ +JUMP r2, OV + +JUMP 0 +JUMP 0, EQ +JUMP 0, OV + +JUMPR 0, 1, LT +JUMPR 4, 5, GT +JUMPR 8, 7, EQ + +JUMPS 0, 1, LT +JUMPS 4, 5, GT +JUMPS 8, 7, EQ +JUMPS 12, 9, LE +JUMPS 16, 11, GE + +WAKE +SLEEP 7 + +TSENS r1, 2 + +HALT + +LD r2, r1, 0 + +# ESP32-S2 specific instructions +NOP # marker + +LDL R1, R2, 0x20 +LDH R1, R2, 0x20 + +STL R1, R2, 0x20 +STL R1, R2, 0x20, 0 +STL R1, R2, 0x20, 1 + +STH R1, R2, 0x20 +STH R1, R2, 0x20, 0 +STH R1, R2, 0x20, 1 + +ST32 R1, R2, 0x20, 0 +ST32 R1, R2, 0x20, 1 + +STI R1, R2 +STI R1, R2, 0 +STI R1, R2, 1 + +STI32 R1, R2, 0 +STI32 R1, R2, 1 + +STO 0x20 + +LDL R1, R2, -0x20 +LDH R1, R2, -0x20 + +STL R1, R2, -0x20 +STH R1, R2, -0x20 diff --git a/tests/fixtures/all_opcodes.esp32s2.lst b/tests/fixtures/all_opcodes.esp32s2.lst new file mode 100644 index 0000000..5de1704 --- /dev/null +++ b/tests/fixtures/all_opcodes.esp32s2.lst @@ -0,0 +1,69 @@ +.text +0000 230d8810 REG_WR 0x123, 1, 2, 3 +0004 21030421 REG_RD 0x321, 2, 1 +0008 03001130 I2C_RD 3, 2, 1, 0 +000c 00011339 I2C_WR 0, 2, 3, 4 +0010 00000040 NOP +0014 07000040 WAIT 7 +0018 07000050 ADC r3, 1, 0 +001c 8b010068 ST r3, r2, 0 +0020 06000070 ADD r2, r1, r0 +0024 06002070 SUB r2, r1, r0 +0028 06004070 AND r2, r1, r0 +002c 06006070 OR r2, r1, r0 +0030 16008070 MOVE r2, r1 +0034 0600a070 LSH r2, r1, r0 +0038 0600c070 RSH r2, r1, r0 +003c 06000074 ADD r2, r1, 0 +0040 06002074 SUB r2, r1, 0 +0044 06004074 AND r2, r1, 0 +0048 06006074 OR r2, r1, 0 +004c 01008074 MOVE r1, 0 +0050 0600a074 LSH r2, r1, 0 +0054 0600c074 RSH r2, r1, 0 +0058 00004078 STAGE_RST +005c 70000078 STAGE_INC 7 +0060 30002078 STAGE_DEC 3 +0064 00002084 JUMP r0 +0068 01006084 JUMP r1, EQ +006c 0200a084 JUMP r2, OV +0070 00000084 JUMP 0 +0074 00004084 JUMP 0, EQ +0078 00008084 JUMP 0, OV +007c 01000080 JUMPR 0, 1, LT +0080 05000580 JUMPR 1, 5, GT +0084 07000a80 JUMPR 2, 7, EQ +0088 01800088 JUMPS 0, 1, LT +008c 05801188 JUMPS 4, 5, GT +0090 07002288 JUMPS 8, 7, EQ +0094 09803288 JUMPS 12, 9, LE +0098 0b804388 JUMPS 16, 11, GE +009c 01000090 WAKE +00a0 07000040 WAIT 7 +00a4 090000a0 TSENS r1, 2 +00a8 000000b0 HALT +00ac 060000d0 LD r2, r1, 0 +00b0 00000040 NOP +00b4 092000d0 LD r1, r2, 8 +00b8 092000d8 LDH r1, r2, 8 +00bc 89210068 ST r1, r2, 8 +00c0 89200068 ST r1, r2, 8 +00c4 99200068 STL r1, r2, 8, 1 +00c8 c9210068 STH r1, r2, 8 +00cc c9200068 STH r1, r2, 8 +00d0 d9200068 STH r1, r2, 8, 1 +00d4 09200068 ST32 r1, r2, 8, 0 +00d8 19200068 ST32 r1, r2, 8, 1 +00dc 89010062 STI r1, r2 +00e0 89000062 STI r1, r2 +00e4 99000062 STI r1, r2, 1 +00e8 09000062 STI32 r1, r2, 0 +00ec 19000062 STI32 r1, r2, 1 +00f0 00200064 STO 8 +00f4 09e01fd0 LD r1, r2, -8 +00f8 09e01fd8 LDH r1, r2, -8 +00fc 89e11f68 ST r1, r2, -8 +0100 c9e11f68 STH r1, r2, -8 +.data +0104 00000000 +0108 fecadec0 diff --git a/tests/fixtures/incl.h b/tests/fixtures/incl.h index 712aa7c..1c1e033 100644 --- a/tests/fixtures/incl.h +++ b/tests/fixtures/incl.h @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + #define CONST1 42 #define MACRO(x,y) x+y #define MULTI_LINE abc \ diff --git a/tests/fixtures/incl2.h b/tests/fixtures/incl2.h index d19aeba..fa64ef0 100644 --- a/tests/fixtures/incl2.h +++ b/tests/fixtures/incl2.h @@ -1,2 +1,9 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + #define CONST2 123 #define CONST3 777 diff --git a/tests/fixtures/manual_bytes-v.esp32.lst b/tests/fixtures/manual_bytes-v.esp32.lst new file mode 100644 index 0000000..7f44ea4 --- /dev/null +++ b/tests/fixtures/manual_bytes-v.esp32.lst @@ -0,0 +1,42 @@ +0000 e1af8c72 MOVE r1, 51966 + dreg = 1 + imm = 51966 (0xcafe) + opcode = 7 + sel = 4 (MOVE) + sreg = 0 + sub_opcode = 1 + unused = 0 +0004 01000068 ST r1, r0, 0 + dreg = 0 + offset = 0 + opcode = 6 + sreg = 1 + sub_opcode = 4 + unused1 = 0 + unused2 = 0 +0008 2705cc19 REG_WR 0x127, 19, 19, 1 + addr = 39 (0x27) + data = 1 + high = 19 (0x13) + low = 19 (0x13) + opcode = 1 + periph_sel = 1 +000c 0005681d REG_WR 0x100, 26, 26, 1 + addr = 0 + data = 1 + high = 26 (0x1a) + low = 26 (0x1a) + opcode = 1 + periph_sel = 1 +0010 000000a0 TSENS r0, 0 + delay = 0 + dreg = 0 + opcode = 10 (0x0a) + unused = 0 +0014 00000074 STAGE_INC 0 + imm = 0 + opcode = 7 + sel = 0 (STAGE_INC) + sub_opcode = 2 + unused1 = 0 + unused2 = 0 diff --git a/tests/fixtures/manual_bytes-v.esp32s2.lst b/tests/fixtures/manual_bytes-v.esp32s2.lst new file mode 100644 index 0000000..7d91dda --- /dev/null +++ b/tests/fixtures/manual_bytes-v.esp32s2.lst @@ -0,0 +1,46 @@ +0000 e1af8c74 MOVE r1, 51966 + dreg = 1 + imm = 51966 (0xcafe) + opcode = 7 + sel = 4 (MOVE) + sreg = 0 + sub_opcode = 1 + unused1 = 0 + unused2 = 0 +0004 81010068 ST r1, r0, 0 + dreg = 0 + offset = 0 + opcode = 6 + sreg = 1 + sub_opcode = 4 + unused1 = 0 + unused2 = 0 + label = 0 + upper = 0 + wr_way = 3 +0008 2705cc19 REG_WR 0x127, 19, 19, 1 + addr = 39 (0x27) + data = 1 + high = 19 (0x13) + low = 19 (0x13) + opcode = 1 + periph_sel = 1 +000c 0005681d REG_WR 0x100, 26, 26, 1 + addr = 0 + data = 1 + high = 26 (0x1a) + low = 26 (0x1a) + opcode = 1 + periph_sel = 1 +0010 000000a0 TSENS r0, 0 + delay = 0 + dreg = 0 + opcode = 10 (0x0a) + unused = 0 +0014 00000078 STAGE_INC 0 + imm = 0 + opcode = 7 + sel = 0 (STAGE_INC) + sub_opcode = 2 + unused1 = 0 + unused2 = 0 diff --git a/tests/fixtures/manual_bytes.esp32.lst b/tests/fixtures/manual_bytes.esp32.lst new file mode 100644 index 0000000..beb1b77 --- /dev/null +++ b/tests/fixtures/manual_bytes.esp32.lst @@ -0,0 +1,6 @@ +0000 e1af8c72 MOVE r1, 51966 +0004 01000068 ST r1, r0, 0 +0008 2705cc19 REG_WR 0x127, 19, 19, 1 +000c 0005681d REG_WR 0x100, 26, 26, 1 +0010 000000a0 TSENS r0, 0 +0014 00000074 STAGE_INC 0 diff --git a/tests/fixtures/manual_bytes.esp32s2.lst b/tests/fixtures/manual_bytes.esp32s2.lst new file mode 100644 index 0000000..52799af --- /dev/null +++ b/tests/fixtures/manual_bytes.esp32s2.lst @@ -0,0 +1,6 @@ +0000 e1af8c74 MOVE r1, 51966 +0004 81010068 ST r1, r0, 0 +0008 2705cc19 REG_WR 0x127, 19, 19, 1 +000c 0005681d REG_WR 0x100, 26, 26, 1 +0010 000000a0 TSENS r0, 0 +0014 00000078 STAGE_INC 0 diff --git a/tests/link.py b/tests/link.py index 8a301f4..09673b3 100644 --- a/tests/link.py +++ b/tests/link.py @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + from esp32_ulp.link import make_binary diff --git a/tests/nocomment.py b/tests/nocomment.py index 8b476d0..daa6406 100644 --- a/tests/nocomment.py +++ b/tests/nocomment.py @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + from esp32_ulp.nocomment import remove_comments ORIG = """\ diff --git a/tests/opcodes.py b/tests/opcodes.py index 85cd710..3dc7453 100644 --- a/tests/opcodes.py +++ b/tests/opcodes.py @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + from uctypes import UINT32, BFUINT32, BF_POS, BF_LEN from esp32_ulp.opcodes import make_ins, make_ins_struct_def from esp32_ulp.opcodes import get_reg, get_imm, get_cond, arg_qualify, eval_arg, ARG, REG, IMM, SYM, COND @@ -174,6 +181,31 @@ def test_reg_address_translations(): assert ins.addr == 0x2a # low 8 bits of 0x12a +def test_reg_address_translations_sens(): + """ + Test addressing of peripheral registers using full DPORT bus addresses + """ + + ins = make_ins(""" + addr : 8 # Address within either RTC_CNTL, RTC_IO, or SARADC + periph_sel : 2 # Select peripheral: RTC_CNTL (0), RTC_IO(1), SARADC(2) + unused : 8 # Unused + low : 5 # Low bit + high : 5 # High bit + opcode : 4 # Opcode (OPCODE_RD_REG) + """) + + # direct ULP address is derived from full address as follows: + # full:0x3ff48904 == ulp:(0x3ff48904-DR_REG_RTCCNTL_BASE) / 4 + # full:0x3ff48904 == ulp:(0x3ff48904-0x3f408000) / 4 + # full:0x3ff48904 == ulp:0x904 / 4 + # full:0x3ff48904 == ulp:0x241 + # see: https://github.com/espressif/binutils-esp32ulp/blob/249ec34/gas/config/tc-esp32ulp_esp32s2.c#L78 + ins.all = opcodes.i_reg_rd("0x3ff48904", "0", "0") + assert ins.periph_sel == 2 # high 2 bits of 0x241 + assert ins.addr == 0x41 # low 8 bits of 0x241 + + test_make_ins_struct_def() test_make_ins() test_arg_qualify() @@ -183,3 +215,4 @@ def test_reg_address_translations(): test_eval_arg() test_reg_direct_ulp_addressing() test_reg_address_translations() +test_reg_address_translations_sens() diff --git a/tests/opcodes_s2.py b/tests/opcodes_s2.py new file mode 100644 index 0000000..4525049 --- /dev/null +++ b/tests/opcodes_s2.py @@ -0,0 +1,270 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +from uctypes import UINT32, BFUINT32, BF_POS, BF_LEN +from esp32_ulp.opcodes_s2 import make_ins, make_ins_struct_def +from esp32_ulp.opcodes_s2 import get_reg, get_imm, get_cond, arg_qualify, eval_arg, ARG, REG, IMM, SYM, COND +from esp32_ulp.assemble import SymbolTable, ABS, REL, TEXT +import esp32_ulp.opcodes_s2 as opcodes + +OPCODE_DELAY = 4 +LAYOUT_DELAY = """ + cycles : 16 # Number of cycles to sleep + unused : 12 # Unused + opcode : 4 # Opcode (OPCODE_DELAY) +""" + + +def test_make_ins_struct_def(): + sd = make_ins_struct_def(LAYOUT_DELAY) + assert set(sd) == {'cycles', 'unused', 'opcode', 'all'} + assert sd['cycles'] == BFUINT32 | 0 << BF_POS | 16 << BF_LEN + assert sd['unused'] == BFUINT32 | 16 << BF_POS | 12 << BF_LEN + assert sd['opcode'] == BFUINT32 | 28 << BF_POS | 4 << BF_LEN + assert sd['all'] == UINT32 + + +def test_make_ins(): + _delay = make_ins(LAYOUT_DELAY) + _delay.cycles = 0x23 + _delay.unused = 0 + _delay.opcode = OPCODE_DELAY + assert _delay.cycles == 0x23 + assert _delay.unused == 0 + assert _delay.opcode == OPCODE_DELAY + assert _delay.all == 0x40000023 + + +def test_arg_qualify(): + assert arg_qualify('r0') == ARG(REG, 0, 'r0') + assert arg_qualify('R3') == ARG(REG, 3, 'R3') + assert arg_qualify('0') == ARG(IMM, 0, '0') + assert arg_qualify('-1') == ARG(IMM, -1, '-1') + assert arg_qualify('1') == ARG(IMM, 1, '1') + assert arg_qualify('0x20') == ARG(IMM, 32, '0x20') + assert arg_qualify('0o100') == ARG(IMM, 64, '0o100') + assert arg_qualify('0b1000') == ARG(IMM, 8, '0b1000') + assert arg_qualify('eq') == ARG(COND, 'eq', 'eq') + assert arg_qualify('Eq') == ARG(COND, 'eq', 'Eq') + assert arg_qualify('EQ') == ARG(COND, 'eq', 'EQ') + + # for the next tests, ensure the opcodes module has a SymbolTable + opcodes.symbols = SymbolTable({}, {}, {}) + opcodes.symbols.set_sym('const', ABS, None, 42) # constant as defined by .set + opcodes.symbols.set_sym('entry', REL, TEXT, 4) # label pointing to code + + assert arg_qualify('1+1') == ARG(IMM, 2, '1+1') + assert arg_qualify('const >> 1') == ARG(IMM, 21, 'const >> 1') + assert arg_qualify('entry') == ARG(SYM, (REL, TEXT, 4), 'entry') # symbols should not (yet) be evaluated + assert arg_qualify('entry + const') == ARG(IMM, 46, 'entry + const') + + # clean up + opcodes.symbols = None + + +def test_get_reg(): + assert get_reg('r0') == 0 + assert get_reg('R3') == 3 + + +def test_get_imm(): + assert get_imm('42') == 42 + + +def test_get_cond(): + assert get_cond('Eq') == 'eq' + + +def test_eval_arg(): + opcodes.symbols = SymbolTable({}, {}, {}) + opcodes.symbols.set_sym('const', ABS, None, 42) # constant + opcodes.symbols.set_sym('raise', ABS, None, 99) # constant using a python keyword as name (is allowed) + + assert eval_arg('1+1') == 2 + assert eval_arg('1+const') == 43 + assert eval_arg('raise*2/3') == 66 + assert eval_arg('raise-const') == 57 + assert eval_arg('(raise-const)*2') == 114 + assert eval_arg('const % 5') == 2 + assert eval_arg('const + 0x19af') == 0x19af + 42 + assert eval_arg('const & ~2') == 40 + assert eval_arg('const << 3') == 336 + assert eval_arg('const >> 1') == 21 + assert eval_arg('(const|4)&0xf') == 0xe + + assert_raises(ValueError, eval_arg, 'evil()') + assert_raises(ValueError, eval_arg, 'def cafe()') + assert_raises(ValueError, eval_arg, '1 ^ 2') + assert_raises(ValueError, eval_arg, '!100') + + # clean up + opcodes.symbols = None + + +def assert_raises(exception, func, *args): + try: + func(*args) + except exception: + raised = True + else: + raised = False + assert raised + + +def test_reg_direct_ulp_addressing(): + """ + Test direct ULP addressing of peripheral registers + input must be <= 0x3ff (10 bits) + periph_sel == high 2 bits from input + addr == low 8 bits from input + """ + + ins = make_ins(""" + addr : 8 # Address within either RTC_CNTL, RTC_IO, or SARADC + periph_sel : 2 # Select peripheral: RTC_CNTL (0), RTC_IO(1), SARADC(2) + unused : 8 # Unused + low : 5 # Low bit + high : 5 # High bit + opcode : 4 # Opcode (OPCODE_RD_REG) + """) + + ins.all = opcodes.i_reg_rd("0x0", "0", "0") + assert ins.periph_sel == 0 + assert ins.addr == 0x0 + + ins.all = opcodes.i_reg_rd("0x012", "0", "0") + assert ins.periph_sel == 0 + assert ins.addr == 0x12 + + ins.all = opcodes.i_reg_rd("0x123", "0", "0") + assert ins.periph_sel == 1 + assert ins.addr == 0x23 + + ins.all = opcodes.i_reg_rd("0x2ee", "0", "0") + assert ins.periph_sel == 2 + assert ins.addr == 0xee + + ins.all = opcodes.i_reg_rd("0x3ff", "0", "0") + assert ins.periph_sel == 3 + assert ins.addr == 0xff + + # anything bigger than 0x3ff must be a valid full address + assert_raises(ValueError, opcodes.i_reg_rd, "0x400", "0", "0") + + +def test_reg_address_translations_s2(): + """ + Test addressing of ESP32-S2 peripheral registers using full DPORT bus addresses + """ + + ins = make_ins(""" + addr : 8 # Address within either RTC_CNTL, RTC_IO, or SARADC + periph_sel : 2 # Select peripheral: RTC_CNTL (0), RTC_IO(1), SARADC(2) + unused : 8 # Unused + low : 5 # Low bit + high : 5 # High bit + opcode : 4 # Opcode (OPCODE_RD_REG) + """) + + # direct ULP address is derived from full address as follows: + # full:0x3f4084a8 == ulp:(0x3f4084a8-DR_REG_RTCCNTL_BASE) / 4 + # full:0x3f4084a8 == ulp:(0x3f4084a8-0x3f408000) / 4 + # full:0x3f4084a8 == ulp:0x4a8 / 4 + # full:0x3f4084a8 == ulp:0x12a + # see: https://github.com/espressif/binutils-esp32ulp/blob/249ec34/gas/config/tc-esp32ulp_esp32s2.c#L78 + ins.all = opcodes.i_reg_rd("0x3f4084a8", "0", "0") + assert ins.periph_sel == 1 # high 2 bits of 0x12a + assert ins.addr == 0x2a # low 8 bits of 0x12a + + +def test_reg_address_translations_s2_sens(): + """ + Test addressing of ESP32-S2 peripheral registers using full DPORT bus addresses + """ + + ins = make_ins(""" + addr : 8 # Address within either RTC_CNTL, RTC_IO, or SARADC + periph_sel : 2 # Select peripheral: RTC_CNTL (0), RTC_IO(1), SARADC(2) + unused : 8 # Unused + low : 5 # Low bit + high : 5 # High bit + opcode : 4 # Opcode (OPCODE_RD_REG) + """) + + # direct ULP address is derived from full address as follows: + # full:0x3f408904 == ulp:(0x3f408904-DR_REG_RTCCNTL_BASE) / 4 + # full:0x3f408904 == ulp:(0x3f408904-0x3f408000) / 4 + # full:0x3f408904 == ulp:0x904 / 4 + # full:0x3f408904 == ulp:0x241 + # see: https://github.com/espressif/binutils-esp32ulp/blob/249ec34/gas/config/tc-esp32ulp_esp32s2.c#L78 + ins.all = opcodes.i_reg_rd("0x3f408904", "0", "0") + assert ins.periph_sel == 2 # high 2 bits of 0x241 + assert ins.addr == 0x41 # low 8 bits of 0x241 + + +def test_reg_address_translations_s3(): + """ + Test addressing of ESP32-S3 peripheral registers using full DPORT bus addresses + """ + + ins = make_ins(""" + addr : 8 # Address within either RTC_CNTL, RTC_IO, or SARADC + periph_sel : 2 # Select peripheral: RTC_CNTL (0), RTC_IO(1), SARADC(2) + unused : 8 # Unused + low : 5 # Low bit + high : 5 # High bit + opcode : 4 # Opcode (OPCODE_RD_REG) + """) + + # direct ULP address is derived from full address as follows: + # full:0x600084a8 == ulp:(0x600084a8-DR_REG_RTCCNTL_BASE) / 4 + # full:0x600084a8 == ulp:(0x600084a8-0x60008000) / 4 + # full:0x600084a8 == ulp:0x4a8 / 4 + # full:0x600084a8 == ulp:0x12a + # see: https://github.com/espressif/binutils-esp32ulp/blob/249ec34/gas/config/tc-esp32ulp_esp32s2.c#L78 + ins.all = opcodes.i_reg_rd("0x600084a8", "0", "0") + assert ins.periph_sel == 1 # high 2 bits of 0x12a + assert ins.addr == 0x2a # low 8 bits of 0x12a + + +def test_reg_address_translations_s3_sens(): + """ + Test addressing of ESP32-S3 peripheral registers using full DPORT bus addresses + """ + + ins = make_ins(""" + addr : 8 # Address within either RTC_CNTL, RTC_IO, or SARADC + periph_sel : 2 # Select peripheral: RTC_CNTL (0), RTC_IO(1), SARADC(2) + unused : 8 # Unused + low : 5 # Low bit + high : 5 # High bit + opcode : 4 # Opcode (OPCODE_RD_REG) + """) + + # direct ULP address is derived from full address as follows: + # full:0x60008904 == ulp:(0x60008904-DR_REG_RTCCNTL_BASE) / 4 + # full:0x60008904 == ulp:(0x60008904-0x60008000) / 4 + # full:0x60008904 == ulp:0x904 / 4 + # full:0x60008904 == ulp:0x241 + # see: https://github.com/espressif/binutils-esp32ulp/blob/249ec34/gas/config/tc-esp32ulp_esp32s2.c#L78 + ins.all = opcodes.i_reg_rd("0x60008904", "0", "0") + assert ins.periph_sel == 2 # high 2 bits of 0x241 + assert ins.addr == 0x41 # low 8 bits of 0x241 + + +test_make_ins_struct_def() +test_make_ins() +test_arg_qualify() +test_get_reg() +test_get_imm() +test_get_cond() +test_eval_arg() +test_reg_direct_ulp_addressing() +test_reg_address_translations_s2() +test_reg_address_translations_s3() +test_reg_address_translations_s2_sens() +test_reg_address_translations_s3_sens() diff --git a/tests/preprocess.py b/tests/preprocess.py index be7cf61..e914577 100644 --- a/tests/preprocess.py +++ b/tests/preprocess.py @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + import os from esp32_ulp.preprocess import Preprocessor diff --git a/tests/tools b/tests/tools new file mode 120000 index 0000000..4887d6e --- /dev/null +++ b/tests/tools @@ -0,0 +1 @@ +../tools \ No newline at end of file diff --git a/tests/util.py b/tests/util.py index 009f3f1..6aadc8b 100644 --- a/tests/util.py +++ b/tests/util.py @@ -1,3 +1,10 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + import os from esp32_ulp.util import split_tokens, validate_expression, file_exists @@ -44,6 +51,10 @@ def test_validate_expression(): assert validate_expression('0x100 & ~2') is True assert validate_expression('0xabcdef') is True assert validate_expression('0x123def') is True + assert validate_expression('0xABC') is True + assert validate_expression('0xaBc') is True + assert validate_expression('0Xabc') is True + assert validate_expression('0X123ABC') is True assert validate_expression('2*3+4/5&6|7') is True assert validate_expression('(((((1+1) * 2') is True # valid characters, even if expression is not valid @@ -55,6 +66,7 @@ def test_validate_expression(): assert validate_expression('123 ^ 4') is False # operator not supported for now assert validate_expression('evil()') is False assert validate_expression('def cafe()') is False # valid hex digits, but potentially dangerous code + assert validate_expression('def CAFE()') is False @test diff --git a/tools/decode.py b/tools/decode.py new file mode 100644 index 0000000..fb93e73 --- /dev/null +++ b/tools/decode.py @@ -0,0 +1,156 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +import esp32_ulp.opcodes as opcodes + + +alu_cnt_ops = ('STAGE_INC', 'STAGE_DEC', 'STAGE_RST') +alu_ops = ('ADD', 'SUB', 'AND', 'OR', 'MOVE', 'LSH', 'RSH') +jump_types = ('--', 'EQ', 'OV') +cmp_ops = ('LT', 'GE', 'LE', 'EQ', 'GT') + +lookup = { + opcodes.OPCODE_ADC: ('ADC', opcodes._adc, lambda op: 'ADC r%s, %s, %s' % (op.dreg, op.mux, op.sar_sel)), + opcodes.OPCODE_ALU: ('ALU', opcodes._alu_imm, { + opcodes.SUB_OPCODE_ALU_CNT: ( + 'ALU_CNT', + opcodes._alu_cnt, + lambda op: '%s%s' % (alu_cnt_ops[op.sel], '' if op.sel == opcodes.ALU_SEL_RST else ' %s' % op.imm) + ), + opcodes.SUB_OPCODE_ALU_IMM: ( + 'ALU_IMM', + opcodes._alu_imm, + lambda op: '%s r%s, %s' % (alu_ops[op.sel], op.dreg, op.imm) if op.sel == opcodes.ALU_SEL_MOV + else '%s r%s, r%s, %s' % (alu_ops[op.sel], op.dreg, op.sreg, op.imm) + ), + opcodes.SUB_OPCODE_ALU_REG: ( + 'ALU_REG', + opcodes._alu_reg, + lambda op: '%s r%s, r%s' % (alu_ops[op.sel], op.dreg, op.sreg) if op.sel == opcodes.ALU_SEL_MOV + else '%s r%s, r%s, r%s' % (alu_ops[op.sel], op.dreg, op.sreg, op.treg) + ), + }), + opcodes.OPCODE_BRANCH: ('BRANCH', opcodes._bx, { + opcodes.SUB_OPCODE_BX: ( + 'BX', + opcodes._bx, + lambda op: 'JUMP %s%s' % (op.addr if op.reg == 0 else 'r%s' % op.dreg, ', %s' % jump_types[op.type] + if op.type != 0 else '') + ), + opcodes.SUB_OPCODE_BR: ( + 'BR', + opcodes._br, + lambda op: 'JUMPR %s, %s, %s' % ('%s%s' % ('-' if op.sign == 1 else '', op.offset), op.imm, cmp_ops[op.cmp]) + ), + opcodes.SUB_OPCODE_BS: ( + 'BS', + opcodes._bs, + lambda op: 'JUMPS %s, %s, %s' % ('%s%s' % ('-' if op.sign == 1 else '', op.offset), op.imm, cmp_ops[op.cmp]) + ), + }), + opcodes.OPCODE_DELAY: ( + 'DELAY', + opcodes._delay, + lambda op: 'NOP' if op.cycles == 0 else 'WAIT %s' % op.cycles + ), + opcodes.OPCODE_END: ('END', opcodes._end, { + opcodes.SUB_OPCODE_END: ( + 'WAKE', + opcodes._end + ), + opcodes.SUB_OPCODE_SLEEP: ( + 'SLEEP', + opcodes._sleep, + lambda op: 'SLEEP %s' % op.cycle_sel + ), + }), + opcodes.OPCODE_HALT: ('HALT', opcodes._halt), + opcodes.OPCODE_I2C: ( + 'I2C', + opcodes._i2c, + lambda op: 'I2C_%s %s, %s, %s, %s' % ('RD' if op.rw == 0 else 'WR', op.sub_addr, op.high, op.low, op.i2c_sel) + ), + opcodes.OPCODE_LD: ('LD', opcodes._ld, lambda op: 'LD r%s, r%s, %s' % (op.dreg, op.sreg, op.offset)), + opcodes.OPCODE_ST: ('ST', opcodes._st, lambda op: 'ST r%s, r%s, %s' % (op.sreg, op.dreg, op.offset)), + opcodes.OPCODE_RD_REG: ( + 'RD_REG', + opcodes._rd_reg, + lambda op: 'REG_RD 0x%x, %s, %s' % (op.periph_sel << 8 | op.addr, op.high, op.low) + ), + opcodes.OPCODE_WR_REG: ( + 'WR_REG', + opcodes._wr_reg, + lambda op: 'REG_WR 0x%x, %s, %s, %s' % (op.periph_sel << 8 | op.addr, op.high, op.low, op.data) + ), + opcodes.OPCODE_TSENS: ('TSENS', opcodes._tsens, lambda op: 'TSENS r%s, %s' % (op.dreg, op.delay)), +} + + +def decode_instruction(i): + if i == 0: + raise Exception('') + + ins = opcodes._end + ins.all = i # abuse a struct to get opcode + + params = lookup.get(ins.opcode, None) + + if not params: + raise Exception('Unknown instruction') + + if len(params) == 3: + name, ins, third = params + ins.all = i + + if callable(third): + params = (third(ins), ins) + else: + params = third.get(ins.sub_opcode, ()) + + if len(params) == 3: + name, ins, pretty = params + ins.all = i + name = pretty(ins) + else: + name, ins = params + ins.all = i + + return ins, name + + +def get_instruction_fields(ins): + possible_fields = ( + 'addr', 'cmp', 'cycle_sel', 'cycles', 'data', 'delay', 'dreg', + 'high', 'i2c_sel', 'imm', 'low', 'mux', 'offset', 'opcode', + 'periph_sel', 'reg', 'rw', 'sar_sel', 'sel', 'sign', 'sreg', + 'sub_addr', 'sub_opcode', 'treg', 'type', 'unused', 'unused1', + 'unused2', 'wakeup' + ) + field_details = [] + for field in possible_fields: + extra = '' + try: + # eval is ugly but constrained to possible_fields and variable ins + val = eval('i.%s' % field, {}, {'i': ins}) + if (val>9): + extra = ' (0x%02x)' % val + except KeyError: + continue + + if field == 'sel': # ALU + if ins.sub_opcode == opcodes.SUB_OPCODE_ALU_CNT: + extra = ' (%s)' % alu_cnt_ops[val] + else: + extra = ' (%s)' % alu_ops[val] + elif field == 'type': # JUMP + extra = ' (%s)' % jump_types[val] + elif field == 'cmp': # JUMPR/JUMPS + extra = ' (%s)' % cmp_ops[val] + + field_details.append((field, val, extra)) + + return field_details diff --git a/tools/decode_s2.py b/tools/decode_s2.py new file mode 100644 index 0000000..f25c5b0 --- /dev/null +++ b/tools/decode_s2.py @@ -0,0 +1,193 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +import esp32_ulp.opcodes_s2 as opcodes + + +alu_cnt_ops = ('STAGE_INC', 'STAGE_DEC', 'STAGE_RST') +alu_ops = ('ADD', 'SUB', 'AND', 'OR', 'MOVE', 'LSH', 'RSH') +jump_types = ('--', 'EQ', 'OV') +cmp_ops = ('LT', 'GT', 'EQ') +bs_cmp_ops = ('??', 'LT', '??', 'GT', 'EQ', 'LE', '??', 'GE') + +lookup = { + opcodes.OPCODE_ADC: ('ADC', opcodes._adc, lambda op: 'ADC r%s, %s, %s' % (op.dreg, op.mux, op.sar_sel)), + opcodes.OPCODE_ALU: ('ALU', opcodes._alu_imm, { + opcodes.SUB_OPCODE_ALU_CNT: ( + 'ALU_CNT', + opcodes._alu_cnt, + lambda op: '%s%s' % (alu_cnt_ops[op.sel], '' if op.sel == opcodes.ALU_SEL_STAGE_RST else ' %s' % op.imm) + ), + opcodes.SUB_OPCODE_ALU_IMM: ( + 'ALU_IMM', + opcodes._alu_imm, + lambda op: '%s r%s, %s' % (alu_ops[op.sel], op.dreg, op.imm) if op.sel == opcodes.ALU_SEL_MOV + else '%s r%s, r%s, %s' % (alu_ops[op.sel], op.dreg, op.sreg, op.imm) + ), + opcodes.SUB_OPCODE_ALU_REG: ( + 'ALU_REG', + opcodes._alu_reg, + lambda op: '%s r%s, r%s' % (alu_ops[op.sel], op.dreg, op.sreg) if op.sel == opcodes.ALU_SEL_MOV + else '%s r%s, r%s, r%s' % (alu_ops[op.sel], op.dreg, op.sreg, op.treg) + ), + }), + opcodes.OPCODE_BRANCH: ('BRANCH', opcodes._bx, { + opcodes.SUB_OPCODE_BX: ( + 'BX', + opcodes._bx, + lambda op: 'JUMP %s%s' % (op.addr if op.reg == 0 else 'r%s' % op.dreg, ', %s' % jump_types[op.type] + if op.type != 0 else '') + ), + opcodes.SUB_OPCODE_B: ( + 'BR', + opcodes._b, + lambda op: 'JUMPR %s, %s, %s' % ('%s%s' % ('-' if op.sign == 1 else '', op.offset), op.imm, cmp_ops[op.cmp]) + ), + opcodes.SUB_OPCODE_BS: ( + 'BS', + opcodes._bs, + lambda op: 'JUMPS %s, %s, %s' % ('%s%s' % ('-' if op.sign == 1 else '', op.offset), op.imm, bs_cmp_ops[op.cmp]) + ), + }), + opcodes.OPCODE_DELAY: ( + 'DELAY', + opcodes._delay, + lambda op: 'NOP' if op.cycles == 0 else 'WAIT %s' % op.cycles + ), + opcodes.OPCODE_END: ('END', opcodes._end, { + opcodes.SUB_OPCODE_END: ( + 'WAKE', + opcodes._end + ), + }), + opcodes.OPCODE_HALT: ('HALT', opcodes._halt), + opcodes.OPCODE_I2C: ( + 'I2C', + opcodes._i2c, + lambda op: 'I2C_%s %s, %s, %s, %s' % ('RD' if op.rw == 0 else 'WR', op.sub_addr, op.high, op.low, op.i2c_sel) + ), + opcodes.OPCODE_LD: ( + 'LD/LDH', + opcodes._ld, + lambda op: '%s r%s, r%s, %s' % ('LDH' if op.rd_upper else 'LD', op.dreg, op.sreg, twos_comp(op.offset, 11)) + ), + opcodes.OPCODE_ST: ('ST', opcodes._st, { + opcodes.SUB_OPCODE_ST_AUTO: ( + 'STI/STI32', + opcodes._st, + lambda op: 'STI32 r%s, r%s, %s' % (op.sreg, op.dreg, op.label) if op.wr_way == 0 + else 'STI r%s, r%s, %s' % (op.sreg, op.dreg, op.label) if op.label + else 'STI r%s, r%s' % (op.sreg, op.dreg) + ), + opcodes.SUB_OPCODE_ST_OFFSET: ( + 'STO', + opcodes._st, + lambda op: 'STO %s' % twos_comp(op.offset, 11) + ), + opcodes.SUB_OPCODE_ST: ( + 'ST/STH/ST32', + opcodes._st, + lambda op: '%s r%s, r%s, %s, %s' % ('STH' if op.upper else 'STL', op.sreg, op.dreg, twos_comp(op.offset, 11), op.label) if op.wr_way and op.label + else '%s r%s, r%s, %s' % ('STH' if op.upper else 'ST', op.sreg, op.dreg, twos_comp(op.offset, 11)) if op.wr_way + else 'ST32 r%s, r%s, %s, %s' % (op.sreg, op.dreg, twos_comp(op.offset, 11), op.label) + ) + }), + opcodes.OPCODE_RD_REG: ( + 'RD_REG', + opcodes._rd_reg, + lambda op: 'REG_RD 0x%x, %s, %s' % (op.periph_sel << 8 | op.addr, op.high, op.low) + ), + opcodes.OPCODE_WR_REG: ( + 'WR_REG', + opcodes._wr_reg, + lambda op: 'REG_WR 0x%x, %s, %s, %s' % (op.periph_sel << 8 | op.addr, op.high, op.low, op.data) + ), + opcodes.OPCODE_TSENS: ('TSENS', opcodes._tsens, lambda op: 'TSENS r%s, %s' % (op.dreg, op.delay)), +} + + +def twos_comp(val, bits): + """ + compute the correct value of a 2's complement + based on the number of bits in the source + """ + if (val & (1 << (bits - 1))) != 0: # if sign bit is set e.g., 8bit: 128-255 + val = val - (1 << bits) # compute negative value + return val + + +def decode_instruction(i): + if i == 0: + raise Exception('') + + ins = opcodes._end + ins.all = i # abuse a struct to get opcode + + params = lookup.get(ins.opcode, None) + + if not params: + raise Exception('Unknown instruction') + + if len(params) == 3: + name, ins, third = params + ins.all = i + + if callable(third): + params = (third(ins), ins) + else: + params = third.get(ins.sub_opcode, ()) + + if len(params) == 3: + name, ins, pretty = params + ins.all = i + name = pretty(ins) + else: + name, ins = params + ins.all = i + + return ins, name + + +def get_instruction_fields(ins): + possible_fields = ( + 'addr', 'cmp', 'cycle_sel', 'cycles', 'data', 'delay', 'dreg', + 'high', 'i2c_sel', 'imm', 'low', 'mux', 'offset', 'opcode', + 'periph_sel', 'reg', 'rw', 'sar_sel', 'sel', 'sign', 'sreg', + 'sub_addr', 'sub_opcode', 'treg', 'type', 'unused', 'unused1', + 'unused2', 'wakeup', + 'rd_upper', 'label', 'upper', 'wr_way', + ) + field_details = [] + for field in possible_fields: + extra = '' + try: + # eval is ugly but constrained to possible_fields and variable ins + val = eval('i.%s' % field, {}, {'i': ins}) + if (val>9): + extra = ' (0x%02x)' % val + except KeyError: + continue + + if field == 'sel': # ALU + if ins.sub_opcode == opcodes.SUB_OPCODE_ALU_CNT: + extra = ' (%s)' % alu_cnt_ops[val] + else: + extra = ' (%s)' % alu_ops[val] + elif field == 'type': # JUMP + extra = ' (%s)' % jump_types[val] + elif field == 'cmp': # JUMPR/JUMPS + if ins.sub_opcode == opcodes.SUB_OPCODE_BS: + extra = ' (%s)' % bs_cmp_ops[val] + else: + extra = ' (%s)' % cmp_ops[val] + elif field == 'offset': + if ins.opcode in (opcodes.OPCODE_ST, opcodes.OPCODE_LD): + val = twos_comp(val, 11) + + field_details.append((field, val, extra)) + + return field_details diff --git a/tools/disassemble.py b/tools/disassemble.py new file mode 100644 index 0000000..774771f --- /dev/null +++ b/tools/disassemble.py @@ -0,0 +1,206 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +from uctypes import struct, addressof, LITTLE_ENDIAN, UINT16, UINT32 +import ubinascii +import sys + + +# placeholders: +# these functions will be dynamically loaded later based on the chosen cpu +decode_instruction, get_instruction_fields = None, None + + +def load_decoder(cpu): + if cpu == 'esp32': + mod = 'decode' + elif cpu == 'esp32s2': + mod = 'decode_s2' + else: + raise ValueError('Invalid cpu') + + relative_import = 1 if '/' in __file__ else 0 + decode = __import__(mod, globals(), locals(), [], relative_import) + + global decode_instruction, get_instruction_fields + decode_instruction = decode.decode_instruction + get_instruction_fields = decode.get_instruction_fields + + +def chunk_into_words(code, bytes_per_word, byteorder): + chunks = [ + ubinascii.hexlify(code[i:i + bytes_per_word]) + for i in range(0, len(code), bytes_per_word) + ] + + words = [int.from_bytes(ubinascii.unhexlify(i), byteorder) for i in chunks] + + return words + + +def print_ulp_header(h): + print('header') + print('ULP magic : %s (0x%08x)' % (h.magic.to_bytes(4, 'little'), h.magic)) + print('.text offset : %s (0x%02x)' % (h.text_offset, h.text_offset)) + print('.text size : %s (0x%02x)' % (h.text_size, h.text_size)) + print('.data offset : %s (0x%02x)' % (h.text_offset+h.text_size, h.text_offset+h.text_size)) + print('.data size : %s (0x%02x)' % (h.data_size, h.data_size)) + print('.bss size : %s (0x%02x)' % (h.bss_size, h.bss_size)) + print('----------------------------------------') + + +def print_code_line(byte_offset, i, asm): + lineformat = '{0:04x} {1} {2}' + hex = ubinascii.hexlify(i.to_bytes(4, 'little')) + print(lineformat.format(byte_offset, hex.decode('utf-8'), asm)) + + +def decode_instruction_and_print(byte_offset, i, verbose=False): + try: + ins, name = decode_instruction(i) + except Exception as e: + print_code_line(byte_offset, i, e) + return + + print_code_line(byte_offset, i, name) + + if verbose: + for field, val, extra in get_instruction_fields(ins): + print(" {:10} = {:3}{}".format(field, val, extra)) + + +def print_text_section(code, verbose=False): + print('.text') + + words = chunk_into_words(code, bytes_per_word=4, byteorder='little') + + for idx, i in enumerate(words): + decode_instruction_and_print(idx << 2,i , verbose) + + +def print_data_section(data_offset, code): + print('.data') + + words = chunk_into_words(code, bytes_per_word=4, byteorder='little') + + for idx, i in enumerate(words): + asm = "" if i == 0 else "" + print_code_line(data_offset + (idx << 2), i, asm) + + +def disassemble_manually(byte_sequence_string, cpu, verbose=False): + load_decoder(cpu) + + sequence = byte_sequence_string.strip().replace(' ','') + chars_per_instruction = 8 + list = [ + sequence[i:i+chars_per_instruction] + for i in range(0, len(sequence), chars_per_instruction) + ] + + for idx, instruction in enumerate(list): + byte_sequence = ubinascii.unhexlify(instruction.replace(' ','')) + i = int.from_bytes(byte_sequence, 'little') + decode_instruction_and_print(idx << 2, i, verbose) + + +def disassemble_file(filename, cpu, verbose=False): + load_decoder(cpu) + + with open(filename, 'rb') as f: + data = f.read() + + binary_header_struct_def = dict( + magic = 0 | UINT32, + text_offset = 4 | UINT16, + text_size = 6 | UINT16, + data_size = 8 | UINT16, + bss_size = 10 | UINT16, + ) + h = struct(addressof(data), binary_header_struct_def, LITTLE_ENDIAN) + + if (h.magic != 0x00706c75): + print('Invalid signature: 0x%08x (should be: 0x%08x)' % (h.magic, 0x00706c75)) + return + + if verbose: + print_ulp_header(h) + + code = data[h.text_offset:(h.text_offset+h.text_size)] + print_text_section(code, verbose) + + if verbose: + print('----------------------------------------') + + data_offset = h.text_offset+h.text_size + code = data[data_offset:(data_offset+h.data_size)] + print_data_section(data_offset-h.text_offset, code) + + +def print_help(): + print('Usage: disassemble.py [] [-m | ]') + print('') + print('Options:') + print(' -c Choose ULP variant: either esp32 or esp32s2') + print(' -h Show this help text') + print(' -m Sequence of hex bytes (8 per instruction)') + print(' -v Verbose mode. Show ULP header and fields of each instruction') + print(' Path to ULP binary') + pass + + +def handle_cmdline(params): + cpu = 'esp32' + verbose = False + filename = None + byte_sequence = None + + while params: + if params[0] == '-h': + print_help() + sys.exit(0) + elif params[0] == '-c': + cpu = params[1] + params = params[1:] # remove first param from list + elif params[0] == '-m': + if len(params) == 1: + print_help() + sys.exit(1) + params = params[1:] # remove -m from list + + sequence_len = len(params) + for i in range(0, len(params)): + if params[i][0] == '-': # start of a next option + sequence_len = i-1 + break + + if sequence_len < 0: + print_help() + sys.exit(1) + + byte_sequence = "".join(params[:sequence_len+1]) + params = params[sequence_len:] + elif params[0] == '-v': + verbose = True + elif params[0][0] == '-': + # ignore unknown options for now + pass + else: + if not filename: + filename = params[0] + + params = params[1:] # remove first param from list + + + if byte_sequence: + disassemble_manually(byte_sequence, cpu, verbose) + elif filename: + disassemble_file(filename, cpu, verbose) + + +if sys.argv: # if run from cmdline + handle_cmdline(sys.argv[1:]) diff --git a/tools/esp32_ulp b/tools/esp32_ulp new file mode 120000 index 0000000..0bc67d4 --- /dev/null +++ b/tools/esp32_ulp @@ -0,0 +1 @@ +../esp32_ulp \ No newline at end of file diff --git a/tools/genpkgjson.py b/tools/genpkgjson.py new file mode 100644 index 0000000..7572f85 --- /dev/null +++ b/tools/genpkgjson.py @@ -0,0 +1,57 @@ +# +# This file is part of the micropython-esp32-ulp project, +# https://github.com/micropython/micropython-esp32-ulp +# +# SPDX-FileCopyrightText: 2018-2023, the micropython-esp32-ulp authors, see AUTHORS file. +# SPDX-License-Identifier: MIT + +""" +Tool for generating package.json for the MIP package manager + +Run this tool from the repo root, like: + +python tools/genpkgjson.py > package.json + +Note: +This tool works with both python3 and micropyton. +""" + +import os +import json + +PACKAGE_JSON_VERSION=1 + +# Update the repo when working with a fork +GITHUB_REPO="micropython/micropython-esp32-ulp" + + +def get_files(path): + files = [f'{path}/{f}' for f in os.listdir(path)] + return files + + +def build_urls(repo_base, files): + return [[f, f'github:{repo_base}/{f}'] for f in files] + + +def print_package_json(urls): + """ + Custom-formatting JSON output for better readability + + json.dumps in MicroPython cannot format the output and python3 + puts each element of each urls' sub-arrays onto a new line. + Here we print each file and its source url onto the same line. + """ + print('{') + print(f' "v":{PACKAGE_JSON_VERSION},') + print(' "urls":[') + print(',\n'.join([f' {json.dumps(u)}' for u in sorted(urls)])) + print(' ]') + print('}') + + +if __name__ == '__main__': + library_root = 'esp32_ulp' + files = get_files(library_root) + urls = build_urls(GITHUB_REPO, files) + print_package_json(urls)