bitregions
Generate a unit structure to represent a set of bit-regions.
Intended to be used both as bitflags held in structs/collections as well
as representing something like a memory-mapped register in more embedded
applications.
This crate is set as #![no_std]
so it can freely be used in other such crates.
Regions are given the #repr({type})
attribute based on the {repr}
given to the macro.
The following traits are generated for the new struct:
Into<{repr}>
From<{repr}>
PartialEq
Display
- toggles print their name if set
- multibit always prints
{name}={val}
Debug
+
and +=
-
and -=
*
and *=
/
and /=
^
and ^=
|
and |=
&
and &=
Basic Example
Example purely to show the API.
Creates a stack-based u16 unit-struct with helper methods.
#[macro_use] extern crate bitregions;
bitregions! {
pub Example u16 {
EN_FEATURE: 0b0000000000000001,
EN_DEVICE: 0b0000000000000010,
PORT_NUM: 0b0000000000011100 | 0..=5, BUSY: 0b0000000001000000,
VAL_BUFFER: 0b1111111100000000,
}
pub fn port_and_value(port: u8, val: u8) -> Example {
let mut r = Example::new(0u16);
r.set_port_num(port);
r.set_val_buffer(val);
r
}
}
fn main() {
println!("value buffer mask is: {:#X}", Example::VAL_BUFFER);
let mut ex = Example::new(0u16);
ex.set_en_feature();
while ex.busy() { println!("bus is busy"); }
ex.toggle_busy();
assert_eq!(ex.extract_busy().raw() & Example::BUSY, Example::BUSY);
ex |= Example::port_and_value(4u8, 0x38u8);
ex.toggle_busy();
while ex.busy() { println!("waiting for response"); }
let resp = ex.val_buffer() as u16;
assert_eq!(resp << 8, ex.extract_val_buffer().raw());
ex.unset_en_feature();
ex += 1u16;
ex -= 1u16;
ex *= 2u16;
ex /= 2u16;
ex |= 0xBDu8;
ex &= 0xDBu8;
ex ^= ex;
ex = Example::with_en_feature(); ex.set_port_num(4u8);
ex.set_val_buffer(0xABu8);
let display = format!("{}", ex);
assert_eq!(display, "EN_FEATURE | PORT_NUM=0x4 | VAL_BUFFER=0xAB");
let debug = format!("{:?}", ex);
assert_eq!(debug, "0xAB11");
let tup = ex.port_num_tuple();
assert_eq!(
ex.port_num(),
match tup {
(0,0,0) => { 0 }
(0,0,1) => { 1 }
(0,1,0) => { 2 }
(0,1,1) => { 3 }
(1,0,0) => { 4 }
(1,0,1) => { 5 }
_ => { 0xFF }
},
"got {:?}, but expected {:b}", tup, ex.port_num(),
);
let bools = ex.port_num_bools();
if bools.1 {
}
}
Memory-mapped Example
A common case for bitmaps/bitflags/etc are memory-mapped registers.
Below is an example that creates a lifetimed reference to some memory
region this register would represent.
You can optionally provide a default address location using the
{name} {repr} @ {addr}
syntax. This variant returns a static, mutable ref.
#[macro_use] extern crate bitregions;
bitregions! {
pub Example u16 @ 0xDEADBEEF {
EN_FEATURE: 0b0000000000000001,
EN_DEVICE: 0b0000000000000010,
PORT_NUM: 0b0000000000011100 | 0..=5, BUSY: 0b0000000001000000,
VAL_BUFFER: 0b1111111100000000,
}
}
const MEMIO_ADDR: usize = 0xC0FFEE;
bitregions! {
pub MemIOBase u16 @ MEMIO_ADDR {
SOME_REGION: 0b0000000000000001,
}
}
bitregions! {
pub ControlReg u16 @ MEMIO_ADDR + 0x80 {
SOME_REGION: 0b0000000000000001,
}
}
fn main() {
let mem: [u8; 4096] = [0u8; 4096];
let ex = unsafe { Example::at_addr_mut(&mem[8] as *const _ as usize) };
ex.set_en_feature();
assert!(ex.en_feature());
ex.set_val_buffer(128u8);
println!("{:#X}", ex.val_buffer());
assert_eq!(128, ex.val_buffer());
let ptr = unsafe { Example::default_ptr() };
assert_eq!(ptr as *mut _ as usize, 0xDEADBEEF);
let memio = unsafe { MemIOBase::default_ptr() };
assert_eq!(memio as *mut _ as usize, MEMIO_ADDR);
let control = unsafe { ControlReg::default_ptr() };
assert_eq!(control as *mut _ as usize, MEMIO_ADDR + 0x80);
}
From Reference Example
Below is an example which casts a reference of the region's underlying
type to our generated struct. This allows you to "add features" to a raw
value. While safer than the memory-mapped example but is still unsafe code
as you could share a reference into a slice.
#[macro_use] extern crate bitregions;
bitregions! {
pub Example u16 {
EN_FEATURE: 0b0000000000000001,
EN_DEVICE: 0b0000000000000010,
PORT_NUM: 0b0000000000011100 | 0..=5, BUSY: 0b0000000001000000,
VAL_BUFFER: 0b1111111100000000,
}
}
fn main() {
let mut mem: [u8; 4096] = [0u8; 4096];
let ex = unsafe { Example::at_ref_mut(&mut mem[8]) };
ex.set_en_feature();
assert!(ex.en_feature());
ex.set_val_buffer(128u8);
println!("{:#X}", ex.val_buffer());
assert_eq!(128, ex.val_buffer());
}
Debug Assertions
When built in debug-mode, setters will assert the given value
both fits in the region (4bit number in 2bit region) and is within
the (optional) range (3bit region, 0-5 allowed, given 7).
#[macro_use] extern crate bitregions;
bitregions! {
pub Example u8 {
RANGED: 0b00011100 | 1..=6,
NON_RANGED: 0b11100000,
}
}
fn main() {
let mut ex = Example::new(0u8);
ex.set_ranged(1u8); ex.set_ranged(3u8); ex.set_ranged(6u8); ex.set_ranged(0u8); ex.set_ranged(7u8); ex.set_ranged(8u8);
ex.set_non_ranged(1u8); ex.set_non_ranged(3u8); ex.set_non_ranged(6u8); ex.set_non_ranged(0u8); ex.set_non_ranged(7u8); ex.set_non_ranged(8u8); }