rust-embedded-community / ssd1306

SSD1306 OLED driver
Apache License 2.0
315 stars 70 forks source link

memory.x or probe-run "fix" #165

Open pdgilbert opened 2 years ago

pdgilbert commented 2 years ago

Description of the problem/feature request/other

I was stripping down my Cargo.toml to report a runtime problem with another crate and discovered that removing the unused ssd1306 gives me a linker problem compiling the example for a completely different crate:

linking with `rust-lld` failed: exit status: 1
  ...
 >>> INCLUDE memory.x

At first I thought the ssd1306 build was "fixing" the link failure by copying a (STM32F103C8T6) memory.x file from thessd1306 crate root into the target directory. But deleting those does not "unfix" the linker, so I suspect something with the build scripts or probe-run. I can do the proper fix by providing the correct memory.x, but I would like to understand what is going on, and most importantly be sure I am getting the proper memory map on various MCUs.

Any ideas?

jamwaffles commented 2 years ago

I don't have much experience in this area, but @allexoll on Element explained it for me:

the STM-rs dinamically create/link the correct memory.x depending on the mcu/reference you're choosing in your toml. since the ssd1306 crate also includes one memory.x for the STM32F103C8T6, it gets included twice in the linking process by build.rs. Since ssd1306 is a driver crate and not a mcu hal crate, it shouldn't include a memory.x linker script. when @pdgilbert was using your crate with (i suppose) another mcu-hal crate with no mechanism for memory.x, it was relying on yours for it. which would also probably explain his runtime error if it was for another mcu with another memory map.

https://github.com/stm32-rs/stm32l0xx-hal/blob/5e0d1946993d19f5ba51ab9698147b510ac1b7ef/build.rs#L71 for reference on how its done for stm32l0xx-hal

but that also mean that you might need to fix your crate/build.rs so it does not include this linker script (might break your example aswell, but there are other ways to have dev-depencencies)

It sounds like we need to remove memory.x from this crate and rely on the one provided by stm32f1xx-hal if I'm not mistaken.

allexoll commented 2 years ago

yes, usually, a device driver crate does not need a memory.x linker script.

Usually, it will be used like this:

my-project:
   - my-mcu-hal (like stm32l0xx-hal)
   - ssd1306

my-project needs a memory map for the linker script. usually it can come from my-mcu-hal or you can have a custom one in my-project. stm32l0xx-hal provides a disable-linker-script for that purpose. ssd1306 does not need to provide one. If you have examples, they can have dev-depencencies for a specific example MCU, with the featureset needed to run that example.

pdgilbert commented 2 years ago

I see that a dependency on stm32l0xx-hal puts a memory.x file in target/thumbv6m-none-eabi/debug/build/stm32l0xx-hal-.../out/memory.x but near as I can tell no other device hals do that. (It seems like a good idea but is not very reliable unless they all do it.) Also @allexoll , could you please explain a bit more

If you have examples, they can have dev-depencencies for a specific example MCU, with the featureset needed to run that example.

I don't see how to use a dev-dependency to get the linker to find memory.x. (Sorry, I'm still somewhat new to everything including cargo, and I'm a bit slow.)

allexoll commented 2 years ago

basically, you need one, and only one memory.x per executable (not per crate, per executable). Usually, this comes from either the hal, or you provide it yourself.

for example

memory.x is dependent on your MCU since it defines the memory map of the device: where the flash starts and how big it is, where the ram starts, and how big it is, and so on.

adding this to a driver for a display does not make sense, since the driver is intended to be usable with different MCU, and therefore with different memory layouts.

If you want to have buildable and runnable example in this repository, you'll need to find a way to have it only included for those examples, and not for projects that include this driver, since it probably will be the wrong one for their MCU.

i think that could be either done in cargo/config (i think this is not used when used as a crate) with something like "linker-arg=Tmemory.x". in any case, having memory.x included in build.rs (which does get used when the crate is used in a parent project) will cause issues for people using it.

I hope it's more clear now

pdgilbert commented 2 years ago

Thanks @allexoll that is more clear. I see that with memory.x, build.rs and build.sh removed the ssd1306 crate can be used properly. (It works and does not put a spurious memory.x in place.) I still need to play a bit to see how to get ssd1306 to build its own examples on a specific MCU.

pdgilbert commented 2 years ago

On a forked branch no_memory.x I have removed the root memory.x and the file build.sh, added a directory memoryMaps with memory.x files for several different MCUs, and replaced the file build.rs with a version that adds a link to a memory.x file into the linker search path. The strategy for determining the link is described in the build.rs file. The summary is that the crate can be built and used without putting a memory.x file anywhere it might mistakenly be picked up, and the crate examples can be build with syntax:

#    bluepill
   cargo build --target thumbv7m-none-eabi 
   MEMMAP=stm32f103  cargo build --target thumbv7m-none-eabi  --release  --examples
   MEMMAP=stm32f103  cargo build --target thumbv7m-none-eabi  --release  --example text_i2c

The examples in ssd1306 specify use stm32f1xx_hal so above is what would be needed. For examples on other devices another MEMMAP and target can be set. I have been testing this with my own examples at https://github.com/pdgilbert/rust-integration-testing/actions.

Let me know if you want me to submit a PR.