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..bf54464 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-22.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..33cec42 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...]]] @@ -203,13 +219,13 @@ def fill(self, section, amount, fill_byte): raise ValueError('fill in bss section not allowed') if section is TEXT: # TODO: text section should be filled with NOPs raise ValueError('fill/skip/align in text section not supported') - fill = int(fill_byte or 0).to_bytes(1, 'little') * amount + fill = int(self.opcodes.eval_arg(str(fill_byte or 0))).to_bytes(1, 'little') * amount self.offsets[section] += len(fill) if section is not BSS: self.sections[section].append(fill) def d_skip(self, amount, fill=None): - amount = int(amount) + amount = int(self.opcodes.eval_arg(amount)) self.fill(self.section, amount, fill) d_space = d_skip @@ -223,14 +239,14 @@ 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): self.symbols.set_global(symbol) def append_data(self, wordlen, args): - data = [int(arg).to_bytes(wordlen, 'little') for arg in args] + data = [int(self.opcodes.eval_arg(arg)).to_bytes(wordlen, 'little') for arg in args] self.append_section(b''.join(data)) def d_byte(self, *args): @@ -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..03849a3 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 """ @@ -6,7 +13,7 @@ from uctypes import struct, addressof, LITTLE_ENDIAN, UINT32, BFUINT32, BF_POS, BF_LEN from .soc import * -from .util import split_tokens, validate_expression +from .util import split_tokens, validate_expression, parse_int # XXX dirty hack: use a global for the symbol table symbols = None @@ -278,7 +285,12 @@ def eval_arg(arg): _, _, sym_value = symbols.get_sym(token) parts.append(str(sym_value)) else: - parts.append(token) + try: + # attempt to parse, to convert numbers with base prefix correctly + int_token = parse_int(token) + parts.append(str(int_token)) + except ValueError: + parts.append(token) parts = "".join(parts) if not validate_expression(parts): raise ValueError('Unsupported expression: %s' % parts) @@ -304,7 +316,7 @@ def arg_qualify(arg): if arg_lower in ['--', 'eq', 'ov', 'lt', 'gt', 'ge', 'le']: return ARG(COND, arg_lower, arg) try: - return ARG(IMM, int(arg), arg) + return ARG(IMM, parse_int(arg), arg) except ValueError: pass try: @@ -379,7 +391,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 +406,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..3a9d643 --- /dev/null +++ b/esp32_ulp/opcodes_s2.py @@ -0,0 +1,868 @@ +# +# 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, parse_int + +# 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: + try: + # attempt to parse, to convert numbers with base prefix correctly + int_token = parse_int(token) + parts.append(str(int_token)) + except ValueError: + 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, parse_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..5f1628d 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,27 +56,39 @@ 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 +def parse_int(literal): + """ + GNU as compatible parsing of string literals into integers + Specifically, GNU as treats literals starting with 0 as octal + All other literals are correctly parsed by Python + See: https://sourceware.org/binutils/docs/as/Integers.html + """ + if len(literal) >= 2 and (literal.startswith("0") or literal.startswith("-0")) and literal.lstrip("-0").isdigit(): + return int(literal, 8) + return int(literal, 0) + + def file_exists(filename): try: os.stat(filename) 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/fade_s2.py b/examples/fade_s2.py new file mode 100644 index 0000000..5f8d4ca --- /dev/null +++ b/examples/fade_s2.py @@ -0,0 +1,84 @@ +# +# 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 + +This example creates a PWM-like dimming effect using self-modifying ULP code. +The ULP program rewrites the `WAIT` instructions to control on/off LED durations, +simulating a variable duty cycle. + +Note: +The `WAIT` instruction uses an immediate operand (fixed value) for delay cycles. However, we can change the lower half of memory +to modify these values at runtime, simulating variable wait times via registers. +""" + +from esp32 import ULP +from machine import mem32 +from esp32_ulp import src_to_binary +from time import sleep + +source = """\ +# Pin with LED: (0 to 21) +.set led_pin, 4 + +# 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: +# Espressif Technical Reference Manual (TRM) Chapter 5.15 Register 5.63: +#define RTCIO_TOUCH_PADn_REG (DR_REG_RTCIO_BASE + 0x84 + 4 * led_pin) +#define RTCIO_TOUCH_PADn_MUX_SEL_M (BIT(19)) + +# constants from: +# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s2/include/soc/rtc_io_reg.h +#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 + +.global entry +program_init: + # connect GPIO to ULP (0: GPIO connected to digital GPIO module, 1: GPIO connected to analog RTC module) + WRITE_RTC_REG(RTCIO_TOUCH_PADn_REG, RTCIO_TOUCH_PADn_MUX_SEL_M, 1, 1); + + # enable GPIO as output, not input (this also enables a pull-down by default) + WRITE_RTC_REG(RTC_GPIO_ENABLE_REG, RTC_GPIO_ENABLE_S + led_pin, 1, 1) + +set_waits: add r0, r0, 200 # Increase r0 (delay time) + move r3, wait_off + st r0, r3, 0 # Overwrite wait_off with new delay value + + move r2, 0xFFFF + sub r1, r2, r0 # Calculate complementary delay time + move r3, wait_on + st r1, r3, 0 # Overwrite wait_on with new value + + WRITE_RTC_REG(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + led_pin, 1, 0) # turn off led (clear GPIO) +wait_off: wait 0 # Placeholder; value overwritten dynamically + WRITE_RTC_REG(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + led_pin, 1, 1) # turn on led (set GPIO) +wait_on: wait 0 # Placeholder; value overwritten dynamically + +jump set_waits # Loop program + +""" + +binary = src_to_binary(source, cpu="esp32s2") # cpu is esp32 or esp32s2 + +load_addr, entry_addr = 0, 0 + +ULP_MEM_BASE = 0x50000000 + +ulp = ULP() +ulp.load_binary(load_addr, binary) + +ulp.run(entry_addr) + +while True: + print(hex(mem32[ULP_MEM_BASE + 40])) # show that the WAIT cycles are changing + sleep(0.5) 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/examples/tsens_s2.py b/examples/tsens_s2.py new file mode 100644 index 0000000..58594e6 --- /dev/null +++ b/examples/tsens_s2.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-S2 + +Example showing how to use the TSENS instruction from the ULP +and access temperature data from the main CPU. + +Note that the temperature sensor clock needs to be enabled for the TSENS instruction to complete. + +""" + +from esp32 import ULP +from machine import mem32 +from esp32_ulp import src_to_binary +from time import sleep + +source = """\ +# constants from: +# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s2/include/soc/reg_base.h +#define DR_REG_SENS_BASE 0x3f408800 + +# constants from: +# https://github.com/espressif/esp-idf/blob/v5.0.2/components/soc/esp32s2/include/soc/sens_reg.h +#define SENS_SAR_TSENS_CTRL2_REG (DR_REG_SENS_BASE + 0x0054) +#define SENS_TSENS_CLKGATE_EN_M (BIT(15)) + +.set token, 0xACED + +.text +magic: .long 0 +temperature_data: .long 0 + +.global entry +entry: + move r3, magic + ld r0, r3, 0 + jumpr start, token, eq #check if we have already initialized + +init: + # Set SENS_TSENS_CLKGATE_EN to enable temperature sensor clock. + WRITE_RTC_REG(SENS_SAR_TSENS_CTRL2_REG, SENS_TSENS_CLKGATE_EN_M, 1, 1) + + # Store temperature_data memory location in r2 + move r2, temperature_data + + # store that we're done with initialisation + move r0, token + st r0, r3, 0 + +start: + tsens r0, 1000 # make measurement for 1000 clock cycles + st r0, r2, 0 # store the temperature in memory to be read by main CPU + 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: + magic_token = hex(mem32[ULP_MEM_BASE + load_addr] & ULP_DATA_MASK) + current_temperature = 0.4386*(mem32[ULP_MEM_BASE + load_addr + 4] & ULP_DATA_MASK)-20.52 + print(magic_token, current_temperature) + sleep(0.5) \ No newline at end of file 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..f8109b5 100644 --- a/tests/opcodes.py +++ b/tests/opcodes.py @@ -1,6 +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 + 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 +from esp32_ulp.opcodes import get_reg, get_imm, get_cond, arg_qualify, parse_int, eval_arg, ARG, REG, IMM, SYM, COND from esp32_ulp.assemble import SymbolTable, ABS, REL, TEXT import esp32_ulp.opcodes as opcodes @@ -39,6 +46,7 @@ def test_arg_qualify(): 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('0100') == ARG(IMM, 64, '0100') assert arg_qualify('0o100') == ARG(IMM, 64, '0o100') assert arg_qualify('0b1000') == ARG(IMM, 8, '0b1000') assert arg_qualify('eq') == ARG(COND, 'eq', 'eq') @@ -89,6 +97,11 @@ def test_eval_arg(): assert eval_arg('const >> 1') == 21 assert eval_arg('(const|4)&0xf') == 0xe + assert eval_arg('0x7') == 7 + assert eval_arg('010') == 8 + assert eval_arg('-0x7') == -7 # negative + assert eval_arg('~0x7') == -8 # complement + assert_raises(ValueError, eval_arg, 'evil()') assert_raises(ValueError, eval_arg, 'def cafe()') assert_raises(ValueError, eval_arg, '1 ^ 2') @@ -98,14 +111,17 @@ def test_eval_arg(): opcodes.symbols = None -def assert_raises(exception, func, *args): +def assert_raises(exception, func, *args, message=None): try: func(*args) - except exception: + except exception as e: raised = True + actual_message = e.args[0] else: raised = False assert raised + if message: + assert actual_message == message, '%s == %s' % (actual_message, message) def test_reg_direct_ulp_addressing(): @@ -174,6 +190,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 +224,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..b9d74d3 --- /dev/null +++ b/tests/opcodes_s2.py @@ -0,0 +1,279 @@ +# +# 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, parse_int, 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('0100') == ARG(IMM, 64, '0100') + 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 eval_arg('0x7') == 7 + assert eval_arg('010') == 8 + assert eval_arg('-0x7') == -7 # negative + assert eval_arg('~0x7') == -8 # complement + + 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, message=None): + try: + func(*args) + except exception as e: + raised = True + actual_message = e.args[0] + else: + raised = False + assert raised + if message: + assert actual_message == message, '%s == %s' % (actual_message, message) + + +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..5f487ae 100644 --- a/tests/util.py +++ b/tests/util.py @@ -1,5 +1,12 @@ +# +# 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 +from esp32_ulp.util import split_tokens, validate_expression, parse_int, file_exists tests = [] @@ -11,6 +18,19 @@ def test(param): tests.append(param) +def assert_raises(exception, func, *args, message=None): + try: + func(*args) + except exception as e: + raised = True + actual_message = e.args[0] + else: + raised = False + assert raised + if message: + assert actual_message == message, '%s == %s' % (actual_message, message) + + @test def test_split_tokens(): assert split_tokens("") == [] @@ -44,6 +64,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 +79,37 @@ 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 +def test_parse_int(): + # decimal + assert parse_int("0") == 0, "0 == 0" + assert parse_int("5") == 5, "5 == 5" + assert parse_int("-0") == 0, "-0 == 0" + assert parse_int("-5") == -5, "-5 == -5" + # hex + assert parse_int("0x5") == 5, "0x5 == 5" + assert parse_int("0x5a") == 90, "0x5a == 90" + assert parse_int("-0x5a") == -90, "-0x5a == -90" + # binary + assert parse_int("0b1001") == 9, "0b1001 == 9" + assert parse_int("-0b1001") == -9, "-0b1001 == 9" + # octal + assert parse_int("07") == 7, "07 == 7" + assert parse_int("0100") == 64, "0100 == 64" + assert parse_int("0o210") == 136, "0o210 == 136" + assert parse_int("00000010") == 8, "00000010 == 8" + assert parse_int("-07") == -7, "-07 == -7" + assert parse_int("-0100") == -64, "-0100 == -64" + assert parse_int("-0o210") == -136, "-0o210 == -136" + assert parse_int("-00000010") == -8, "-00000010 == -8" + # negative cases + assert_raises(ValueError, parse_int, '0b123', message="invalid syntax for integer with base 2: '123'") + assert_raises(ValueError, parse_int, '0900', message="invalid syntax for integer with base 8: '0900'") + assert_raises(ValueError, parse_int, '0o900', message="invalid syntax for integer with base 8: '900'") + assert_raises(ValueError, parse_int, '0xg', message="invalid syntax for integer with base 16: 'g'") @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) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy