Skip to content

Get rid of avr-libc dependency #76

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

Open
Rahix opened this issue Feb 14, 2021 · 9 comments
Open

Get rid of avr-libc dependency #76

Rahix opened this issue Feb 14, 2021 · 9 comments
Assignees
Labels
runtime Related to startup/interrupts/runtime

Comments

@Rahix
Copy link
Owner

Rahix commented Feb 14, 2021

Right now we use the CRT and other bits from avr-libc. Let's rewrite it in Rust, also to support devices which avr-libc currently does not support.

@Rahix Rahix added the runtime Related to startup/interrupts/runtime label Feb 14, 2021
@Rahix Rahix self-assigned this Feb 14, 2021
@Rahix
Copy link
Owner Author

Rahix commented Feb 14, 2021

This is an existing project archieving no libc dependence: https://github.com/shepmaster/rust-arduino-blink-led-no-core-with-cargo

@MarkuBu
Copy link

MarkuBu commented Jun 15, 2021

Any progress on this?

@Rahix
Copy link
Owner Author

Rahix commented Jun 16, 2021

No, unfortunately not. To be quite honest, I'm waiting for nightly rustc to be fixed for AVR before I intend to spend time on this, because it is getting increasingly harder to stick with the old revision...

@LuigiPiucco
Copy link
Contributor

@Rahix I think with the compiler update we're getting to a position in which this is feasible. In particular, the change that adds avr-none and requires -C target-cpu is really helpful, because now we can compile without having to copy-paste the JSON specs everywhere. @tones111, you mention wanting to pick this up in a comment, so I'll ping you too. I've done some in-house experiments on this in the past, and here's a run down of what I've learned:

  1. The first thing we will need is some design around a linkage approach. Currently, the use of avr-gcc instead of avr-ld (or lld, though from my testing it can't yet link any useful programs for AVR, and even compiling it with the experimental support gave me trouble) hardcodes what we can generate and what will be included in the final binary. If that's removed, we'll have to provide our own conventions.
  2. Inevitably, this will require use of linker scripts. And here comes a big hurdle, which is that linker scripts are for applications (i.e. code meant to generate a binary, almost always in ELF format), but here we implement a library. Therefore, we don't get to include it ourselves, the user has to. However, I believe avr-ld resolves them from the search path (-L flag) when a relative argument is passed to the -T flag, so we could augment it from the build script to have a directory where our linker script(s) can be found.
  3. Linker scripts can specify an entry-point, i.e. the function that gets called when the executable is run. This assumes that the executable will be loaded into memory by an OS and some startup code will run before though, which doesn't really make sense for AVR. It can't load programs on-the-fly, they must be there when the chip powers on, and it doesn't allow configuring the entry-point, it's hard-coded as the reset interrupt, which I believe is always index 0 in the vector table (also a hard-coded address). All this is handled by avr-gcc, it places a jmp instruction in the vector table to the actual address of startup (that is, for all interrupts, not just reset). It expects the interrupt handlers to be called __vector_{index} at link time, where {index} is an integer. We will need to handle it ourselves.
  4. There's also the issue of collision of the handlers. As our code is a library, we don't really "know" which MCU it will be compiled for, we have to support all of them with conditional compilation via cargo features. But what if 2 MCUs get selected? This is valid when building for docs.rs, for instance. Also, we can't not support it, since cargo features are supposed to be additive. I think if this case, the solution is to write a linker script such that multiple .text sections get generated, one per selected MCU, and when uploading we decide which to send. I know it'd work with avrdude, since it allows uploading section-per-section, and we may be able to make the selection automatic with ravedude.
  5. One extra benefit of using avr-ld directly is that we get to provide (or rather, become obligated to provide) minimal startup functionally ourselves, such as data memory initialization from flash, stack setup and possibly some minimal peripheral initialization. This means we can make it better suited for Rust than if using the C conventions. Moreover, one neat thing I experimented with a while ago is defining main to receive the Peripherals struct as an argument, from the startup code. This ensures uniqueness without depending on mutable statics. I think we could extend this idea to pass-in other kinds of "ownership over state tokens", like interrupt state, allowing to safely enforce some rules over what can and can't be done if interrupts are disabled vs. enabled. This could be very useful in a scenario where an integration with embassy is achieved someday.
  6. At first, we may need to use JSON specs again, because the current implementation of the avr-none target seems to imply avr-gcc must be used (and I don't know if there are other ways to override its linker). When the implementation matures we can open a PR to rustc and add a target that uses avr-ld.

I can't elaborate this into a roadmap without actually picking it up to implement myself, and because of #157 I decided to post here first, in case someone wants to get it moving before that is merged. But I think what I wrote is enough for someone else to start, at least. Later I can help too if needed.

@tones111
Copy link
Contributor

tones111 commented Apr 4, 2025

Also check out the investigation @Patryk27 and @benshi001 have done in Rahix/avr-hal#471. It looked like the next steps were to add intrinsics to compiler-builtins but I believe that's blocked on support for AVR assembly.

For linking we should see if we can take some inspiration from the cortex-m crates.
cortex-m-rt abstracts the low-level bits and depends on a memory.x file that's usually provided in the HAL crates (example).

@Patryk27
Copy link
Contributor

Patryk27 commented Apr 4, 2025

It looked like the next steps were to add intrinsics to compiler-builtins but I believe that's blocked on support for AVR assembly.

fwiw, that's tracked over rust-lang/compiler-builtins#711 - recently I've added support for floating-point intrinsics, divrem, and a couple of others, and currently we're missing just four:

rust-lang/compiler-builtins#711 (comment)

It should be possible to implement them right now, even (using naked functions and assembly), I just didn't have time yet (but I'm planning on finding some in coming weeks).

@LuigiPiucco
Copy link
Contributor

LuigiPiucco commented Apr 5, 2025

From what I could gather, the missing intrinsics aren't actually used, because LLVM casts to 32-bit instead of 16-bit in all known cases, right? If so, that shouldn't pose a problem for this thread. But I'll try to lend a hand and implement them, if I manage to do so in time.

@Patryk27
Copy link
Contributor

Patryk27 commented Apr 5, 2025

the missing intrinsics aren't actually used, because LLVM casts to 32-bit instead of 16-bit in all known cases, right?

GCC provides dedicated intrinsics (as in "intrinsics that have arbitrary calling-convention that otherwise doesn't match anything else") for some 16-bit and 32-bit integer operations. LLVM doesn't emit calls to the 16-bit intrinsics, but the four 32-bit intrinsics (__u?divmod[hq]i4 as mentioned there) are used and that's what needs to be implemented if we want to drop avr-libc.

@LuigiPiucco
Copy link
Contributor

I see, I thought those were the ones currently implemented, now it makes sense.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
runtime Related to startup/interrupts/runtime
Projects
None yet
Development

No branches or pull requests

5 participants