Overruns

    You probably received something like this on your laptop when you executed the program compiled indebug mode.

    1. (..)
    2. The uic brwn oxjums oer helaz do.

    And if you compiled in release mode, you probably only got something like this:

    What went wrong?

    You see, sending bytes over the wire takes a relatively large amount of time. I already did the mathso let me quote myself:

    Our pangram has a length of 45 bytes. That means it’s going to take, at least, 3,900 microseconds(45 bytes / (11,520 bytes/s) = 3,906 us) to send the string. The processor is working at 8 MHz,where executing an instruction takes 125 nanoseconds, so it’s likely going to be done with the forloop is less than 3,900 microseconds.

    We can actually time how long it takes to execute the for loop. aux11::init() returns aMonoTimer (monotonic timer) value that exposes an Instant API that’s similar to the one instd::time.

    1. #![deny(unsafe_code)]
    2. #![no_main]
    3. #![no_std]
    4. #[allow(unused_imports)]
    5. use aux11::{entry, iprint, iprintln};
    6. #[entry]
    7. fn main() -> ! {
    8. let (usart1, mono_timer, mut itm) = aux11::init();
    9. let instant = mono_timer.now();
    10. // Send a string
    11. for byte in b"The quick brown fox jumps over the lazy dog.".iter() {
    12. }
    13. let elapsed = instant.elapsed(); // in ticks
    14. iprintln!(
    15. &mut itm.stim[0],
    16. "`for` loop took {} ticks ({} us)",
    17. elapsed,
    18. elapsed as f32 / mono_timer.frequency().0 as f32 * 1e6
    19. );
    20. loop {}
    21. }

    In debug mode, I get:

    This is less than 3,900 microseconds but it’s not that far off and that’s why only a few bytes ofinformation are lost.

    How do we avoid this? The status register (ISR) has a flag, TXE, that indicates if it’s “safe”to write to the TDR register without incurring in data loss.

    Let’s use that to slowdown the processor.

    1. #![deny(unsafe_code)]
    2. #![no_main]
    3. #![no_std]
    4. #[allow(unused_imports)]
    5. use aux11::{entry, iprint, iprintln};
    6. #[entry]
    7. let (usart1, mono_timer, mut itm) = aux11::init();
    8. // Send a string
    9. for byte in b"The quick brown fox jumps over the lazy dog.".iter() {
    10. // wait until it's safe to write to TDR
    11. while usart1.isr.read().txe().bit_is_clear() {} // <- NEW!
    12. usart1.tdr.write(|w| w.tdr().bits(u16::from(*byte)));
    13. }
    14. let elapsed = instant.elapsed(); // in ticks
    15. iprintln!(
    16. &mut itm.stim[0],
    17. "`for` loop took {} ticks ({} us)",
    18. elapsed,
    19. elapsed as f32 / mono_timer.frequency().0 as f32 * 1e6
    20. );
    21. loop {}
    22. }

    This time, running the program in debug or release mode should result in a complete string on thereceiving side.

    The timing of the for loop should be closer to the theoretical 3,900 microseconds as well. Thetiming below is for the debug version.

    1. $ # itmdump terminal