msp432-hal/src/bitband.rs

75 lines
2.7 KiB
Rust

//! Bit-banding access for peripheral registers
//!
//! Accesses to the bit-band region translate to the same-width accesses in the
//! target region, so it is important to use the correct pointer size for the desired
//! access.
//!
//! Bit-band writes on the Cortex M4F translate to an atomic read/modify/write operation
use core::ptr::{write_volatile, read_volatile};
/// This is the first address in the SRAM bit-band region.
const SRAM_BASE: usize = 0x2000_0000;
/// This is the first address *beyond* the SRAM bit-band region.
const SRAM_LIMIT: usize = 0x2000_8000;
/// This is the first address in the peripheral bit-band region.
const PERIPHERAL_BASE: usize = 0x4000_0000;
/// This is the first address *beyond* the peripheral bit-band region.
const PERIPHERAL_LIMIT: usize = 0x4010_0000;
/// Each bit-band alias region starts at this offset from the base of
/// the region that it aliases.
const ALIAS_OFFSET: usize = 0x0200_0000;
const REGION_MASK: usize = 0xF000_0000;
const WORD_MASK: usize = 0x0FFF_0000;
/// Atomically sets (via read/modify/write) a single bit at the given address
/// without affecting other bits in that memory location.
#[inline]
pub unsafe fn set_bit<T>(address: *const T, bit: u8) {
let address = address as usize;
let bit_address = bitband_alias_of(address, bit);
write_volatile(bit_address as *mut _, 0x01);
}
/// Atomically clears (via read/modify/write) a single bit at the given address
/// without affecting other bits in that memory location.
#[inline]
pub unsafe fn clear_bit<T>(address: *const T, bit: u8) {
let address = address as usize;
let bit_address = bitband_alias_of(address, bit);
write_volatile(bit_address as *mut _, 0x00);
}
/// Reads a single bit at the given address without affecting other
/// bits in that memory location.
#[inline]
pub unsafe fn read_bit<T>(address: *const T, bit: u8) -> T {
let address = address as usize;
let bit_address = bitband_alias_of(address, bit);
read_volatile(bit_address as *const _)
}
/// Calculate the address in the bitband region of the given bit
#[inline]
fn bitband_alias_of(address: usize, bit: u8) -> usize {
// Only bits 0-31 are valid
assert!(bit < 32);
let region_base = address & REGION_MASK;
// Ensure the address falls in a bit-band region
assert!((region_base == SRAM_BASE && address < SRAM_LIMIT) ||
(region_base == PERIPHERAL_BASE && address < PERIPHERAL_LIMIT));
let bit_number = bit as usize;
let bit_band_base = region_base + ALIAS_OFFSET;
let byte_offset = address & WORD_MASK;
let bit_word_offset = (byte_offset * 32) + (bit_number * 4);
let bit_word_addr = bit_band_base + bit_word_offset;
bit_word_addr
}