diff --git a/README.rst b/README.rst index 49e722e..a869e01 100644 --- a/README.rst +++ b/README.rst @@ -1,63 +1,76 @@ -What is py-esp32-ulp? ---------------------- +.. start-badges -It is an assembler toolchain for the ESP32 ULP (Ultra Low-Power) Co-Processor, -written in MicroPython. +.. image:: ../../actions/workflows/run_tests.yaml/badge.svg + :height: 20px + :target: ../../actions/workflows/run_tests.yaml + :alt: Build Status -It is able to translate small, simple assembler language programs to a -loadable/executable machine code binary, at runtime, on the ESP32 -microcontroller, from projects implemented in MicroPython. +.. end-badges -This is intended as an alternative approach to assembling such programs on a -development machine using the binutils-esp32ulp toolchain from Espressif. +===================== +py-esp32-ulp +===================== +py-esp32-ulp is an assembler toolchain for the ESP32 ULP (Ultra Low-Power) +Co-Processor, written in MicroPython. -Status ------- +It can translate small assembly language programs to a loadable/executable +ULP machine code binary, directly on the ESP32 microcontroller. -The most commonly used stuff should work. Many ULP code examples found on -the web will work unmodified. Notably, assembler macros and #include processing -are not supported. +This is intended as an alternative approach to assembling such programs using +the binutils-esp32ulp toolchain from Espressif on a development machine. -Expressions in assembly source code are supported and get evaluated during -assembling. Only expressions evaluating to a single integer are supported. -Constants defined with ``.set`` are supported in expressions. +It can also be useful in cases where binutils-esp32ulp is not available. -We have some unit tests and also compatibility tests that compare the output -whether it is identical with binutils-esp32ulp output. -There is a simple preprocessor that understands just enough to allow assembling -ULP source files containing convenience macros such as WRITE_RTC_REG. The -preprocessor and how to use it is documented here: -`Preprocessor support `_. +Features +-------- -The minimum supported version of MicroPython is v1.12. py-esp32-ulp has been -tested with MicroPython v1.12 and v1.17. It has been tested on real ESP32 -devices with the chip type ESP32D0WDQ6 (revision 1) without SPIRAM. It has -also been tested on the Unix port. +The following features are supported: -There might be some stuff missing, some bugs and other symptoms of beta -software. Also, error and exception handling is rather rough yet. +* the entire `ESP32 ULP instruction set `_ +* 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 -Please be patient or contribute missing parts or fixes. -See the issue tracker for known bugs and todo items. +Quick start +----------- +To get going run the following directly on the ESP32: -Links ------ +.. code-block:: python -We are NOT (fully) compatible with "as", but we try to be close for the stuff -that is actually implemented: + # Step 1: Install py-esp32-ulp + # IMPORTANT: Ensure the ESP32 is connected to a network with internet connectivity. + import upip + upip.install('micropython-py-esp32-ulp') -https://sourceware.org/binutils/docs/as/index.html + # Step 2: Run an example + # First, upload examples/counter.py to the ESP32. + import counter -Espressif docs: +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. -https://esp-idf.readthedocs.io/en/latest/api-guides/ulp_instruction_set.html -https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf +Documentation +------------- +See `docs/index.rst `_. -Espressif ULP examples: -https://github.com/espressif/esp-iot-solution/tree/master/examples/ulp_examples +Requirements +------------ + +The minimum supported version of MicroPython is v1.12. + +An ESP32 is required to run the ULP machine code binary produced by py-esp32-ulp +(the ESP32-S2 will not work as it is not binary compatible with the ESP32). + + +License +------- + +This project is released under the `MIT License `_. diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..ba06dff --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,173 @@ +py-esp32-ulp Documentation +========================== + +.. contents:: Table of Contents + + +Overview +-------- + +`README.rst `_ gives a general overview of this project. + + +Installation +------------ + +On the ESP32, install using upip: + +.. code-block:: python + + # ensure the ESP32 is connected to a network with internet connectivity + import upip + upip.install('micropython-py-esp32-ulp') + +On a PC, simply ``git clone`` this repo. + + +Getting started +--------------- + +On the ESP32 +++++++++++++ + +The simplest example to try on the ESP32 is `counter.py `_. +It shows how to assemble code, load and run the resulting binary and exchange +data between the ULP and the main CPU. + +Run the ``counter.py`` example: + +1. Install py-esp32-ulp onto the ESP32 as shown above +2. Upload the `examples/counter.py `_ file to the ESP32 +3. Run with ``import counter`` + +You can also try the `blink.py `_ example, which shows how to +let the ULP blink an LED. + +Look inside each example for a more detailed description. + + +On a PC ++++++++ + +On a PC with the unix port of MicroPython, you can assemble source code as +follows: + +.. code-block:: shell + + git clone https://github.com/ThomasWaldmann/py-esp32-ulp.git + cd py-esp32-ulp + micropython -m esp32_ulp path/to/code.S # this results in path/to/code.ulp + + +More examples ++++++++++++++ + +Other ULP examples from around the web: + +* https://github.com/espressif/esp-iot-solution/tree/master/examples/ulp_examples +* https://github.com/duff2013/ulptool +* https://github.com/joba-1/Blink-ULP/blob/master/main/ulp/ + + +Advanced usage +-------------- + +In some applications you might want to separate the assembly stage from the +loading/running stage, to avoid having to assemble the code on every startup. +This can be useful in battery-powered applications where every second of sleep +time matters. + +Splitting the assembly and load stage can be combined with other techniques, +for example to implement a caching mechansim for the ULP binary that +automatically updates the binary every time the assembly source code changes. + +The ``esp32_ulp.assemble_file`` function can be used to assemble and link an +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: + + .. code-block:: python + + import esp32_ulp + esp32_ulp.assemble_file('code.S') # 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 + the entry point to your code. + +3. Now load and run the resulting binary as follows: + + .. code-block:: python + + from esp32 import ULP + + ulp = ULP() + with open('test.ulp', 'r') as f: + # load the binary into RTC memory + ulp.load_binary(0, f.read()) + + # configure how often the ULP should wake up + ulp.set_wakeup_period(0, 500000) # 500k usec == 0.5 sec + + # start the ULP + # assemble_file printed offsets in number of 32-bit words. + # ulp.run() expects an offset in number of bytes. + # Thus, multiply the offset to our entry point by 4. + # e.g. for an offset of 2: + # 2 words * 4 = 8 bytes + ulp.run(2*4) # specify the offset of the entry point label + +To update the binary every time the source code changes, you would need a +mechanism to detect that the source code changed. This could trigger a re-run +of the ``assemble_file`` function to update the binary. Manually re-running +this function as needed would also work. + + +Preprocessor +------------ + +There is a simple preprocessor that understands just enough to allow assembling +ULP source files containing convenience macros such as WRITE_RTC_REG. This is +especially useful for assembling ULP examples from Espressif or other ULP code +found as part of Arduino/ESP-IDF projects. + +The preprocessor and how to use it is documented here: `Preprocessor support `_. + + +Limitations +----------- + +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. + +py-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. + +Consult the Github Actions `workflow definition file `_ +for how to run the different tests. + + +Links +----- + +Espressif documentation: + +* `ESP32 ULP coprocessor instruction set `_ +* `ESP32 Technical Reference Manual `_ + +GNU Assembler "as" documentation (we try to be compatible for all features that are implemented) + +* `GNU Assembler manual `_ diff --git a/docs/preprocess.rst b/docs/preprocess.rst index 0716e69..a3bba20 100644 --- a/docs/preprocess.rst +++ b/docs/preprocess.rst @@ -1,5 +1,6 @@ +===================== Preprocessor ---------------------- +===================== py-esp32-ulp contains a small preprocessor, which aims to fulfill one goal: facilitate assembling of ULP code from Espressif and other open-source diff --git a/examples/README b/examples/README deleted file mode 100644 index b758d49..0000000 --- a/examples/README +++ /dev/null @@ -1,3 +0,0 @@ -To run the micropython examples which load and run binaries on the ULP, -you need a ESP32 MicroPython build that was made after 2018-05-01. - diff --git a/setup.py b/setup.py index 00093a7..e16ceda 100644 --- a/setup.py +++ b/setup.py @@ -1,18 +1,28 @@ -from pathlib import Path -from setuptools import setup +import re import sdist_upip +from setuptools import setup +VERSION = "1.0.0" -HERE = Path(__file__).parent -README = (HERE / 'README.rst').read_text() -VERSION = "1.0.0" +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-py-esp32-ulp", version=VERSION, description="Assembler toolchain for the ESP32 ULP co-processor, written in MicroPython", - long_description=README, + long_description=long_desc_from_readme(), long_description_content_type='text/x-rst', url="https://github.com/ThomasWaldmann/py-esp32-ulp", license="MIT",