From 1cc5a419199096127e322520ad8dc2c28406677a Mon Sep 17 00:00:00 2001 From: Tobias Schmale Date: Wed, 20 Mar 2024 20:54:19 +0100 Subject: [PATCH 01/10] first proof of concept (WIP) --- .github/workflows/publish.yml | 49 ---------- .github/workflows/run_tests.yaml | 107 ---------------------- MANIFEST.in | 11 --- esp32_ulp/assemble.py | 5 + esp32_ulp/link.py | 53 +++++++---- esp32_ulp/opcodes_s2.py | 27 ++++-- esp32_ulp/preprocess.py | 10 +- esp32_ulp/util.py | 2 +- sdist_upip.py | 151 ------------------------------- setup.py | 48 ---------- 10 files changed, 67 insertions(+), 396 deletions(-) delete mode 100644 .github/workflows/publish.yml delete mode 100644 .github/workflows/run_tests.yaml delete mode 100644 MANIFEST.in delete mode 100644 sdist_upip.py delete mode 100644 setup.py diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index d541818..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,49 +0,0 @@ -# -# 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: - # trigger when publishing a release - release: - types: [published] - - # also allow triggering this workflow manually for testing - workflow_dispatch: - -jobs: - publish: - - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - # just fetching 1 commit is not enough for setuptools-scm, so we fetch all - fetch-depth: 0 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Install dependencies - run: | - pip install setuptools setuptools_scm - - name: Build package - run: | - python setup.py sdist - rm dist/*.orig # clean sdist_upip noise - - name: Publish to Test PyPI - uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.TEST_PYPI_API_TOKEN }} - repository_url: https://test.pypi.org/legacy/ - - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 - if: github.event.release.tag_name # only when releasing a new version - with: - password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/run_tests.yaml b/.github/workflows/run_tests.yaml deleted file mode 100644 index 72f6917..0000000 --- a/.github/workflows/run_tests.yaml +++ /dev/null @@ -1,107 +0,0 @@ -# -# 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-20.04 - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 1 - - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install -y git build-essential libffi-dev pkg-config python3 bison flex xxd - - - name: Record version - run: | - export VER=$(git describe --always --tags) - echo ${VER} - - ###### Install tools ###### - - - name: Build MicroPython - id: build_micropython - run: | - echo "Building micropython" - git clone --depth 1 https://github.com/micropython/micropython.git - pushd micropython/mpy-cross - make - popd - pushd micropython/ports/unix - git describe --always --tags - make submodules - make - export OUTDIR=$PWD/build-standard - export PATH=$PATH:$OUTDIR - echo "bin_dir=$OUTDIR" >> $GITHUB_OUTPUT - test $(micropython -c 'print("test")') = "test" - popd - - - name: Fetch binutils-esp32ulp - id: fetch_binutils - run: | - 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 - - ###### Run tests ###### - - - name: Run unit tests - id: unit_tests - run: | - export PATH=$PATH:${{ steps.build_micropython.outputs.bin_dir }} - cd tests - ./00_unit_tests.sh - - - name: Run compat tests - id: compat_tests - run: | - export PATH=$PATH:${{ steps.build_micropython.outputs.bin_dir }} - export PATH=$PATH:${{ steps.fetch_binutils.outputs.bin_dir }} - cd tests - ./01_compat_tests.sh - - - name: Run compat tests with RTC macros - id: compat_rtc_tests - run: | - export PATH=$PATH:${{ steps.build_micropython.outputs.bin_dir }} - export PATH=$PATH:${{ steps.fetch_binutils.outputs.bin_dir }} - cd tests - ./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/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 5496a0e..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,11 +0,0 @@ -# stuff we need to include into the sdist is handled automatically by -# setuptools_scm - it includes all git-committed files. -# but we want only the main source code and exclude everything else -# to not waste space on the esp32 -# (upip packages are not installable by pip on a PC, so on a PC one -# would git clone anyway and get all the other files) -exclude * # exclude all files in repo root -prune .github -prune docs -prune examples -prune tests diff --git a/esp32_ulp/assemble.py b/esp32_ulp/assemble.py index 8b79071..213c63d 100644 --- a/esp32_ulp/assemble.py +++ b/esp32_ulp/assemble.py @@ -23,6 +23,7 @@ def __init__(self, symbols, bases, globals): self._symbols = symbols self._bases = bases self._globals = globals + self._from_section, self._from_offset = None, None def set_bases(self, bases): self._bases = bases @@ -294,6 +295,7 @@ def assembler_pass(self, lines): result = (result,) for instruction in result: + print("appending instruction", instruction) self.append_section(instruction.to_bytes(4, 'little'), TEXT) continue raise ValueError('Unknown opcode or directive: %s' % opcode) @@ -305,7 +307,10 @@ def assemble(self, text, remove_comments=True): self.assembler_pass(lines) self.symbols.set_bases(self.compute_bases()) garbage_collect('before pass2') + import gc + gc.disable() self.init(2) # now we know all symbols and bases, do the real assembler pass, pass 2 self.assembler_pass(lines) garbage_collect('after pass2') + gc.enable() diff --git a/esp32_ulp/link.py b/esp32_ulp/link.py index f0d81df..6425442 100644 --- a/esp32_ulp/link.py +++ b/esp32_ulp/link.py @@ -5,29 +5,44 @@ # 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 - +#from uctypes import struct, addressof, LITTLE_ENDIAN, UINT16, UINT32 +import struct def make_binary(text, data, bss_size): if not isinstance(text, bytes): raise TypeError('text section must be binary bytes') if not isinstance(data, bytes): raise TypeError('data section must be binary bytes') - binary_header_struct_def = dict( - magic = 0 | UINT32, - text_offset = 4 | UINT16, - text_size = 6 | UINT16, - data_size = 8 | UINT16, - bss_size = 10 | UINT16, - ) - header = bytearray(12) - h = struct(addressof(header), binary_header_struct_def, LITTLE_ENDIAN) - # https://github.com/espressif/esp-idf/blob/master/components/ulp/ld/esp32.ulp.ld - # ULP program binary should have the following format (all values little-endian): - h.magic = 0x00706c75 # (4 bytes) - h.text_offset = 12 # offset of .text section from binary start (2 bytes) - h.text_size = len(text) # size of .text section (2 bytes) - h.data_size = len(data) # size of .data section (2 bytes) - h.bss_size = bss_size # size of .bss section (2 bytes) - return bytes(header) + text + data + + text = text[4:] + + format_str = " 1: - ext = ext[1] - else: - ext = "" - if ext != "py": - resources.append(fname) - - if resources: - print("creating resource module R.py") - resources.sort() - last_pkg = None - r_file = None - for fname in resources: - try: - pkg, res_name = fname.split("/", 1) - except ValueError: - print("not treating %s as a resource" % fname) - continue - if last_pkg != pkg: - last_pkg = pkg - if r_file: - r_file.write("}\n") - r_file.close() - r_file = open(pkg + "/R.py", "w") - r_file.write("R = {\n") - - with open(fname, "rb") as f: - r_file.write("%r: %r,\n" % (res_name, f.read())) - - if r_file: - r_file.write("}\n") - r_file.close() - - -class sdist(_sdist): - - def run(self): - self.filelist = FileList() - self.get_file_list() - make_resource_module(self.filelist.files) - - r = super().run() - - assert len(self.archive_files) == 1 - print("filtering files and recompressing with 4K dictionary") - filter_tar(self.archive_files[0]) - outbuf.seek(0) - gzip_4k(outbuf, self.archive_files[0]) - - return r - - -# For testing only -if __name__ == "__main__": - filter_tar(sys.argv[1]) - outbuf.seek(0) - gzip_4k(outbuf, sys.argv[1]) diff --git a/setup.py b/setup.py deleted file mode 100644 index 4eab8ff..0000000 --- a/setup.py +++ /dev/null @@ -1,48 +0,0 @@ -# -# 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 - - -def long_desc_from_readme(): - with open('README.rst', 'r') as fd: - long_description = fd.read() - - # remove badges - long_description = re.compile(r'^\.\. start-badges.*^\.\. end-badges', re.M | re.S).sub('', long_description) - - # strip links. keep link name and use literal text formatting - long_description = re.sub(r'`([^<`]+) ]+>`_', '``\\1``', long_description) - - return long_description - - -setup( - name="micropython-esp32-ulp", - use_scm_version={ - 'local_scheme': 'no-local-version', - }, - description="Assembler toolchain for the ESP32 ULP co-processor, written in MicroPython", - long_description=long_desc_from_readme(), - long_description_content_type='text/x-rst', - url="https://github.com/micropython/micropython-esp32-ulp", - license="MIT", - author="micropython-esp32-ulp authors", - author_email="tw@waldmann-edv.de", - maintainer="micropython-esp32-ulp authors", - maintainer_email="tw@waldmann-edv.de", - classifiers=[ - 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python :: Implementation :: MicroPython', - ], - setup_requires=['setuptools_scm'], - platforms=["esp32", "linux", "darwin"], - cmdclass={"sdist": sdist_upip.sdist}, - packages=["esp32_ulp"], -) From a16ebf0658f6165fa9febb86fa68584a39385570 Mon Sep 17 00:00:00 2001 From: Sola85 Date: Fri, 22 Mar 2024 22:00:48 +0100 Subject: [PATCH 02/10] Update README.rst --- README.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index 6b650fa..45494f2 100644 --- a/README.rst +++ b/README.rst @@ -1,11 +1,11 @@ -.. start-badges +=================== +This project is WIP +=================== -.. image:: ../../actions/workflows/run_tests.yaml/badge.svg - :height: 20px - :target: ../../actions/workflows/run_tests.yaml - :alt: Build Status +The goal is to be able to write ESP-ULP Assembly code and run it from within circuit python. +This requires `my branch `_ of the circuit python repo, since the `espulp` module in default circuit python is basically not functional. -.. end-badges +The readme below is outdated. ===================== micropython-esp32-ulp From a64004255a1dc4bd57fe8301478d41b5c5369b51 Mon Sep 17 00:00:00 2001 From: Tobias Schmale Date: Thu, 9 May 2024 21:09:29 +0200 Subject: [PATCH 03/10] update examples --- README.rst | 22 +---- demo.S | 79 ------------------ demo.sh | 8 -- examples/README.md | 7 ++ examples/main_blink1.py | 107 +++++++++++++++++++++++++ examples/main_blink2.py | 70 ++++++++++++++++ examples/main_blink3.py | 64 +++++++++++++++ examples/main_counter.py | 36 +++++++++ examples/main_counter_precompiled.py | 21 +++++ examples/main_read_gpio1.py | 82 +++++++++++++++++++ examples/main_read_gpio2.py | 63 +++++++++++++++ examples/main_read_gpio3.py | 85 ++++++++++++++++++++ examples/main_wake_from_ulp1.py | 75 +++++++++++++++++ examples/main_wake_from_ulp2.py | 86 ++++++++++++++++++++ examples/{ => original}/blink.py | 0 examples/{ => original}/blink_s2.py | 0 examples/{ => original}/counter.py | 0 examples/{ => original}/counter_s2.py | 0 examples/{ => original}/readgpio.py | 0 examples/{ => original}/readgpio_s2.py | 0 examples/{ => original}/readgpio_s3.py | 0 package.json | 19 ----- 22 files changed, 700 insertions(+), 124 deletions(-) delete mode 100644 demo.S delete mode 100644 demo.sh create mode 100644 examples/README.md create mode 100644 examples/main_blink1.py create mode 100644 examples/main_blink2.py create mode 100644 examples/main_blink3.py create mode 100644 examples/main_counter.py create mode 100644 examples/main_counter_precompiled.py create mode 100644 examples/main_read_gpio1.py create mode 100644 examples/main_read_gpio2.py create mode 100644 examples/main_read_gpio3.py create mode 100644 examples/main_wake_from_ulp1.py create mode 100644 examples/main_wake_from_ulp2.py rename examples/{ => original}/blink.py (100%) rename examples/{ => original}/blink_s2.py (100%) rename examples/{ => original}/counter.py (100%) rename examples/{ => original}/counter_s2.py (100%) rename examples/{ => original}/readgpio.py (100%) rename examples/{ => original}/readgpio_s2.py (100%) rename examples/{ => original}/readgpio_s3.py (100%) delete mode 100644 package.json diff --git a/README.rst b/README.rst index 45494f2..c701794 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,9 @@ This project is WIP The goal is to be able to write ESP-ULP Assembly code and run it from within circuit python. This requires `my branch `_ of the circuit python repo, since the `espulp` module in default circuit python is basically not functional. -The readme below is outdated. +The examples are tested with on esp32s3. + +The documentation in `docs`_ is outdated and the tests do not work. ===================== micropython-esp32-ulp @@ -58,23 +60,7 @@ The following features are supported: Quick start ----------- -To get going run the following directly on the ESP32: - -.. code-block:: python - - # 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') - - # Step 2: Run an example - # First, upload examples/counter.py to the ESP32. - import counter +To get going, copy the esp32_ulp folder to the lib folder on the circuitpy drive. The `examples/counter.py `_ example shows how to assemble code, load and run the resulting binary and exchange data between the ULP and the main CPU. diff --git a/demo.S b/demo.S deleted file mode 100644 index 0522924..0000000 --- a/demo.S +++ /dev/null @@ -1,79 +0,0 @@ -# -# 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 - - .set constant42, 42 - -textstart: ld r0, r1, 0 # a comment! - st r0, r1, 0 // another comment! - add r0, r1, r2 - add r0, r1, 42 - sub r0, r1, r2 - sub r0, r1, 42 - and r0, r1, r2 - and r0, r1, 42 - or r0, r1, r2 - or r0, r1, 42 - lsh r0, r1, r2 - lsh r0, r1, 42 - rsh r0, r1, r2 - rsh r0, r1, 42 - move r0, r1 - move r0, 42 - move r0, textstart # moves abs addr of textstart to r0 - move r0, constant42 - stage_rst - stage_inc 42 - stage_dec 23 - -rel_b: jumpr -1, 42, lt - jumpr rel_b, 42, LT - jumpr rel_f, 23, ge -rel_f: jumpr +1, 23, GE - jump textstart - jump 0, eq - jump 0, OV - jump r0 - jump r0, EQ - jump r0, ov - jumps -1, 42, lt - jumps +1, 23, GT - jumps 0, 0xAD, Eq - - reg_rd 0x3ff48000, 7, 0 - reg_wr 0x3ff48000, 7, 0, 42 - - i2c_rd 0x10, 7, 0, 0 - i2c_wr 0x23, 0x42, 7, 0, 1 - - adc r0, 0, 1 - - tsens r0, 42 - - nop - wait 1000 - wake - sleep 1 - halt -textend: - - .data -data0: .skip 4, 0x23 -data1: .space 4, 0x42 -data2: .skip 4 -dataw: .word 1, 2, 3, 4 -datal: .long 1, 2, 3, 4 -datab: .byte 1, 2, 3 # test alignment / fill up of section -dataend: - - .bss -bss0: .skip 4 -bss1: .skip 2 # test alignment / fill up of section -bssend: diff --git a/demo.sh b/demo.sh deleted file mode 100644 index 6dea9b6..0000000 --- a/demo.sh +++ /dev/null @@ -1,8 +0,0 @@ -# -# 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/examples/README.md b/examples/README.md new file mode 100644 index 0000000..3c21125 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,7 @@ +These examples require [my branch](https://github.com/Sola85/circuitpython/tree/improve_espulp) of circuitpython to work. + +They are tested on an esp32s3. + +`main_counter.py` should work on an esp32s2 without any changes and should work on an esp32 by simply changing the target cpu in the call to `src_to_binary`. + +All other examples require the addresses of registers to be adjusted in the main python files, as indicated by the comments in the appropriate places. \ No newline at end of file diff --git a/examples/main_blink1.py b/examples/main_blink1.py new file mode 100644 index 0000000..365f03c --- /dev/null +++ b/examples/main_blink1.py @@ -0,0 +1,107 @@ +""" +A example that blinks an LED connected to board.IO13 from the ULP. + +In order to change the pin that does the blinking, one needs to change: +- RTC_IO_TOUCH_PAD13_REG +- RTCIO_GPIO13_CHANNEL + +In order to switch to an ESP32 or ESP32S2 on needs to change: +- DR_REG_RTCIO_BASE +- The compile target in the call to src_to_binary +""" + +from esp32_ulp import src_to_binary +import espulp +import memorymap +import board +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_RTCIO_BASE 0x60008400 #for esp32s3. use 0x3f408400 for esp32s2 + +# 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_PAD13_REG (DR_REG_RTCIO_BASE + 0xB8) +#define RTC_IO_TOUCH_PAD13_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_GPIO13_CHANNEL 13 + +# When accessed from the RTC module (ULP) GPIOs need to be addressed by their channel number +.set gpio, RTCIO_GPIO13_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_PAD13_REG, RTC_IO_TOUCH_PAD13_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 + + +ulp = espulp.ULP() +ulp.halt() +ulp.set_wakeup_period(0, 500000) # wake up every 500 000us = 0.5s +ulp.run(binary, entrypoint=8, pins=[board.IO13]) # entrypoint = 8 because we have 2 variables ('magic' and 'state') + # before the main entry, each consisting of 4 bytes. + +state_variable = memorymap.AddressRange(start=0x50000004, length=4) +while True: + print(int.from_bytes(state_variable[:], "little")) + sleep(0.5) \ No newline at end of file diff --git a/examples/main_blink2.py b/examples/main_blink2.py new file mode 100644 index 0000000..e12ab0b --- /dev/null +++ b/examples/main_blink2.py @@ -0,0 +1,70 @@ +""" +An alternative implementation of the blink example. + +Here we initialize the GPIO from within circuitpython, so the assembly looks a bit simpler. +""" + +from esp32_ulp import src_to_binary, Register +import esp32_ulp.soc_s3 as soc +import espulp +import memorymap +import board +from time import sleep + + +source = """ +#define DR_REG_RTCIO_BASE 0x60008400 #for esp32s3. use 0x3f408400 for esp32s2 +#define RTC_GPIO_OUT_REG (DR_REG_RTCIO_BASE + 0x0) +#define RTC_GPIO_OUT_DATA_S 10 + +.set gpio, 13 + +.text +state: .long 0 + +.global entry +entry: + 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 + + +ulp = espulp.ULP() +ulp.halt() + +pin_number = 13 +RTC_IO_TOUCH_PAD13_REG = Register(soc.DR_REG_RTCIO_BASE + 0xB8) +RTC_IO_TOUCH_PAD13_REG.set_bit(19) # connect GPIO to ULP (bit cleared: GPIO connected to digital GPIO module, bit set: GPIO connected to analog RTC module) (19 = RTC_IO_TOUCH_PAD13_MUX_SEL_M) +RTC_GPIO_ENABLE_REG = Register(soc.DR_REG_RTCIO_BASE + 0x0C) +RTC_GPIO_ENABLE_REG.set_bit(10 + pin_number) # GPIO shall be output, not input (this also enables a pull-down by default) + +ulp.set_wakeup_period(0, 500000) +ulp.run(binary, entrypoint=4, pins=[board.IO13]) + +state_variable = memorymap.AddressRange(start=0x50000000, length=4) +while True: + print(int.from_bytes(state_variable[:], "little")) + sleep(0.5) \ No newline at end of file diff --git a/examples/main_blink3.py b/examples/main_blink3.py new file mode 100644 index 0000000..a67a722 --- /dev/null +++ b/examples/main_blink3.py @@ -0,0 +1,64 @@ +""" +A third version of the blink example. + +Here we dont rely on the wakeup period to act as a delay. +Instead we implement the delay on the ULP itself. + +Note that this version is less desirable, since the ULP is running continuously +and hence consumes much more power. +""" + + +from esp32_ulp import src_to_binary, Register +import esp32_ulp.soc_s3 as soc +import espulp +import board + + +source = """ +#define DR_REG_RTCIO_BASE 0x60008400 #for esp32s3. use 0x3f408400 for esp32s2 +#define RTC_GPIO_OUT_REG (DR_REG_RTCIO_BASE + 0x0) +#define RTC_GPIO_OUT_DATA_S 10 + +.set gpio, 13 + +.text + +.global entry +entry: + # turn on led (set GPIO) + WRITE_RTC_REG(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + gpio, 1, 1) + move r1, 100 # 100 ms delay + move r2, off + jump delay + +off: + # turn off led (clear GPIO) + WRITE_RTC_REG(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + gpio, 1, 0) + move r1, 1000 # 1000 ms delay + move r2, entry + jump delay + +delay: + wait 8000 # 8000 cycles at 8 MHz -> 1 ms + sub r1, r1, 1 + jump r2, eq # if ms count is zero, then return to caller + jump delay +""" + + +binary = src_to_binary(source, cpu="esp32s2") # cpu is esp32 or esp32s2 + + +ulp = espulp.ULP() +ulp.halt() + +pin_number = 13 +RTC_IO_TOUCH_PAD13_REG = Register(soc.DR_REG_RTCIO_BASE + 0xB8) +RTC_IO_TOUCH_PAD13_REG.set_bit(19) # connect GPIO to ULP (bit cleared: GPIO connected to digital GPIO module, bit set: GPIO connected to analog RTC module) (19 = RTC_IO_TOUCH_PAD13_MUX_SEL_M) +RTC_GPIO_ENABLE_REG = Register(soc.DR_REG_RTCIO_BASE + 0x0C) +RTC_GPIO_ENABLE_REG.set_bit(10 + pin_number) # GPIO shall be output, not input (this also enables a pull-down by default) + +# dont need to set wakeup_period for this example +# ulp.set_wakeup_period(0, 50000) +ulp.run(binary, entrypoint=0, pins=[board.IO13]) diff --git a/examples/main_counter.py b/examples/main_counter.py new file mode 100644 index 0000000..1b48148 --- /dev/null +++ b/examples/main_counter.py @@ -0,0 +1,36 @@ +""" +A simple example demonstrating how the main cpu can access variables on the ULP. +The ULP increments a variable once per second and the main cpu prints this variable. +""" + +from esp32_ulp import src_to_binary +import espulp +import memorymap +from time import sleep + + +source = """ +data: .long 123 # initial value of data variable + +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 + +ulp = espulp.ULP() +ulp.halt() +ulp.set_wakeup_period(0, 1000000) # 1 million micro seconds = wake up once per second +ulp.run(binary, entrypoint=4) + + +data_variable = memorymap.AddressRange(start=0x50000000, length=4) + +while True: + print(int.from_bytes(data_variable[:], "little")) + sleep(1) diff --git a/examples/main_counter_precompiled.py b/examples/main_counter_precompiled.py new file mode 100644 index 0000000..9ab558e --- /dev/null +++ b/examples/main_counter_precompiled.py @@ -0,0 +1,21 @@ +""" +The compiled version of the counter example, containing the compiled binary for the ESPS2/S3. +""" + +import espulp +import memorymap +from time import sleep + +binary = b'ulp\x00\x0c\x00\x18\x00\x00\x00\x00\x00{\x00\x00\x00\x03\x00\x80t\x0e\x00\x00\xd0\x1a\x00\x00t\x8e\x01\x00h\x00\x00\x00\xb0' + +ulp = espulp.ULP() +ulp.halt() +ulp.set_wakeup_period(0, 1000000) # 1 million micro seconds = wake up once per second +ulp.run(binary, entrypoint=4) + + +data_variable = memorymap.AddressRange(start=0x50000000, length=4) + +while True: + print(int.from_bytes(data_variable[:], "little")) + sleep(1) diff --git a/examples/main_read_gpio1.py b/examples/main_read_gpio1.py new file mode 100644 index 0000000..c15b68d --- /dev/null +++ b/examples/main_read_gpio1.py @@ -0,0 +1,82 @@ + +""" +Very basic example showing how to read a GPIO pin from the ULP and access +that data from the main CPU. + +In this case GPIO0 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_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/esp32s2/rtc_io_periph.c#L60 + + +This example is for the esp32s3. Change the following addresses for other targets: +- DR_REG_RTCIO_BASE +- DR_REG_SENS_BASE +""" + +import board +from esp32_ulp import src_to_binary +import memorymap +import time +import espulp + +source = """\ +#define DR_REG_RTCIO_BASE 0x60008400 # for esp32s3. modify for esp32s2 +#define RTC_IO_TOUCH_PAD0_REG (DR_REG_RTCIO_BASE + 0x84) +#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 10 +#define DR_REG_SENS_BASE 0x60008800 # for esp32s3. modify for esp32s2 +#define SENS_SAR_PERI_CLK_GATE_CONF_REG (DR_REG_SENS_BASE + 0x104) +#define SENS_IOMUX_CLK_EN (BIT(31)) +.set channel, 0 + +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_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) + + # enable correct pull resistor + WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, (BIT(27)), 1, 1) + WRITE_RTC_REG(RTC_IO_TOUCH_PAD0_REG, (BIT(28)), 1, 0) + + # 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 + + +ulp = espulp.ULP() +ulp.halt() + +ulp.set_wakeup_period(0, 50000) +ulp.run(binary, entrypoint=4, pins=[board.IO0]) + +state_variable = memorymap.AddressRange(start=0x50000000, length=4) + +while True: + print(int.from_bytes(state_variable[:], "little")) + time.sleep(1) \ No newline at end of file diff --git a/examples/main_read_gpio2.py b/examples/main_read_gpio2.py new file mode 100644 index 0000000..1326f4e --- /dev/null +++ b/examples/main_read_gpio2.py @@ -0,0 +1,63 @@ +""" +Same as the previous example, but we initialize the GPIO from circuitpython, +hence the assembly is a bit simpler. + +This example is for the esp32s3. Change the following addresses for other targets: +- DR_REG_RTCIO_BASE +- DR_REG_SENS_BASE +""" + +import board +from esp32_ulp import src_to_binary, Register +import esp32_ulp.soc_s3 as soc +import memorymap +import time +import espulp + +source = """\ +#define DR_REG_RTCIO_BASE 0x60008400 +#define RTC_GPIO_IN_REG (DR_REG_RTCIO_BASE + 0x24) + +.set channel, 0 + +state: .long 0 + +entry: + # read the GPIO's current state into r0 + READ_RTC_REG(RTC_GPIO_IN_REG, 10 + 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 + + +ulp = espulp.ULP() +ulp.halt() + + +SENS_SAR_PERI_CLK_GATE_CONF_REG = Register(soc.DR_REG_SENS_BASE+0x104) +RTC_IO_TOUCH_PAD0_REG = Register(soc.DR_REG_RTCIO_BASE + 0x84) +RTC_IN_REG = Register(soc.DR_REG_RTCIO_BASE + 0x24) + +SENS_SAR_PERI_CLK_GATE_CONF_REG.set_bit(31) # enable IOMUX clock +RTC_IO_TOUCH_PAD0_REG.clear_bit(28) # Disable pull-down +RTC_IO_TOUCH_PAD0_REG.set_bit(27) # Enable pull-up +RTC_IO_TOUCH_PAD0_REG.set_bit(19) # Connect GPIO to rtc subsystem +RTC_IO_TOUCH_PAD0_REG.set_bit(13) # Set GPIO to Input + +ulp.set_wakeup_period(0, 50000) +ulp.run(binary, entrypoint=4, pins=[board.IO0]) + +state_variable = memorymap.AddressRange(start=0x50000000, length=4) + +while True: + print(int.from_bytes(state_variable[:], "little")) + time.sleep(1) \ No newline at end of file diff --git a/examples/main_read_gpio3.py b/examples/main_read_gpio3.py new file mode 100644 index 0000000..5ea4d1a --- /dev/null +++ b/examples/main_read_gpio3.py @@ -0,0 +1,85 @@ +""" +This is a combination of the read_gpio and the blink example. + +The ULP blinks the LED on IO13 until IO0 goes low. + +To demonstrate the capability of the ULP to run while the main cpu is sleeping, +we directly go to sleep mode after everything is set up. +""" + +import board +from esp32_ulp import src_to_binary, Register +import esp32_ulp.soc_s3 as soc +import espulp +import alarm + +source = """\ +#define DR_REG_RTCIO_BASE 0x60008400 #for esp32s3. use 0x3f408400 for esp32s2 +#define RTC_GPIO_OUT_REG (DR_REG_RTCIO_BASE + 0x0) +#define RTC_GPIO_OUT_DATA_S 10 + +#define RTC_GPIO_IN_REG (DR_REG_RTCIO_BASE + 0x24) + +.set channel, 0 + +.set led, 13 + +state: .long 0 + +entry: + move r1, state + ld r0, r1, 0 + + move r2, 1 + sub r0, r2, r0 # toggle state + st r0, r1, 0 # store updated state + + move r2, r0 + + + # read the GPIO's current state into r0 + READ_RTC_REG(RTC_GPIO_IN_REG, 10 + channel, 1) + and r0, r2, r0 + + jumpr on, 0, gt # if r0 (state) > 0, jump to 'on' + jump off # else jump to 'off' + +off: + # turn off led (clear GPIO) + WRITE_RTC_REG(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + led, 1, 0) + halt + +on: + # turn on led (set GPIO) + WRITE_RTC_REG(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + led, 1, 1) + halt +""" + +binary = src_to_binary(source, cpu="esp32s2") # cpu is esp32 or esp32s2 + + +ulp = espulp.ULP() +ulp.halt() + + +SENS_SAR_PERI_CLK_GATE_CONF_REG = Register(soc.DR_REG_SENS_BASE+0x104) +RTC_IO_TOUCH_PAD0_REG = Register(soc.DR_REG_RTCIO_BASE + 0x84) +RTC_IN_REG = Register(soc.DR_REG_RTCIO_BASE + 0x24) + +SENS_SAR_PERI_CLK_GATE_CONF_REG.set_bit(31) # enable IOMUX clock +RTC_IO_TOUCH_PAD0_REG.clear_bit(28) # Disable pull-down +RTC_IO_TOUCH_PAD0_REG.set_bit(27) # Enable pull-up +RTC_IO_TOUCH_PAD0_REG.set_bit(19) # Connect GPIO to rtc subsystem +RTC_IO_TOUCH_PAD0_REG.set_bit(13) # Set GPIO to Input + +pin_number = 13 +RTC_IO_TOUCH_PAD13_REG = Register(soc.DR_REG_RTCIO_BASE + 0xB8) +RTC_IO_TOUCH_PAD13_REG.set_bit(19) # connect GPIO to ULP (bit cleared: GPIO connected to digital GPIO module, bit set: GPIO connected to analog RTC module) (19 = RTC_IO_TOUCH_PAD13_MUX_SEL_M) +RTC_GPIO_ENABLE_REG = Register(soc.DR_REG_RTCIO_BASE + 0x0C) +RTC_GPIO_ENABLE_REG.set_bit(10 + pin_number) # GPIO shall be output, not input (this also enables a pull-down by default) + +ulp.set_wakeup_period(0, 500000) +ulp.run(binary, entrypoint=4, pins=[board.IO0, board.IO13]) + + +alarm.exit_and_deep_sleep_until_alarms() diff --git a/examples/main_wake_from_ulp1.py b/examples/main_wake_from_ulp1.py new file mode 100644 index 0000000..64f6633 --- /dev/null +++ b/examples/main_wake_from_ulp1.py @@ -0,0 +1,75 @@ +""" +An example demonstrating the capability of the ULP to wake the main chip. + +Here we use the ULP to emulate a timer interrupt. The main cpu blinks an LED 3 times, +the goes to sleep. The ULP counts down from 10 and then wakes up the main cpu again. +""" + +import board +from esp32_ulp import src_to_binary +import memorymap +import time +import espulp +from digitalio import DigitalInOut +from alarm import exit_and_deep_sleep_until_alarms, wake_alarm + +source = """\ + +#define DR_REG_RTCCNTL_BASE 0x60008000 # for esp32s3, modify for esp32s2 +#define RTC_CNTL_LOW_POWER_ST_REG (DR_REG_RTCCNTL_BASE + 0xD0) +#define RTC_CNTL_RDY_FOR_WAKEUP (BIT(19)) + +state: .long 10 + +entry: + # set r3 to the memory address of "state" + move r3, state + + ld r0, r3, 0 # load data contents ([r3+0]) into r0 + sub r0, r0, 1 # decrement r0 + jump check_wakeup, eq # wake main cpu if state == 0 + + # store contents of r0 into the "state" variable + st r0, r3, 0 + + # halt ULP co-processor (until it gets woken up again) + halt + +check_wakeup: // Read RTC_CNTL_RDY_FOR_WAKEUP and RTC_CNTL_MAIN_STATE_IN_IDLE bit + READ_RTC_REG(RTC_CNTL_LOW_POWER_ST_REG, 27, 1) + MOVE r1, r0 // Copy result in to r1 + READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP) + OR r0, r0, r1 + JUMP check_wakeup, eq // Retry until either of the bit are set + WAKE // Trigger wake up + add r0, r0, 10 // increment state variable, so we dont immediately wake up again. + st r0, r3, 0 + HALT // Stop the ULP program + +""" +print("\n", wake_alarm) + +binary = src_to_binary(source, cpu="esp32s2") # cpu is esp32 or esp32s2 + +ulp = espulp.ULP() +ulp.halt() +ulp.set_wakeup_period(0, 1000000) +ulp.run(binary, entrypoint=4, pins=[]) + +led = DigitalInOut(board.IO13) # Led to indicate when main cpu is running and when it is sleeping +led.switch_to_output() + +state_variable = memorymap.AddressRange(start=0x50000000, length=4) + +#Watch state variable decrease and blink led for 3 seconds +for _ in range(3): + print(int.from_bytes(state_variable[:], "little")) + led.value = not led.value + time.sleep(1) + +# sleep. now ulp keeps decrementing its state variable and when it hits 0, we will wake up again. +led.value = 0 +exit_and_deep_sleep_until_alarms(espulp.ULPAlarm(ulp)) + + + diff --git a/examples/main_wake_from_ulp2.py b/examples/main_wake_from_ulp2.py new file mode 100644 index 0000000..1a41124 --- /dev/null +++ b/examples/main_wake_from_ulp2.py @@ -0,0 +1,86 @@ +""" +An example demonstrating the capability of the ULP to wake the main chip. + +Here we use the ULP to emulate a gpio interrupt. We blink an LED 3 times on the main cpu +and then go to sleep. +The ULP reads IO0 10 times per second and if the pin goes low, it wakes up the main cpu. +""" + +import board +from esp32_ulp import src_to_binary, Register +from esp32_ulp import soc_s3 as soc +import time +import espulp +from digitalio import DigitalInOut +from alarm import exit_and_deep_sleep_until_alarms, wake_alarm + +source = """\ + +#define DR_REG_RTCIO_BASE 0x60008400 #for esp32s3. use 0x3f408400 for esp32s2 +#define RTC_GPIO_OUT_REG (DR_REG_RTCIO_BASE + 0x0) +#define RTC_GPIO_OUT_DATA_S 10 +#define RTC_GPIO_IN_REG (DR_REG_RTCIO_BASE + 0x24) + +#define DR_REG_RTCCNTL_BASE 0x60008000 +#define RTC_CNTL_LOW_POWER_ST_REG (DR_REG_RTCCNTL_BASE + 0xD0) +#define RTC_CNTL_RDY_FOR_WAKEUP (BIT(19)) +#define button 0 + +entry: + READ_RTC_REG(RTC_GPIO_IN_REG, 10 + button, 1) + and r0, r0, 1 + jump check_wakeup, eq # wake main cpu if r0 == 0 + + # halt ULP co-processor (until it gets woken up again) + halt + +check_wakeup: // Read RTC_CNTL_RDY_FOR_WAKEUP and RTC_CNTL_MAIN_STATE_IN_IDLE bit + READ_RTC_REG(RTC_CNTL_LOW_POWER_ST_REG, 27, 1) + MOVE r1, r0 // Copy result in to r1 + READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP) + OR r0, r0, r1 + JUMP check_wakeup, eq // Retry until either of the bit are set + WAKE // Trigger wake up + jump trap + +trap: + nop + jump trap + +""" +print("\n", wake_alarm) + + +binary = src_to_binary(source, cpu="esp32s2") # cpu is esp32 or esp32s2 + +ulp = espulp.ULP() +ulp.halt() + + +SENS_SAR_PERI_CLK_GATE_CONF_REG = Register(soc.DR_REG_SENS_BASE+0x104) +RTC_IO_TOUCH_PAD0_REG = Register(soc.DR_REG_RTCIO_BASE + 0x84) +RTC_IN_REG = Register(soc.DR_REG_RTCIO_BASE + 0x24) + +SENS_SAR_PERI_CLK_GATE_CONF_REG.set_bit(31) # enable IOMUX clock +RTC_IO_TOUCH_PAD0_REG.clear_bit(28) # Disable pull-down +RTC_IO_TOUCH_PAD0_REG.set_bit(27) # Enable pull-up +RTC_IO_TOUCH_PAD0_REG.set_bit(19) # Connect GPIO to rtc subsystem +RTC_IO_TOUCH_PAD0_REG.set_bit(13) # Set GPIO to Input + +ulp.set_wakeup_period(0, 100000) +ulp.run(binary, entrypoint=0, pins=[board.IO0]) + +led = DigitalInOut(board.IO13) # Led to indicate when main cpu is running and when it is sleeping +led.switch_to_output() + +# Blink so we see the main cpu in awake +for _ in range(3): + led.value = not led.value + time.sleep(1) + +# sleep until ulp wakes us up +led.value = 0 +exit_and_deep_sleep_until_alarms(espulp.ULPAlarm(ulp)) + + + diff --git a/examples/blink.py b/examples/original/blink.py similarity index 100% rename from examples/blink.py rename to examples/original/blink.py diff --git a/examples/blink_s2.py b/examples/original/blink_s2.py similarity index 100% rename from examples/blink_s2.py rename to examples/original/blink_s2.py diff --git a/examples/counter.py b/examples/original/counter.py similarity index 100% rename from examples/counter.py rename to examples/original/counter.py diff --git a/examples/counter_s2.py b/examples/original/counter_s2.py similarity index 100% rename from examples/counter_s2.py rename to examples/original/counter_s2.py diff --git a/examples/readgpio.py b/examples/original/readgpio.py similarity index 100% rename from examples/readgpio.py rename to examples/original/readgpio.py diff --git a/examples/readgpio_s2.py b/examples/original/readgpio_s2.py similarity index 100% rename from examples/readgpio_s2.py rename to examples/original/readgpio_s2.py diff --git a/examples/readgpio_s3.py b/examples/original/readgpio_s3.py similarity index 100% rename from examples/readgpio_s3.py rename to examples/original/readgpio_s3.py diff --git a/package.json b/package.json deleted file mode 100644 index 7290bdf..0000000 --- a/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "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"] - ] -} From 9a592c852fa1244feb5b384087134ae044ea1bba Mon Sep 17 00:00:00 2001 From: Tobias Schmale Date: Thu, 9 May 2024 21:11:41 +0200 Subject: [PATCH 04/10] update readme --- README.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.rst b/README.rst index c701794..7a942d8 100644 --- a/README.rst +++ b/README.rst @@ -74,9 +74,7 @@ See `docs/index.rst `_. Requirements ------------ -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). +`This branch of circuitpython: `_ An ESP32 device is required to run the ULP machine code binary produced by micropython-esp32-ulp. From d80b623874854079f1972d862fcb4bd5c4e78984 Mon Sep 17 00:00:00 2001 From: Tobias Schmale Date: Thu, 9 May 2024 21:15:18 +0200 Subject: [PATCH 05/10] update example --- examples/main_blink3.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/main_blink3.py b/examples/main_blink3.py index a67a722..759e947 100644 --- a/examples/main_blink3.py +++ b/examples/main_blink3.py @@ -1,4 +1,6 @@ """ +Adapted from https://github.com/joba-1/Blink-ULP/blob/master/main/ulp/blink.S + A third version of the blink example. Here we dont rely on the wakeup period to act as a delay. From 1500cce33125b1c810222c14154f802dc2ccde47 Mon Sep 17 00:00:00 2001 From: Tobias Schmale Date: Sun, 26 May 2024 14:48:54 +0200 Subject: [PATCH 06/10] fix assembler for esp32 and add precompiled example for es32 --- esp32_ulp/opcodes.py | 26 +++++++++++++++++----- examples/main_counter_precompiled_esp32.py | 21 +++++++++++++++++ 2 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 examples/main_counter_precompiled_esp32.py diff --git a/esp32_ulp/opcodes.py b/esp32_ulp/opcodes.py index 10fc3d1..7e37c86 100644 --- a/esp32_ulp/opcodes.py +++ b/esp32_ulp/opcodes.py @@ -9,8 +9,8 @@ ESP32 ULP Co-Processor Instructions """ -from ucollections import namedtuple -from uctypes import struct, addressof, LITTLE_ENDIAN, UINT32, BFUINT32, BF_POS, BF_LEN +from collections import namedtuple +#from uctypes import struct, addressof, LITTLE_ENDIAN, UINT32, BFUINT32, BF_POS, BF_LEN from .soc import * from .util import split_tokens, validate_expression @@ -88,21 +88,35 @@ def make_ins_struct_def(layout): name, width = bitfield.split(':', 1) name = name.strip() width = int(width.strip()) - struct_def[name] = BFUINT32 | pos << BF_POS | width << BF_LEN + struct_def[name] = (pos, width) 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 +class Instruction: + + def __init__(self, struct_def): + self.struct_def = struct_def + + def __getattr__(self, __name): + #print("getattr called", __name) + + if __name != "all": + raise ValueError(f"Instruction does not have attribute {__name}!") + val = 0 + for key in self.struct_def: + mask = self.__dict__[key] << self.struct_def[key][0] + val |= mask + return val + 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) + return Instruction(struct_def) # instruction structure definitions diff --git a/examples/main_counter_precompiled_esp32.py b/examples/main_counter_precompiled_esp32.py new file mode 100644 index 0000000..25b0926 --- /dev/null +++ b/examples/main_counter_precompiled_esp32.py @@ -0,0 +1,21 @@ +""" +The compiled version of the counter example, containing the compiled binary for the ESPS2/S3. +""" + +import espulp +import memorymap +from time import sleep + +binary = b'ulp\x00\x0c\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x80r\x0e\x00\x00\xd0\x1a\x00\x00r\x0e\x00\x00h\x00\x00\x00\xb0' + +ulp = espulp.ULP() +ulp.halt() +ulp.set_wakeup_period(0, 1000000) # 1 million micro seconds = wake up once per second +ulp.run(binary, entrypoint=4) + + +data_variable = memorymap.AddressRange(start=0x50000000, length=2) + +while True: + print(int.from_bytes(data_variable[:], "little")) + sleep(1) From caeaf581878370213c4c2f7726d1cc52f501f5c1 Mon Sep 17 00:00:00 2001 From: Tobias Schmale Date: Sun, 26 May 2024 14:54:28 +0200 Subject: [PATCH 07/10] update readme --- README.rst | 7 +++++-- examples/README.md | 4 +++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 7a942d8..62dd993 100644 --- a/README.rst +++ b/README.rst @@ -1,11 +1,14 @@ =================== -This project is WIP +circuitpython-esp32-ulp-assembler =================== The goal is to be able to write ESP-ULP Assembly code and run it from within circuit python. This requires `my branch `_ of the circuit python repo, since the `espulp` module in default circuit python is basically not functional. -The examples are tested with on esp32s3. +A fork of the original micropython-esp32-ulp assembler was necessary, since that project uses micropython packages that are not enabled in cirucitpython. +This fork fixes this incompatibility. + +The examples are tested to work on esp32s3 and partially tested on an esp32. The documentation in `docs`_ is outdated and the tests do not work. diff --git a/examples/README.md b/examples/README.md index 3c21125..ec30403 100644 --- a/examples/README.md +++ b/examples/README.md @@ -4,4 +4,6 @@ They are tested on an esp32s3. `main_counter.py` should work on an esp32s2 without any changes and should work on an esp32 by simply changing the target cpu in the call to `src_to_binary`. -All other examples require the addresses of registers to be adjusted in the main python files, as indicated by the comments in the appropriate places. \ No newline at end of file +All other examples require the addresses of registers to be adjusted in the main python files, as indicated by the comments in the appropriate places. + +`main_counter_precompiled_esp32.py` and `main_counter_precompiled.py` contain precompiled ulp binaries for the counter example that do not require assembling. \ No newline at end of file From 4b4027d2a0bef26fc60223519709e3ccedbf1e02 Mon Sep 17 00:00:00 2001 From: Tobias Schmale Date: Sun, 26 May 2024 14:56:11 +0200 Subject: [PATCH 08/10] update readme --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 62dd993..0f7c197 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,6 @@ -=================== +================================= circuitpython-esp32-ulp-assembler -=================== +================================= The goal is to be able to write ESP-ULP Assembly code and run it from within circuit python. This requires `my branch `_ of the circuit python repo, since the `espulp` module in default circuit python is basically not functional. @@ -10,7 +10,7 @@ This fork fixes this incompatibility. The examples are tested to work on esp32s3 and partially tested on an esp32. -The documentation in `docs`_ is outdated and the tests do not work. +The documentation in `docs`_ is outdated and the tests, as well as the tools do not work. ===================== micropython-esp32-ulp From 2cfaaf974176c48076bcac2ca8d1c18790e7e4f9 Mon Sep 17 00:00:00 2001 From: Tobias Schmale Date: Sun, 26 May 2024 14:58:11 +0200 Subject: [PATCH 09/10] update readme --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 0f7c197..b3e429b 100644 --- a/README.rst +++ b/README.rst @@ -8,9 +8,9 @@ This requires `my branch Date: Sat, 14 Sep 2024 14:21:27 +0200 Subject: [PATCH 10/10] Update README.rst --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index b3e429b..a84007e 100644 --- a/README.rst +++ b/README.rst @@ -3,7 +3,7 @@ circuitpython-esp32-ulp-assembler ================================= The goal is to be able to write ESP-ULP Assembly code and run it from within circuit python. -This requires `my branch `_ of the circuit python repo, since the `espulp` module in default circuit python is basically not functional. +This requires CircuitPython 9.1.0 or later. A fork of the original micropython-esp32-ulp assembler was necessary, since that project uses micropython packages that are not enabled in cirucitpython. This fork fixes this incompatibility.