#bitflags #bits #ffi #bit #embedded

bitregions-impl

bitregionscrate的实现

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日

#45 in #bitflags

每月 43 次下载
用于 2 个crate(通过 bitregions

GPL-3.0-or-later

24KB
578

bitregions

docs.rs/bitregions Build Status

生成一个单元结构来表示一组位区域。旨在用于在结构体/集合中作为位标志持有,以及在更多嵌入式应用程序中代表类似内存映射寄存器的东西。

此crate被设置为 #![no_std] 因此可以自由地在其他此类crate中使用。

区域根据宏提供的 #repr({type}) 属性给出。

为新的结构体生成以下特质

  • Into<{repr}>
  • From<{repr}>
  • PartialEq
  • Display
    • 如果设置,则切换打印其名称
    • 多比特始终打印 {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