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/README.rst b/README.rst index 6b650fa..a84007e 100644 --- a/README.rst +++ b/README.rst @@ -1,11 +1,16 @@ -.. start-badges +================================= +circuitpython-esp32-ulp-assembler +================================= -.. 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 CircuitPython 9.1.0 or later. -.. end-badges +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 provided in this fork are tested to work on esp32s3 and partially tested on an esp32. + +The documentation in `docs`_ is outdated and the tests, as well as the tools are not yet updated to circuitpython. ===================== micropython-esp32-ulp @@ -58,23 +63,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. @@ -88,9 +77,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. 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/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 = " 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..759e947 --- /dev/null +++ b/examples/main_blink3.py @@ -0,0 +1,66 @@ +""" +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. +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_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) 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"] - ] -} diff --git a/sdist_upip.py b/sdist_upip.py deleted file mode 100644 index 60c0c3e..0000000 --- a/sdist_upip.py +++ /dev/null @@ -1,151 +0,0 @@ -# This module is part of Pycopy https://github.com/pfalcon/pycopy -# and pycopy-lib https://github.com/pfalcon/pycopy-lib, projects to -# create a (very) lightweight full-stack Python distribution. -# -# Copyright (c) 2016-2019 Paul Sokolovsky -# Licence: MIT -# -# This module overrides distutils (also compatible with setuptools) "sdist" -# command to perform pre- and post-processing as required for Pycopy's -# upip package manager. -# -# Preprocessing steps: -# * Creation of Python resource module (R.py) from each top-level package's -# resources. -# Postprocessing steps: -# * Removing metadata files not used by upip (this includes setup.py) -# * Recompressing gzip archive with 4K dictionary size so it can be -# installed even on low-heap targets. -# -import sys -import os -import zlib -from subprocess import Popen, PIPE -import glob -import tarfile -import re -import io - -from distutils.filelist import FileList -from setuptools.command.sdist import sdist as _sdist - - -def gzip_4k(inf, fname): - comp = zlib.compressobj(level=9, wbits=16 + 12) - with open(fname + ".out", "wb") as outf: - while 1: - data = inf.read(1024) - if not data: - break - outf.write(comp.compress(data)) - outf.write(comp.flush()) - os.rename(fname, fname + ".orig") - os.rename(fname + ".out", fname) - - -FILTERS = [ - # include, exclude, repeat - (r".+\.egg-info/(PKG-INFO|requires\.txt)", r"setup.py$"), - (r".+\.py$", r"[^/]+$"), - (None, r".+\.egg-info/.+"), -] - - -outbuf = io.BytesIO() - -def filter_tar(name): - fin = tarfile.open(name, "r:gz") - fout = tarfile.open(fileobj=outbuf, mode="w") - for info in fin: -# print(info) - if not "/" in info.name: - continue - fname = info.name.split("/", 1)[1] - include = None - - for inc_re, exc_re in FILTERS: - if include is None and inc_re: - if re.match(inc_re, fname): - include = True - - if include is None and exc_re: - if re.match(exc_re, fname): - include = False - - if include is None: - include = True - - if include: - print("including:", fname) - else: - print("excluding:", fname) - continue - - farch = fin.extractfile(info) - fout.addfile(info, farch) - fout.close() - fin.close() - - -def make_resource_module(manifest_files): - resources = [] - # Any non-python file included in manifest is resource - for fname in manifest_files: - ext = fname.rsplit(".", 1) - if len(ext) > 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"], -)