Skip to content

Configurable constants #25

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading