Skip to content

Commit 8362fb6

Browse files
committed
add documentation for the preprocessor
This defines what the preprocessor aims to do, why and what it's intentional limitations are. Examples on how to use it and how to use the "Defines DB" are also provided
1 parent 6e56e6c commit 8362fb6

File tree

2 files changed

+144
-0
lines changed

2 files changed

+144
-0
lines changed

README.rst

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ Constants defined with ``.set`` are supported in expressions.
2424
We have some unit tests and also compatibility tests that compare the output
2525
whether it is identical with binutils-esp32ulp output.
2626

27+
There is a simple preprocessor that understands just enough to allow assembling
28+
ULP source files containing convenience macros such as WRITE_RTC_REG. The
29+
preprocessor and how to use it is documented here:
30+
`Preprocessor support <docs/preprocess.rst>`_.
31+
2732
There might be some stuff missing, some bugs and other symptoms of alpha
2833
software. Also, error and exception handling is rather rough yet.
2934

docs/preprocess.rst

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
Preprocessor
2+
---------------------
3+
4+
py-esp32-ulp contains a small preprocessor, which aims to fulfill one goal:
5+
facilitate assembling of ULP code from Espressif and other open-source
6+
projects to loadable/executable machine code without modification.
7+
8+
Such code uses convenience macros (``READ_RTC_*`` and ``WRITE_RTC_*``)
9+
provided by the ESP-IDF framework, along with constants defined in the
10+
framework's include files (such as ``RTC_GPIO_IN_REG``), to make reading
11+
and writing from/to peripheral registers much easier.
12+
13+
In order to do this the preprocessor has two capabilities:
14+
15+
1. Parse and replace identifiers defined with ``#define``
16+
2. Recognise the ``WRITE_RTC_*`` and ``READ_RTC_*`` macros and expand
17+
them in a way that mirrors what the real ESP-IDF macros do.
18+
19+
20+
Usage
21+
------------------------
22+
23+
Normally the assembler is called as follows
24+
25+
.. code-block:: python
26+
27+
src = "..full assembler file contents"
28+
assembler = Assembler()
29+
assembler.assemble(src)
30+
...
31+
32+
With the preprocessor, simply pass the source code via the preprocessor first:
33+
34+
.. code-block:: python
35+
36+
from preprocess import Preprocessor
37+
38+
src = "..full assembler file contents"
39+
preprocessor = Preprocessor()
40+
src = preprocessor.preprocess(src)
41+
assembler = Assembler()
42+
assembler.assemble(src)
43+
...
44+
45+
46+
Using a "Defines Database"
47+
--------------------------
48+
49+
Because the py-esp32-ulp assembler was built for running on the ESP32
50+
microcontroller with limited RAM, the preprocessor aims to work there too.
51+
52+
To handle large number of defined constants (such as the ``RTC_*`` constants from
53+
the ESP-IDF) the preprocessor can use a database (based on BerkleyDB) stored on the
54+
device's filesystem for looking up defines.
55+
56+
The database needs to be populated before preprocessing. (Usually, when only using
57+
constants from the ESP-IDF, this is a one-time step, because the include files
58+
don't change.) The database can be reused for all subsequent preprocessor runs.
59+
60+
(The database can also be generated on a PC and then deployed to the ESP32, to
61+
save processing effort on the device. In that case the include files themselves
62+
are not needed on the device either.)
63+
64+
1. Build the defines database
65+
66+
The ``esp32_ulp.parse_to_db`` tool can be used to generate the defines
67+
database from include files. The resulting file will be called
68+
``defines.db``.
69+
70+
(The following assume running on a PC. To do this on device, refer to the
71+
`esp32_ulp/parse_to_db.py <../esp32_ulp/parse_to_db.py>`_ file.)
72+
73+
.. code-block:: bash
74+
75+
# general command
76+
micropython -m esp32_ulp.parse_to_db path/to/include.h
77+
78+
# loading specific ESP-IDF include files
79+
micropython -m esp32_ulp.parse_to_db esp-idf/components/soc/esp32/include/soc/soc_ulp.h
80+
81+
# loading multiple files at once
82+
micropython -m esp32_ulp.parse_to_db esp-idf/components/soc/esp32/include/soc/*.h
83+
84+
# if file system space is not a concern, the following can be convenient
85+
# by including all relevant include files from the ESP-IDF framework
86+
micropython -m esp32_ulp.parse_to_db \
87+
esp-idf/components/soc/esp32/include/soc/*.h \
88+
esp-idf/components/esp_common/include/*.h
89+
90+
2. Tell the Preprocessor to use the database
91+
92+
Once the defines database has been created (i.e. you have a populated
93+
``defines.db`` file), you can instruct the preprocessor to use this
94+
database.
95+
96+
.. code-block:: python
97+
98+
from preprocess import Preprocessor
99+
from definesdb import DefinesDB
100+
101+
preprocessor = Preprocessor()
102+
db = DefinesDB()
103+
preprocessor.use_db(db)
104+
src = preprocessor.preprocess(src)
105+
...
106+
107+
Note: You can also use the above approach by default, because the preprocessor
108+
will treat an absent database like an empty database (and take care not to
109+
create an empty database file cluttering up the filesystem).
110+
111+
Design choices
112+
--------------
113+
114+
The preprocessor does not support:
115+
116+
1. Function style macros such as :code:`#define f(a,b) (a+b)`
117+
118+
This is not important, because there are only few RTC macros that need
119+
to be supported and they are simply implemented as Python functions.
120+
121+
Since the preprocessor will understand ``#define`` directives directly in the
122+
assembler source file, include mechanisms are not needed in some cases
123+
(simply copying the needed ``#define`` statements from include files into the
124+
assembler source will work).
125+
126+
2. ``#include`` directives
127+
128+
The preprocessor does not currently follow ``#include`` directives. To
129+
limit space requirements (both in memory and on disk), the preprocessor
130+
relies on a database of defines (key/value pairs). This database should be
131+
populated before using the preprocessor, by using the ``esp32_ulp.parse_to_db``
132+
tool (see section below), which parses include files for identifiers
133+
defined therein.
134+
135+
3. Preserving comments
136+
137+
The assumption is that the output will almost always go into the
138+
assembler directly, so preserving comments is not very useful and
139+
would a lot of complexity.

0 commit comments

Comments
 (0)