-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Add compiler support for AVR architecture (Arduino) #14393
Add compiler support for AVR architecture (Arduino) #14393
Conversation
I broke the compiler with the
That may be what broke CI :p |
We should try to pull out changes that are not directly affecting AVR into separate PRs. That's mostly the refactor around |
eaeba2f
to
9475fa3
Compare
The interpreter specs broke on CI with a linker error:
I'm not sure what's happening? |
You need: require "spec"
{% if flag?(:interpreted) && !flag?(:win32) %}
# TODO: figure out how to link against libstdc++ in interpreted code (#14398)
pending LLVM::ABI::AVR
{% skip_file %}
{% end %}
require "llvm"
# ... |
Thanks @HertzDevil ! |
23427b7
to
d2cfa7b
Compare
I extracted the size_t patches into its own pull request, and rebased this branch off that new pull request, to clean it up and keep it focused on the AVR target. Support for |
Does |
@HertzDevil I guess it depends on what we want |
It's implemented via Anyway, the meaning of |
I'm running some tests to pass structs (seems to be OK) as well as pointers to structs, and I feel like passing pointers isn't working or it's accessing There might be something wrong in the ABI. |
I don't see an issue in the LLVM IR codegen related to pointers. However the call addrspace(1) void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(16) %.fca.3.insert.fca.2.gep, i8 0, i64 16, i1 false) The |
Weird, the c_memset_fun in codegen is using the proper |
The non optimized LLVM IR (-O0) properly declares and uses The generated assembly is close to the one generated by clang (again: much less optimized than avr gcc). I guess it was all a non issue. |
I changed I'll extract the change to its own PR because I'm wondering if we shouldn't delegate to the ABI to get the proper alignment. For example its always 1 for AVR while we still get pointless N bytes alignments for chars, ints and floats... which may waste some space (the ATmega328 only has 2KB of SRAM). |
I think you could try querying the "preferrred alignment" instead of the "ABI alignment" since it seems Clang sets it up that way. On the other hand that would increase |
@HertzDevil I'll look into the "preferred alignment", and yes, the data layout for AVR is to align everything to 8-bit / 1-byte, same as AVR GCC. The i386 change means that the change indeed warrants its own PR. Apparently clang overrides the data layout to have 4 byte alignment for |
I reverted the alignment as it was only used for atomic load & store instructions that don't exist on AVR. Let's tackle that later in its own PR. |
I tested this myself, and wrote a little code on an attiny202, which has only 2kb of flash and 128 bytes of ram! Here's a video of blinking lights. |
Add support for the `avr-unknown-unknown` target to the compiler, which enables the LLVM AVR target when available (should be compiled in by default). Implement the ABI, following the AVR GCC call convention, hence extern C libraries must be compiled with AVR GCC (clang is buggy when passing of returning some structs). The ABI is experimental, and only basic calls have been verified against the same calls made by AVR GCC so far. Both regular and "reduced tiny" cores (with less registers) are supported.
This allows to know actual support for some features, for example which pins are available.
Don't hardcode the alignment to 4 or 8 when the actual value depends on the actual target (e.g. it's always 1 for AVR targets).
This reverts commit f1b5feb.
- Automatically link the executable program; - Set file extension to ".elf" instead of none; - Require CPU model (--mcpu) that impacts the ABI, codegen & linker
Rebased from master (without any squash) to remove the initial commit that was an older version of #14442 that got merged, and now created a conflict. |
df638cb
to
56f453b
Compare
Co-authored-by: Sijawusz Pur Rahnama <[email protected]>
This pull request has been mentioned on Crystal Forum. There might be relevant details there: https://forum.crystal-lang.org/t/real-stuff-written-in-crystal/6840/12 |
This pull request has been mentioned on Crystal Forum. There might be relevant details there: https://forum.crystal-lang.org/t/real-stuff-written-in-crystal/6840/13 |
@ysbaddaden wow! I have been hoping for this for years! 🚀 |
This pull request has been mentioned on Crystal Forum. There might be relevant details there: https://forum.crystal-lang.org/t/crystal-lang-for-microcontrollers/7165/2 |
Experimental support for the AVR (Atmel) CPU architectures as found on popular Arduino boards (e.g. Uno).
avr-unknown-unknown
target triple;Starts hacking support for 16-bitextracted as Addsize_t
Program#size_t
andTarget#size_bit_width
#14442;--mcpu
(impacts the ABI, codegen & linker);--prelude=empty
), for obvious reasons. Alternative stdlib shall happen in external shards.Initial cross compilations seem to generate a proper file, identical or close to clang and less optimized for size than AVR GCC.
Follow ups:
Add support for
-Os
(optsize) and-Oz
(minsize) withLLVMPassBuilderOptionsSetInlinerThreshold
. The thresholds in LLVM are 50 and 5 respectively (I guess clang uses these) while Rust uses 75 and 25. We could also disable some specific optimizations (e.g. loop unrolling). That would help reduce the program size which can be very restricted on AVR (e.g. 32KB). See Add compiler flags-Os
and-Oz
to optimize binary size #14463.The compiler hardcodes pointer sizes to 64-bit (
Pointer#address
,#object_id
and pointer manipulation). It would be interesting to have per-target support forsize_t
anduintptr_t
actual sizes, instead of casting and assuming LLVM will optimize.Basic instructions:
Install the GCC AVR toolchain and avr-libc. On Debian/Ubuntu:
Cross compile a crystal binary, specifying the actual CPU (e.g. Arduino Uno uses an atmega328p) and the empty prelude, link it as a
.elf
file and generate a.hex
file:When #14463 is merged we may use -Os and -Oz to further reduce the executable file size. The
--gc-sections
linker option is mostly useful when linking external libraries.Now you should be able to upload the hex file to your AVR board, using avrdude for example.