-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Preprocessor for ULP/RTC macros #43
Conversation
Actually... it fixes issue #16 too (commit with subject: "add support for the .global directive. only symbols flagged as global will be exported") |
Wow, this is big. :-) Guess I'll need a bit for reviewing this. Could you please work on #44 while I do that? Would be good to have master branch testing successfully "as is" and then rebase this PR onto new master. |
Will do. Started looking into making the Travis build pass - the failure is because of the old gcc on Ubuntu Trusty. But if you want to move to Github Actions, I will look into that! |
just merged another PR of yours into master, so refresh master and rebase again. |
So. It's rebased now. And all tests pass :) . |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
started reviewing:
add units test for the .set directive 5a9b7e9
add support for left aligned assembler directives (e.g. .set) … 8d7981d
fix a crash bug where BSS size calculation was attemped on the value … … 45b3769
raise error when attempting to store values in .bss section … f65cbc2
fix reference to non-exiting variable f0d86db
fixed typo in comment of instruction definition 15633eb
add support for the .global directive. only symbols flagged as global… … 3a2d312
let SymbolTable.export() optionally export non-global symbols too … 2417778
i only found a few nitpicks. overall, great stuff so far. i like how you do clean commits!
For now, I have pushed the fixed as additional commits (because I dont know what force pushing will do to this PR's history). But I want to rebase/squash them later, so they "original" commits are clean, without those commits. |
Much open-source code out there has .global, .set, etc directives starting in the first column of a line. This change allows assembling such code. Incidentally this also fixes a bug, where directives without parameters, such as .text, .data, etc were silently accepted when left-aligned but in those cases treated as labels instead of section headers.
f78130e
to
5f76237
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed:
support ULP opcodes in upper case … 33b4d3d
add a compatibilty test for the recent fixes and improvements … 9c94247
add support for evaluating expressions … 4962139
add a compatibilty test for evaluating expressions … be27cdc
docs: add that expressions are suppported 521e924
guess that eval() is dangerous.
esp32_ulp/opcodes.py
Outdated
try: | ||
entry = symbols.get_sym(arg) | ||
return ARG(SYM, entry, arg) | ||
except KeyError: | ||
pass | ||
return ARG(IMM, int(eval_arg(arg)), arg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
try:
entry = symbols.get_sym(arg)
except KeyError:
return ARG(IMM, int(eval_arg(arg)), arg)
else:
return ARG(SYM, entry, arg)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seen my previous comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
806bcf2
to
1606b5d
Compare
For now I am done fixing what I could. Happy to get more feedback. |
BTW, i first thought "OMG, this DefinesDB is a bit too much". But considering the memory issues lurking and that the db has to be built only once, guess this is the only non-cumbersome method we can use. The only (cumbersome) alternative would be to extract "used symbols only" from the large header files. |
0e932f8
to
4f70602
Compare
This defines what the preprocessor aims to do, why and what its intentional limitations are. Examples on how to use it and how to use the "Defines DB" are also provided
…utput in all cases This fix makes compat tests pass for: https://github.com/duff2013/ulptool/blob/master/src/ulp_examples/ulp_rtc_gpio/rtcio.s
This change allows specifying the address in 32-bit words (i.e. the address as seen from the ULP), in addition to the existing mode of specifying a register's full address on the DPORT bus. If an address is between 0 and DR_REG_MAX_DIRECT (0x3ff), treat it as a word offset (ULP address), otherwise treat it as a full address on the DPORT bus as before.
.long and .int are the same as per GNU assembler manual: https://sourceware.org/binutils/docs/as/Long.html binutils-esp32ulp also treats them the same (compat test included to verify this)
Ok, I have cleaned up everything, and rebased/squashed it all into as clean commits as I could. I updated the preprocessor docs too, since there's now the convenience function for preprocessing which defaults to using the DefinesDB. In one of the unit test files, I built an I also built a context manager for opening and closing the DefinesDB when preprocessing or parsing include files into the defines db. That definitely makes the code look cleaner. Thank you for that suggestion. You can now take a 2nd pass over the commits - they should be pretty close to what they were like before, but with the improvements embedded into them. |
BTW, please add yourself either to the LICENSE file or add a new file AUTHORS and reference that from LICENSE (see borgbackup/borg for an example). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, did a 2nd pass and found some minor stuff.
please only fix these without extending scope, do additional commits for now.
esp32_ulp/opcodes.py
Outdated
try: | ||
entry = symbols.get_sym(arg) | ||
return ARG(SYM, entry, arg) | ||
except KeyError: | ||
pass | ||
return ARG(IMM, int(eval_arg(arg)), arg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seen my previous comment?
tests/compat/expr.S
Outdated
#test that expressions evaluate correctly for all supported operators | ||
move r3, 1+2 | ||
move r3, 3-5 | ||
move r3, -5 | ||
move r3, 2*3 | ||
move r3, 4/2 | ||
move r3, 4 % 3 | ||
move r3, 0xff << 2 | ||
move r3, 0xff >> 1 | ||
move r3, (0xabcdef | 0xff) & 0xff | ||
move r3, 0x1234 & ~2 | ||
move r3, 42|4&0xf # 46 (4&0xf is evaluated first) | ||
move r3, (42|4)&0xf # 14 (42|4 is evaluated first) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a bit strange mix of a real-world code example and some lines of assembler testing.
maybe separate these lines at least?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you mean into a separate file, or maybe I can move them below the exit: halt
line and add a comment that they are all "artificial" examples?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whatever you like better.
class ctx: | ||
def __init__(self, db): | ||
self._db = db | ||
|
||
def __enter__(self): | ||
# not opening DefinesDB - it opens itself when needed | ||
return self._db | ||
|
||
def __exit__(self, type, value, traceback): | ||
if isinstance(self._db, DefinesDB): | ||
self._db.close() | ||
|
||
if self._defines_db: | ||
return ctx(self._defines_db) | ||
|
||
return ctx(self._defines) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did you try having the contextmanager inside DefinesDB class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if it works, also call the context manager from the tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have tried to put the context manager into the DefinesDB class, but because it handles both the "with DefinesDB" and the "without a DefinesDB" case, this doesn't fit into the DefinesDB class.
I could find an elegant way to add a condition to the "with" statement - ie. only use the context manager from DefinesDB when we're actually using the DefinesDB, otherwise work with the class-internal dict.
I'll mull over this a bit more and see if I can find a way.
Regarding testing: in effect this was tested - because the existing tests exercise the code which uses the context manager, but I added another test now to check specifically that the db is actually closed too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made all the fixes now (except the Context Manger move to the DefinesDB class, which I need to think over more). I'll push this as a commit on top now, and then check the Context Manager topic.
class ctx: | ||
def __init__(self, db): | ||
self._db = db | ||
|
||
def __enter__(self): | ||
# not opening DefinesDB - it opens itself when needed | ||
return self._db | ||
|
||
def __exit__(self, type, value, traceback): | ||
if isinstance(self._db, DefinesDB): | ||
self._db.close() | ||
|
||
if self._defines_db: | ||
return ctx(self._defines_db) | ||
|
||
return ctx(self._defines) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have tried to put the context manager into the DefinesDB class, but because it handles both the "with DefinesDB" and the "without a DefinesDB" case, this doesn't fit into the DefinesDB class.
I could find an elegant way to add a condition to the "with" statement - ie. only use the context manager from DefinesDB when we're actually using the DefinesDB, otherwise work with the class-internal dict.
I'll mull over this a bit more and see if I can find a way.
Regarding testing: in effect this was tested - because the existing tests exercise the code which uses the context manager, but I added another test now to check specifically that the db is actually closed too.
Pushed latest improvements as a commit on top: 5fd45a2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM - merge and do the remaining stuff in later PRs?
Ok. I force pushed one small thing - moving the "made up" statement in tests/compat/expr.S to the bottom of the file with an improved comment. And I updated the LICENCE file and added an AUTHORS file. Perhaps check if you are happy with that, and then we're good for a merge. (For now I don't manage to find a solution to the Context Manager challenge, but that can be improved later) |
LICENSE
Outdated
Copyright (c) 2018 Thomas Waldmann | ||
Copyright (c) 2018 Thomas Waldmann (see AUTHORS file) | ||
Copyright (c) 2021 Wilko Nienhaus (see AUTHORS file) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it is simpler to update authors only at one place, so I suggest this:
Copyright 2018-2021 by the py-esp32-ulp authors, see AUTHORS file.
Also, I would not go into details about who has written what, that's where git log is better anyway and a file describing this will likely be always outdated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. 47d5e8a
Yay! \o/ |
Yay! And thank you for all your effort with this big PR! |
This PR implements issue #19 and in a way also issue #12, because now many examples from the net can be assembled unmodified.
This is a big PR. I therefore split it up into many small commits, each standing on its own and intended to be short and simple to review. It will be best to review this PR one commit at a time. Each commit describes in more detail what the change is.
Each commit has at least unit tests (that also pass) covering the change. All changes also have compat tests (even though they sometimes have their own commit), to ensure the resulting binary always matches what binutils-esp32ulp would have produced. There are also a new set of compat tests, that download sources from the net (including the examples from the tests of binutils-esp32ulp) and run them through both py-esp32-ulp and binutils-esp32ulp to ensure the output is the same).
In summary, the PR has 4 changes:
I originally set myself the goal to be able to compile all examples from this repo (https://github.com/duff2013/ulptool/tree/master/src/ulp_examples) without modification to the sources. I will say I managed (including it working on the ESP32 itself), except for the cases that use assembler macros (see issue #40), which I intentionally did not implement. (I might try that as a future side project).
The back story to this all is that I used to do a lot of manual steps with my ULP assembly source code. I passed it through the gcc preprocessor to get the macros I used expanded, but those macros spit out long expressions with countless brackets. Manually evaluating those (especially when using my own constants in the expressions) was tedious. I also kept the original code (before preprocessing) in comments above the resulting statements, so I could more easily understand where the final reg_rd or reg_wr statements came from. While that helped, keeping the statements and comments in sync was also messy.
Ultimately, I wanted a better way. I never used binutils-esp32ulp to compile my ULP sources, because when I started with ULP coding, I didn't want to download and build this massive tool on my machine. I found py-esp32-ulp and it was just so simple and fast and did what I needed. So while using biunutils-esp32ulp is an alternative (especially, when creating a docker image of it), I wanted to continue with the lightweight and simple approach of py-esp32-ulp, directly in MicroPython. (Of course this also seemed like a fun side project, which is definitely was! In particular, getting it to work on the actual device with little memory!)