10个版本
0.2.5 | 2021年12月15日 |
---|---|
0.2.4 | 2020年4月4日 |
0.2.2 | 2020年3月28日 |
0.2.1 | 2020年2月27日 |
0.1.3 | 2020年2月23日 |
#331 in 嵌入式开发
每月 40 次下载
用于 async-mp4
34KB
366 行
bitregions
生成一个单元结构来表示一组位区域。旨在同时用作在结构体/集合中持有的位标志以及在某些更嵌入式应用中代表类似内存映射寄存器的功能。
此crate设置为#![no_std]
,因此可以自由地用于其他此类crate。
根据宏提供的#repr({type})
属性指定区域。
为新的结构生成以下特质
Into<{repr}>
From<{repr}>
PartialEq
Display
- toggles打印其名称(如果设置)
- multibit始终打印
{name}={val}
Debug
- 以十六进制形式打印原始值
+
和+=
-
和-=
*
和*=
/
和/=
^
和^=
|
和|=
&
和&=
基本示例
示例仅用于展示API。创建一个基于堆的u16单元结构,具有辅助方法。
#[macro_use] extern crate bitregions;
bitregions! {
pub Example u16 {
EN_FEATURE: 0b0000000000000001,
EN_DEVICE: 0b0000000000000010,
PORT_NUM: 0b0000000000011100 | 0..=5, // only 0-5 is valid
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);
// create an example memory mapped io register
// exists on the stack with the value 0.
// see below for using this as a pointer to the register.
let mut ex = Example::new(0u16);
// enable the feature this register governs
ex.set_en_feature();
// wait for the busy bit to clear
// then set busy to block reader (could be more pedantic with ex.set_busy())
while ex.busy() { println!("bus is busy"); }
ex.toggle_busy();
assert_eq!(ex.extract_busy().raw() & Example::BUSY, Example::BUSY);
// set the port to write to. must be 0-5
// otherwise we trigger a debug_assert! (removed in release builds)
// same with the value buffer
ex |= Example::port_and_value(4u8, 0x38u8);
// clear busy bit (could be more pedantic with ex.unset_busy())
ex.toggle_busy();
// wait for a response
while ex.busy() { println!("waiting for response"); }
// read the value out of the buffer (pre-shifted for you)
// then, assert the shift happened correctly by looking at the
// unshifted version returned by the extract_{field} variant.
let resp = ex.val_buffer() as u16;
assert_eq!(resp << 8, ex.extract_val_buffer().raw());
// disable the feature this register governs
ex.unset_en_feature();
//
// math and bitwise operations
//
ex += 1u16;
ex -= 1u16;
ex *= 2u16;
ex /= 2u16;
ex |= 0xBDu8;
ex &= 0xDBu8;
ex ^= ex;
//
// display and debug
//
ex = Example::with_en_feature(); // use a with_{field} ctor
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");
//
// get region as a tuple
//
// as a tuple of u8 e.g. (u8, u8, u8) for port_num
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(),
);
// or as a tuple of booleans e.g. (bool, bool, bool) for port_num
let bools = ex.port_num_bools();
if bools.1 {
// the second bit in the port number is set
}
}
内存映射示例
位图/位标志等的一个常见用例是内存映射寄存器。下面是一个示例,创建了一个指向寄存器所代表的一些内存区域的生存期引用。
你可以选择使用 {name} {repr} @ {addr}
语法来提供默认地址位置。这种变体返回一个静态可变引用。
#[macro_use] extern crate bitregions;
bitregions! {
pub Example u16 @ 0xDEADBEEF {
EN_FEATURE: 0b0000000000000001,
EN_DEVICE: 0b0000000000000010,
PORT_NUM: 0b0000000000011100 | 0..=5, // only 0-5 is valid
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() {
// create "fake memory" so the doc-test works
// address is the important thing
let mem: [u8; 4096] = [0u8; 4096];
// create a lifetimed reference to the register elsewhere
// in memory (the above slice, in our case, but could be anywhere)
let ex = unsafe { Example::at_addr_mut(&mem[8] as *const _ as usize) };
// everything else works like normal
ex.set_en_feature();
assert!(ex.en_feature());
ex.set_val_buffer(128u8);
println!("{:#X}", ex.val_buffer());
assert_eq!(128, ex.val_buffer());
// you can also initialize the pointer directly
let ptr = unsafe { Example::default_ptr() };
assert_eq!(ptr as *mut _ as usize, 0xDEADBEEF);
// but we cannot use it in the examples or it will segfault :/
// you can set the default address using a literal, ident, or const expression
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);
}
从引用示例
下面是一个示例,它将区域的底层类型引用转换为生成的结构体。这允许你为原始值“添加功能”。虽然比内存映射示例更安全,但仍然是危险代码,因为你可以将引用共享到切片中。
#[macro_use] extern crate bitregions;
bitregions! {
pub Example u16 {
EN_FEATURE: 0b0000000000000001,
EN_DEVICE: 0b0000000000000010,
PORT_NUM: 0b0000000000011100 | 0..=5, // only 0-5 is valid
BUSY: 0b0000000001000000,
VAL_BUFFER: 0b1111111100000000,
}
}
fn main() {
// create "fake memory" to illustrate the example
// the reference could be to a single u16 or relevant type...
let mut mem: [u8; 4096] = [0u8; 4096];
// create the reference -- this is unsafe because we allow
// for a wider range of types than strictly the underlying type.
// you can see in this example we use a &u8 to create (effectively) a &u16
let ex = unsafe { Example::at_ref_mut(&mut mem[8]) };
// everything else works like normal
ex.set_en_feature();
assert!(ex.en_feature());
ex.set_val_buffer(128u8);
println!("{:#X}", ex.val_buffer());
assert_eq!(128, ex.val_buffer());
}
调试断言
在调试模式下构建时,设置器将断言给定值既适合该区域(2位区域中的4位数字),又处于(可选)范围内(3位区域,0-5允许,给定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); // works fine
ex.set_ranged(3u8); // works fine
ex.set_ranged(6u8); // works fine
ex.set_ranged(0u8); // will panic do to range violation
ex.set_ranged(7u8); // will panic do to range violation
ex.set_ranged(8u8); // will panic do to region violation
ex.set_non_ranged(1u8); // works fine
ex.set_non_ranged(3u8); // works fine
ex.set_non_ranged(6u8); // works fine
ex.set_non_ranged(0u8); // works fine
ex.set_non_ranged(7u8); // works fine
ex.set_non_ranged(8u8); // will panic do to region violation
}
依赖关系
~1.5MB
~35K SLoC