Skip to content

Commit

Permalink
Merge pull request #25 from SamClercky/configurable_constants
Browse files Browse the repository at this point in the history
Configurable constants
  • Loading branch information
thvdveld authored May 31, 2024
2 parents a583f28 + 4bfcf22 commit a3ed43c
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 32 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@ Add this to your `Cargo.toml`:
dot15d4 = "0.1.0"
```

### Configurable features

* `std`: Enables `std` only features
* `log`: Use the `log` crate for structured logging
* `defmt`: Use the `defmt` crate for structured logging

### Configurable environment variables

* `DOT15D4_MAC_MIN_BE` (default: 0): Minimum backoff exponent used in `CSMA`
* `DOT15D4_MAC_MAX_BE` (default: 8): Maximum backoff exponent used in `CSMA`
* `DOT15D4_MAC_UNIT_BACKOFF_DURATION` (default: 320us): The time of one backoff period
* `DOT15D4_MAC_MAX_FRAME_RETRIES` (default: 3): Maximum CCA/ACK rounds
* `DOT15D4_MAC_AIFS_PERIOD` (default: 1ms): The minimal time for the receiving end to go from transmitting to receiving mode when sending an ACK
* `DOT15D4_MAC_SIFS_PERIOD` (default: 1ms): The inter-frame spacing time for short frames
* `DOT15D4_MAC_LIFS_PERIOD` (default: 10ms): The inter-frame spacing time for long frames

For more information, see the [API documentation](https://docs.rs/dot15d4).

## dot15d4 as a binary
Expand Down
66 changes: 66 additions & 0 deletions dot15d4/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use std::collections::HashMap;
use std::env;
use std::fmt::Write;
use std::path::PathBuf;

fn main() {
// (Variable, Type, Default value)
let mut configs: HashMap<&str, (&str, &str)> = HashMap::from([
("MAC_MIN_BE", ("u16", "0")),
("MAC_MAX_BE", ("u16", "8")),
("MAC_MAX_CSMA_BACKOFFS", ("u16", "16")),
(
"MAC_UNIT_BACKOFF_DURATION",
(
"Duration",
"Duration::from_us((UNIT_BACKOFF_PERIOD * SYMBOL_RATE_INV_US) as i64)",
),
),
("MAC_MAX_FRAME_RETIES", ("u16", "3")),
(
"CSMA_INTER_FRAME_TIME",
("Duration", "Duration::from_us(1000)"),
),
("MAC_AIFS_PERIOD", ("Duration", "Duration::from_us(1000)")),
("MAC_SIFS_PERIOD", ("Duration", "Duration::from_us(1000)")),
("MAC_LIFS_PERIOD", ("Duration", "Duration::from_us(10_000)")),
]);

// Make sure we get rerun if needed
println!("cargo:rerun-if-changed=build.rs");
for name in configs.keys() {
println!("cargo:rerun-if-env-changed=DOT15D4_{name}");
}

// Collect environment variables
let mut data = String::new();
// Write preamble
writeln!(data, "use crate::time::Duration;").unwrap();
writeln!(
data,
"use crate::csma::{{SYMBOL_RATE_INV_US, UNIT_BACKOFF_PERIOD}};"
)
.unwrap();

for (var, value) in std::env::vars() {
if let Some(name) = var.strip_prefix("DOT15D4_") {
// discard from hashmap as a way of consuming the setting
let Some((ty, _)) = configs.remove_entry(name) else {
panic!("Wrong configuration name {name}");
};

// write to file
writeln!(data, "pub const {name}: {ty} = {value};").unwrap();
}
}

// Take the remaining configs and write the default value to the file
for (name, (ty, value)) in configs.iter() {
writeln!(data, "pub const {name}: {ty} = {value};").unwrap();
}

// Now that we have the code of the configuration, actually write it to a file
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let out_file = out_dir.join("config.rs");
std::fs::write(out_file, data).unwrap();
}
33 changes: 20 additions & 13 deletions dot15d4/src/csma/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ where
// guaranteed to be exact. This is due to how Rust futures
// work and the timer becomes an 'at least this waiting time'
// The goal is to transmit an ACK between 1ms and 2ms.
let delay = ACKNOWLEDGEMENT_INTERFRAME_SPACING / 2;
let delay = MAC_AIFS_PERIOD / 2;
timer.delay_us(delay.as_us() as u32).await;

// We already have the lock on the radio, so start transmitting and do not
Expand Down Expand Up @@ -383,7 +383,7 @@ where
}

let mut radio_guard = None;
'ack: for i_ack in 0..MAC_MAX_FRAME_RETIES + 1 {
'ack: for i_ack in 1..MAC_MAX_FRAME_RETIES + 1 {
// Set vars for CCA
let backoff_strategy =
transmission::CCABackoffStrategy::new_exponential_backoff(&self.rng);
Expand All @@ -396,6 +396,7 @@ where
&mut tx,
&mut timer,
backoff_strategy,
&self.driver,
)
.await
{
Expand All @@ -413,10 +414,10 @@ where
.await;

// We expect an ACK to come back AIFS + time for an ACK to travel + SIFS (guard)
// An ACK is 3 bytes + 6 bytes (PHY header) long and should take around 288us at 250kbps to get back
let delay = ACKNOWLEDGEMENT_INTERFRAME_SPACING
+ MAC_SIFT_PERIOD
+ Duration::from_us(288);
// An ACK is 3 bytes + 6 bytes (PHY header) long
// and should take around 288us at 250kbps to get back
let delay = MAC_AIFS_PERIOD + MAC_SIFS_PERIOD + Duration::from_us(288);

match select::select(
Self::wait_for_valid_ack(
&mut *radio_guard.unwrap(),
Expand Down Expand Up @@ -449,7 +450,7 @@ where
radio_guard = None;

// Wait for SIFS here
let delay = MAC_SIFT_PERIOD.max(Duration::from_us(
let delay = MAC_SIFS_PERIOD.max(Duration::from_us(
(TURNAROUND_TIME * SYMBOL_RATE_INV_US) as i64,
));
timer.delay_us(delay.as_us() as u32).await;
Expand All @@ -459,6 +460,8 @@ where
// Fail transmission
self.driver.error(driver::Error::AckFailed).await;
break 'ack;
} else {
self.driver.error(driver::Error::AckRetry(i_ack)).await;
}
}
}
Expand Down Expand Up @@ -841,9 +844,11 @@ pub mod tests {
);
});
radio.wait_until_asserts_are_consumed().await;
assert_eq!(
monitor.errors.receive().await,
driver::Error::CcaFailed, // CCA has failed, so we propagate an error up
assert!(
matches!(
monitor.errors.receive().await,
driver::Error::CcaFailed | driver::Error::CcaBackoff(_), // CCA has failed, so we propagate an error up
),
"Packet transmission should fail due to CCA"
);
})
Expand Down Expand Up @@ -915,9 +920,11 @@ pub mod tests {
inner.total_event_count = 0;
});
radio.wait_until_asserts_are_consumed().await;
assert_eq!(
monitor.errors.receive().await,
driver::Error::AckFailed, // ACK has failed, so we propagate an error up
assert!(
matches!(
monitor.errors.receive().await,
driver::Error::AckFailed | driver::Error::AckRetry(_), // ACK has failed, so we propagate an error up
),
"Packet transmission should fail due to ACK not received after to many times"
);
})
Expand Down
17 changes: 14 additions & 3 deletions dot15d4/src/csma/transmission.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ use super::utils;

use crate::phy::config;
use crate::phy::config::TxConfig;
use crate::phy::driver;
use crate::phy::driver::Driver;
use crate::phy::driver::FrameBuffer;
use crate::phy::radio::futures::transmit;
use crate::phy::radio::Radio;
use crate::sync::channel::Sender;
use crate::sync::join::join;
use crate::sync::mutex::Mutex;
use crate::sync::mutex::MutexGuard;

Expand All @@ -19,19 +22,22 @@ pub enum TransmissionError {
CcaError,
}

pub async fn transmit_cca<'m, R, TIMER, Rng>(
#[allow(clippy::too_many_arguments)]
pub async fn transmit_cca<'m, R, TIMER, Rng, D>(
radio: &'m Mutex<R>,
radio_guard: &mut Option<MutexGuard<'m, R>>,
channel: config::Channel,
wants_to_transmit_signal: &Sender<'_, ()>,
tx_frame: &mut FrameBuffer,
timer: &mut TIMER,
mut backoff_strategy: CCABackoffStrategy<'_, Rng>,
driver: &D,
) -> Result<(), TransmissionError>
where
R: Radio,
TIMER: DelayNs,
Rng: RngCore,
D: Driver,
{
'cca: for number_of_backoffs in 1..MAC_MAX_CSMA_BACKOFFS + 1 {
// try to transmit
Expand Down Expand Up @@ -59,9 +65,14 @@ where
// Was this the last attempt?
if number_of_backoffs == MAC_MAX_CSMA_BACKOFFS {
return Err(TransmissionError::CcaError); // Fail transmission
} else {
// Perform backoff and report current status to driver
join(
backoff_strategy.perform_backoff(timer),
driver.error(driver::Error::CcaBackoff(number_of_backoffs)),
)
.await;
}

backoff_strategy.perform_backoff(timer).await;
}

Ok(())
Expand Down
42 changes: 26 additions & 16 deletions dot15d4/src/csma/user_configurable_constants.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
#![allow(dead_code)]
/// Export all user definable constants
pub use constants::*;

use crate::time::Duration;
#[cfg(test)]
mod constants {
#![allow(dead_code)]
use crate::csma::{SYMBOL_RATE_INV_US, UNIT_BACKOFF_PERIOD};
use crate::time::Duration;

use super::constants::{SYMBOL_RATE_INV_US, UNIT_BACKOFF_PERIOD};
// XXX These are just random numbers I picked by fair dice roll; what should
// they be?
pub const MAC_MIN_BE: u16 = 0;
pub const MAC_MAX_BE: u16 = 8;
pub const MAC_MAX_CSMA_BACKOFFS: u16 = 16;
pub const MAC_UNIT_BACKOFF_DURATION: Duration =
Duration::from_us((UNIT_BACKOFF_PERIOD * SYMBOL_RATE_INV_US) as i64);
pub const MAC_MAX_FRAME_RETIES: u16 = 3; // 0-7
pub const MAC_INTER_FRAME_TIME: Duration = Duration::from_us(1000); // TODO: XXX
/// AIFS=1ms, for SUN PHY, LECIM PHY, TVWS PHY
pub const MAC_AIFS_PERIOD: Duration = Duration::from_us(1000);
pub const MAC_SIFS_PERIOD: Duration = Duration::from_us(1000); // TODO: SIFS=XXX
pub const MAC_LIFS_PERIOD: Duration = Duration::from_us(10_000); // TODO: LIFS=XXX
}

// XXX These are just random numbers I picked by fair dice roll; what should
// they be?
pub const MAC_MIN_BE: u16 = 0;
pub const MAC_MAX_BE: u16 = 8;
pub const MAC_MAX_CSMA_BACKOFFS: u16 = 16;
pub const MAC_UNIT_BACKOFF_DURATION: Duration =
Duration::from_us((UNIT_BACKOFF_PERIOD * SYMBOL_RATE_INV_US) as i64);
pub const MAC_MAX_FRAME_RETIES: u16 = 3; // 0-7
pub const _MAC_INTER_FRAME_TIME: Duration = Duration::from_us(1000); // TODO: XXX
/// AIFS=1ms, for SUN PHY, LECIM PHY, TVWS PHY
pub const ACKNOWLEDGEMENT_INTERFRAME_SPACING: Duration = Duration::from_us(1000);
pub const MAC_SIFT_PERIOD: Duration = Duration::from_us(1000); // TODO: SIFS=XXX
pub const MAC_LIFS_PERIOD: Duration = Duration::from_us(10_000); // TODO: LIFS=XXX
#[cfg(not(test))]
mod constants {
#![allow(unused)]
include!(concat!(env!("OUT_DIR"), "/config.rs"));
}
4 changes: 4 additions & 0 deletions dot15d4/src/phy/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ use core::future::Future;
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Error {
/// Cca failed, resulting in a backoff (nth try)
CcaBackoff(u16),
/// Cca failed after to many fallbacks
CcaFailed,
/// Ack failed, resulting in a retry later (nth try)
AckRetry(u16),
/// Ack failed, after to many retransmissions
AckFailed,
/// The buffer did not follow the correct device structure
Expand Down

0 comments on commit a3ed43c

Please sign in to comment.