nrf-hal: EasyDMA doesn't read from Flash

In general, EasyDMA does not work with items in Flash, e.g. a &'static str. From the datasheet:

EasyDMA is an easy-to-use direct memory acc s module that some peripherals implement to gain direct access to Data RAM. The EasyDMA is an AHB bus master similar to the CPU and it is connected to the AHB multilayer interconnect for direct access to the Data RAM. The EasyDMA is not able to access the Flash.

I did this accidentally, and did not receive any visible error when sending via UARTE. I think there would be 3 ways to address this:

  1. if there is some way to detect this error condition and return an Err
  2. debug_assert that the slice ptr is in the RAM region
  3. Do an explicit check, roughly if !( RAM_LOWER <= buf.as_ptr() <= RAM_UPPER ) { return Err(...); }

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Comments: 23 (22 by maintainers)

Commits related to this issue

Most upvoted comments

I found this issue after some confusion when writeln!(uart, "Hello World") worked fine to print over the UART, but uart.write("Hello World"); seemed to spew out garbage (although both functions seemed to execute successfully).

Summary

I think the two main points brought up in this thread are both equally valid (summarized below):

  1. The embedded-hal should provide zero-cost abstractions (or near-zero) to provide functionality.
  2. The embedded-hal should provide a common API that higher-level drivers can use.

To these points, issues have been brought up that seem contradictory to the above points:

  • (1) having an implicit copy from flash to RAM may trigger timing and performance inefficiencies.
  • (2) Not copying buffers provided to the EasyDMA can potentially break higher level drivers, which should not need to know that the EasyDMA cannot transfer from flash, since they are (ideally) platform-independent.

Example

Take a hypothetical example of a sensor that communicates via UART. This driver uses a 4-byte sync word before each command. For efficiency, the device driver crate stores this 4-byte sync word as a constant and sends it before each command to the device. The sync word is stored in flash, but commands are RAM-based.

When an end-user comes and utilizes the provided device crate, it won’t work without a copy from flash to RAM, so the user has to clone the device crate and modify it specifically for use with this platform, which breaks the modularity that crates provide.

Proposal

Performing implicit copies without the end-users knowledge should be avoided, since this would not be a zero-cost abstraction. Instead, I propose that we develop a cargo feature within the nrf52-hal that will allow the user to specifically allow implicit copies from flash to RAM if data is provided that exists in flash. If the feature is not enabled, the EasyDMA driver will return an Error whenever data contained in flash is specified. This has the benefits of:

  1. By default, users will be informed that the EasyDMA driver cannot transfer data from flash. No implicit performance hits. The user is informed of the problem and the error can inform them of the optional feature.
  2. When the user enables the feature, they are acknowledging that data transferred via EasyDMA may be copied from flash into a RAM buffer. The copy is now completed with explicit consent from the end-user.

@nrf-rs/nrf52 do we maybe want to bring this up to the embedded wg, or maybe the embedded-hal team? I think this is probably worth discussing for the proposed “patterns book”.

I think the approach made by @simonsso is very good if we want to take a “works at all cost” approach (though, I think that is too strong of a wording, you can get optimal behavior if you know to send data out of RAM, though I admit it is also possible to get wrong and have no notification).

It might be good to discuss with the embedded-hal team whether the goal is portability at all costs, or portable with optimal behavior as the primary goal.

@Yatekii @hannobraun I haven’t had a chance to look at this project for a few months (Hopefully work will quiet down again soon) so I don’t feel I can really comment on the specifics of this issue. But I think I can talk generally.

I do think this raises an interesting architecture issue, we have a library that runs on multiple platforms but can’t always behave the same way on all platforms. This can’t be the only place this sort of issue will arise and I would prefer a consistent approach. It might be simple in this case to add an implicit workaround but it might not be in other cases and so I would like to see all cases made explicit.

As an aside I can’t remember being upset at a library for forcing me to deal with an error case, but I definitely remember the times a library has tried to be clever and implicitly ‘help’ me.