Skip to content

Commit f1eef15

Browse files
authored
Merge pull request #30 from lord-ne/rerewrite
Rewrite: Prevent overflow from public functions Hopefully are the underlaying commits preserved. Yes, I do fear that "github.com" does `git merge --squash`. What "lord-ne" wrote: This is the rewrite discussed in #17. Its main purpose is to ensure that public functions cannot overflow, by having them delegate to private functions that accept larger parameters. Some work was also put in to mitigating the effects of the delays not being inlined at compile time. Additional a link to #22 are is being asked for good commit messages.
2 parents 8ec7a7c + b1963f7 commit f1eef15

File tree

2 files changed

+81
-42
lines changed

2 files changed

+81
-42
lines changed

src/delay_impl.rs

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#[allow(unused_imports)]
2+
use core::arch::asm;
3+
4+
/// Internal function to implement a variable busy-wait loop.
5+
/// # Arguments
6+
/// * 'count' - an u32, the number of times to cycle the loop (4 clock cycles per loop).
7+
#[inline(always)]
8+
pub fn delay_count_32(count: u32) {
9+
let mut outer_count: u16 = (count >> 16) as u16;
10+
let inner_count: u16 = count as u16;
11+
if inner_count != 0 {
12+
delay_loop_4_cycles(inner_count);
13+
}
14+
while outer_count != 0 {
15+
delay_loop_4_cycles(0);
16+
outer_count -= 1;
17+
}
18+
}
19+
20+
/// Internal function to implement a variable busy-wait loop.
21+
/// # Arguments
22+
/// * 'count' - an u64, the number of times to cycle the loop (4 clock cycles per loop). *The top 16 bits are ignored.*
23+
#[inline(always)]
24+
pub fn delay_count_48(count: u64) {
25+
let mut outer_count: u32 = (count >> 16) as u32;
26+
let inner_count: u16 = count as u16;
27+
if inner_count != 0 {
28+
delay_loop_4_cycles(inner_count);
29+
}
30+
while outer_count != 0 {
31+
delay_loop_4_cycles(0);
32+
outer_count -= 1;
33+
}
34+
}
35+
36+
/// Internal function to implement a 16-bit busy-wait loop in assembly.
37+
/// Delays for 4 cycles per iteration, not including setup overhead.
38+
/// Up to 2^16 iterations (the value 2^16 would have to be passed as 0).
39+
/// # Arguments
40+
/// * 'cycles' - an u16, the number of times to cycle the loop.
41+
#[inline(always)]
42+
#[allow(unused_variables, unused_mut, unused_assignments, dead_code)]
43+
pub fn delay_loop_4_cycles(mut cycles: u16) {
44+
#[cfg(target_arch = "avr")]
45+
unsafe {
46+
asm!("1: sbiw {i}, 1",
47+
"brne 1b",
48+
i = inout(reg_iw) cycles => _,
49+
)
50+
}
51+
// Allow compilation even on non-avr targets, for testing purposes
52+
}

src/lib.rs

+29-42
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#![no_std]
33
#![crate_name = "avr_delay"]
44

5-
use core::arch::asm;
5+
mod delay_impl;
66

77
/// This library is intended to provide a busy-wait delay
88
/// similar to the one provided by the arduino c++ utilities
@@ -12,60 +12,47 @@ use core::arch::asm;
1212
// This library does all of the busy-wait loop in rust.
1313
// We pack as much of the looping as possible into asm!
1414
// so that we can count cycles.
15-
//
16-
// Ignoring the overhead, which may be significant:
17-
// An arduino runs at 16MHZ. Each asm loop is 4 cycles.
18-
// so each loop is 0.25 us.
19-
//
20-
// the overhead of delay() seems to be about 13 cycles
21-
// initially, and then 11 cycles per outer loop. We ignore
22-
// all that.
2315

24-
/// Internal function to implement a variable busy-wait loop.
16+
/// Internal function to implement a variable busy-wait loop. Even if count isn't
17+
/// known at compile time, this function shouldn't have too much overhead.
2518
/// # Arguments
26-
/// * 'count' - a u64, the number of times to cycle the loop.
19+
/// * 'count' - an u32, the number of times to cycle the loop.
2720
#[inline(always)]
28-
pub fn delay(count: u64) {
29-
// Our asm busy-wait takes a 16 bit word as an argument,
30-
// so the max number of loops is 2^16
31-
let outer_count = count / 65536;
32-
let last_count = ((count % 65536) + 1) as u16;
33-
for _ in 0..outer_count {
34-
// Each loop through should be 4 cycles.
35-
let zero = 0u16;
36-
unsafe {
37-
asm!("1: sbiw {i}, 1",
38-
"brne 1b",
39-
i = inout(reg_iw) zero => _,
40-
)
41-
}
42-
}
43-
unsafe {
44-
asm!("1: sbiw {i}, 1",
45-
"brne 1b",
46-
i = inout(reg_iw) last_count => _,
47-
)
48-
}
21+
pub fn delay(count: u32) {
22+
delay_impl::delay_count_32(count);
4923
}
5024

5125
///delay for N milliseconds
5226
/// # Arguments
53-
/// * 'ms' - an u64, number of milliseconds to busy-wait
27+
/// * 'ms' - an u32, number of milliseconds to busy-wait. This should be known at
28+
/// compile time, otherwise the delay may be much longer than specified.
5429
#[inline(always)]
55-
pub fn delay_ms(ms: u64) {
56-
// microseconds
57-
let us = ms * 1000;
58-
delay_us(us);
30+
pub fn delay_ms(ms: u32) {
31+
const GCD: u32 = gcd(avr_config::CPU_FREQUENCY_HZ, 4_000);
32+
const NUMERATOR: u32 = avr_config::CPU_FREQUENCY_HZ / GCD;
33+
const DENOMINATOR: u32 = 4_000 / GCD;
34+
let ticks: u64 = (u64::from(ms) * u64::from(NUMERATOR)) / u64::from(DENOMINATOR);
35+
delay_impl::delay_count_48(ticks);
5936
}
6037

6138
///delay for N microseconds
6239
/// # Arguments
63-
/// * 'us' - an u64, number of microseconds to busy-wait
40+
/// * 'ms' - an u32, number of microseconds to busy-wait. This should be known at
41+
/// compile time, otherwise the delay may be much longer than specified.
6442
#[inline(always)]
65-
pub fn delay_us(us: u64) {
66-
let us_in_loop = (avr_config::CPU_FREQUENCY_HZ / 1000000 / 4) as u64;
67-
let loops = us * us_in_loop;
68-
delay(loops);
43+
pub fn delay_us(us: u32) {
44+
const GCD: u32 = gcd(avr_config::CPU_FREQUENCY_HZ, 4_000_000);
45+
const NUMERATOR: u32 = avr_config::CPU_FREQUENCY_HZ / GCD;
46+
const DENOMINATOR: u32 = 4_000_000 / GCD;
47+
let ticks: u64 = (u64::from(us) * u64::from(NUMERATOR)) / u64::from(DENOMINATOR);
48+
delay_impl::delay_count_48(ticks);
49+
}
50+
51+
const fn gcd(mut a: u32, mut b: u32) -> u32 {
52+
while b != 0 {
53+
(a, b) = (b, a % b);
54+
}
55+
return a;
6956
}
7057

7158
#[cfg(test)]

0 commit comments

Comments
 (0)