-
Notifications
You must be signed in to change notification settings - Fork 991
esp: add support for the Espressif ESP32 chip #1289
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
Conversation
70f91be
to
95932ea
Compare
3089d5b
to
df56910
Compare
This is now ready for review. |
.circleci/config.yml
Outdated
variant: | ||
type: string | ||
steps: | ||
# Cache the file because the Espressif download website is not particularly fast. |
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.
Another option is to download the same file from GitHub Releases, which should be pretty fast: https://github.com/espressif/crosstool-NG/releases/tag/esp-2020r2
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.
Good idea, thanks!
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've updated the PR with this. That makes the CI file smaller and simpler (no caches needed).
* SRAM1 has other addresses as well but the datasheet seems to indicate | ||
* these are aliases. | ||
*/ | ||
DRAM (rw) : ORIGIN = 0x3FFAE000, LENGTH = 200K + 128K /* Internal SRAM 1 + 2 */ |
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.
One thing to watch out for is that the startup stack is somewhere in that range. Might be useful to add an assert that the .data/.rodata sections don't overlap the stack used by the bootloader.
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.
That's what gave me trouble initially. I solved it by setting the stack pointer at startup, see src/device/esp/esp32.S. That also makes the system more flexible, as it is no longer bound by the stack that was set by the boot ROM.
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.
My point is that the ROM bootloader is using certain region as stack, and if the app's .data section happens to overlap the ROM bootloader stack, the bootloader will overwrite its own stack, while trying to load app's .data.
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.
Right. I see. Thank you for explaining. In that case, I think there are two options:
- Use the stack that is used by the ROM bootloader.
- Let the application load
.data
instead of letting the ROM bootloader do that.
Is the bootloader stack documented somewhere?
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.
PRO CPU stack is at 0x3ffe1320 — 0x3ffe3f20, and APP CPU stack is at 0x3ffe5230 — 0x3ffe7e30
(I think these values are defined somewhere in IDF, but I can't find the link right now. They can be extracted from the ROM code ELF file, provided at https://dl.espressif.com/dl/esp32_rom.elf.)
In addition to that there are .bss and .data sections used by various ROM functions, at
0x3ffe0000 — 0x3ffe0440, 0x3ffe3f20 — 0x3ffe4350, 0x3ffae000 — 0x3ffae6e0. If you do not plan to call ROM functions (other than stateless functions like memcpy and memset), and do not plan to call any libraries which in turn call ROM functions, you can ignore these other reserved regions.
On the ESP32-S2, the situation is a bit more sane, as the reserved regions are near the end of DRAM, and don't make "holes" in the continuous region you would otherwise use for the application.
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.
Thanks, that is some great insight. Also thanks for the ROM file which will be useful. I will take a look and improve this PR accordingly.
It may be possible to use the given area for the heap anyway and block out the parts that are used by the ROM code. That shouldn't need any changes to the TinyGo heap. Although I'll probably ignore that for this PR and do that only when we actually want to call these special ROM functions.
On the ESP32-S2, the situation is a bit more sane, as the reserved regions are near the end of DRAM, and don't make "holes" in the continuous region you would otherwise use for the application.
Glad to hear! Unfortunately the ESP32 will be around for a while and we'll need to adjust to it somehow.
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.
It may be possible to use the given area for the heap anyway and block out the parts that are used by the ROM code.
Right, that's what we do in IDF — these ROM stack regions are getting added to the heap allocator once the startup is complete. And the regions used for ROM .data/.bss are marked as reserved.
Unfortunately the ESP32 will be around for a while and we'll need to adjust to it somehow.
Of course, it was more of me saying that we realize that reserved regions in the middle of DRAM are tricky to work with, and in the subsequent chips the situation is somewhat improved.
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.
@igrr thanks a lot for checking this PR!
* SRAM1 has other addresses as well but the datasheet seems to indicate | ||
* these are aliases. | ||
*/ | ||
DRAM (rw) : ORIGIN = 0x3FFAE000, LENGTH = 200K + 128K /* Internal SRAM 1 + 2 */ |
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.
That's what gave me trouble initially. I solved it by setting the stack pointer at startup, see src/device/esp/esp32.S. That also makes the system more flexible, as it is no longer bound by the stack that was set by the boot ROM.
.circleci/config.yml
Outdated
variant: | ||
type: string | ||
steps: | ||
# Cache the file because the Espressif download website is not particularly fast. |
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.
Good idea, thanks!
ffab466
to
b4595f4
Compare
src/device/esp/esp32.S
Outdated
.long _stack_top | ||
.global call_start_cpu0 | ||
call_start_cpu0: | ||
l32r a1, 1b |
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.
In Xtensa window ABI changing a1 like it is done here is not sufficient, i think. If any of the previous register windows contain registers not spilled onto the stack, these registers will get saved to their original frame locations, once the window overflow happens. This might not be a practical issue, unless the boot ROM stack overlaps with app's .bss and the window overflow happens after .bss is zeroed out. If that happens, some part of .bss will contain spilled registers.
To avoid this issue, disable WOE in PS register, set WINDOWBASE to 1 << WINDOWSTART, and re-enable WOE in PS. Here is the code snippet which does this: https://github.com/espressif/esp-idf/blob/c77c4ccf6c43ab09fd89e7c907bf5cf2a3499e3b/components/xtensa/include/xt_instr_macros.h#L26
@igrr pointed out some flaws in the PR so I will mark this a draft again until I've fixed the issues. |
I think I've fixed all the issues pointed out by @igrr now. I'm still using a custom stack, but hopefully did the stack switch correctly this time. I've also added an assert to make sure the |
I think the only hesitation I still have is depending on the Xtensa fork of LLVM for everything, instead of using the official LLVM repo. Perhaps we should fork the Xtensa fork ourselves, and then use that dependency, just in case something else changes again. Not sure on that, perhaps I am just being over-careful? I will find some of my ESP32 boards for some device level testing. |
Yes, good point. I have forked llvm-project into tinygo-org and pushed the branch and updated this PR accordingly, so that we have our own copy of LLVM (just to be on the safe side). |
This is only very minimal support. More support (such as tinygo flash, or peripheral access) should be added in later commits, to keep this one focused. Importantly, this commit changes the LLVM repo from llvm/llvm-project to tinygo-org/llvm-project. This provides a little bit of versioning in case something changes in the Espressif fork. If we want to upgrade to LLVM 11 it's easy to switch back to llvm/llvm-project until Espressif has updated their fork.
Took a long time to build the LLVM. Also worth mentioning somewhere is you need to install the The program built the first time, and flashed/worked exactly as expected on my ESP32 board! Seems like at this point we can merge this. We still have a lot of work to do to make it useful, but this is a massive step forward, and a long time in the making. |
Ah yes, forgot to mention that. Maybe I'll make a PR to Espressif one day to implement it in |
OK, merging. Incredible work @aykevl this is very exciting moment. |
Awesome! 🎉 |
Great job @aykevl ! |
This PR adds very minimal ESP32 support. You can use it as follows:
tinygo build -o test.elf -target=esp32 -size=short examples/serial esptool.py --chip=esp32 elf2image test.elf esptool.py --chip=esp32 --port /dev/ttyUSB0 write_flash 0x1000 test.bin -ff 80m -fm dout picocom --baud 115200 /dev/ttyUSB0 # optional
Support for other important things (GPIO control,
tinygo flash
, etc) can be added in later PRs. I've left them out to keep this as small and reviewable as possible, especially considering the LLVM branch switch.