Giter Site home page Giter Site logo

bitfield's Introduction

bitbybit: Bit fields and bit enums

This crate provides macros that create bit fields and bit enums, which are useful in bit packing code (e.g. in drivers or networking code).

Some highlights:

  • Highly efficient and 100% safe code that is just as good and hand-writen shifts and masks,
  • Full compatibility with const contexts,
  • Useable in no-std environments,
  • Strong compile time guarantees (for example, taking 5 bits out of a bitfield and putting them into another won't even need to compile a bounds check),
  • Automatic creation of bitenums, which allow converting enums to/from numbers,
  • Array support within bitfields to represent repeating bit patterns.

Basic declaration

A bit field is created similar to a regular Rust struct. Annotations define the layout of the structure. As an example, consider the following definition, which specifies a bit field:

#[bitfield(u32)]
struct GICD_TYPER {
    #[bits(11..=15, r)]
    lspi: u5,

    #[bit(10, r)]
    security_extn: bool,

    #[bits(5..=7, r)]
    cpu_number: u3,

    #[bits(0..=4, r)]
    itlines_number: u5,
}

How this works:

  • #[bitfield(u32)] specifies that this is a bitfield in which u32 is the underlying data type. This means that all the bits inside of the bitfield have to fit within 32 bits. u8, u16, u32, u64 and u128 are supported as underlying data types.
  • Each field is annotated with the range of bits that are used by the field. The data type must match the number of bits: A range of 0..=8 with u8 would cause a compile error, as u9 is the data type that matches 0..=8.
  • Single bit fields are declared as "bit", all other fields as "bits"
  • Valid data types for fields are the basic types u8, u16, u32, u64, u128, bool as well as enums (see below) or types like u1, u2, u3 from arbitrary-int
  • Bit numbering is LSB0, which means that bits are counted from the bottom: bit(0) has a value of 0x1, bit(1) is 0x2, bit(2) is 0x4, bit(15) is 0x8000 and so on. An MSB0 mode (to match documentation of some big endian devices) can be added later, if there is demand.
  • Fields are declared as "r" for read-only, "w" for write-only or "rw" as read/write. In the example above, all fields are read-only as this specific register is only used to read values.

Enumerations

Very often, fields aren't just numbers but really enums. This is supported by first defining a bitenum and then using that inside of a bitfield:

#[bitenum(u2, exhaustive: false)]
enum NonExhaustiveEnum {
    Zero = 0b00,
    One = 0b01,
    Two = 0b10,
}

#[bitenum(u2, exhaustive: true)]
enum ExhaustiveEnum {
    Zero = 0b00,
    One = 0b01,
    Two = 0b10,
    Three = 0b11,
}

#[bitfield(u64, default: 0)]
struct BitfieldWithEnum {
    #[bits(2..=3, rw)]
    e2: Option<NonExhaustiveEnum>,

    #[bits(0..=1, rw)]
    e1: ExhaustiveEnum,
}
  • The bitenum macro turns an enum into an enum that can be used within bitfields. The first argument is the base data type, which specifies, how many bits the enum occupies. This can be any value from u1 up to u64
  • When an enum is used within a bitfield, the number of bits has to match - if it doesn't, there will be a compiler error
  • The exhaustive argument specifies whether every possible bit combination is contained within the enum. The example above has both an exhaustive and a non-exhaustive enum. Notice how the non-exhaustive enum has to be wrapped in an Option to account for the case of e2 not being one of the defined enum values.

Arrays

Sometimes, bits inside bitfields are repeated. To support this, this crate allows specifying bitwise arrays. For example, the following struct gives read/write access to each individual nibble (hex character) of a u64:

#[bitfield(u64, default: 0)]
struct Nibble64 {
     #[bits(0..=3, rw)]
     nibble: [u4; 16],
}

Arrays can also have a stride. This is useful in the case of multiple smaller values repeating. For example, the following definition provides access to each bit of each nibble:

#[bitfield(u64, default: 0)]
struct NibbleBits64 {
    #[bit(0, rw, stride: 4)]
    nibble_bit0: [bool; 16],

    #[bit(1, rw, stride: 4)]
    nibble_bit1: [bool; 16],

    #[bit(2, rw, stride: 4)]
    nibble_bit2: [bool; 16],

    #[bit(3, rw, stride: 4)]
    nibble_bit3: [bool; 16],
}

Default

Bitfields can always be created through new_with_raw_value():

let a = NibbleBits64::new_with_raw_value(0x43218765);

However, pretty often a specific value can be considered the default (for example 0). This can be specified like this:

#[bitfield(u32, default: 0x1234)]
struct Bitfield1 {
  #[bits(0..=3, rw)]
  nibble: [u4; 4],
}

If a default value is specified, the bitfield can easily be created with this specific value:

let a = Bitfield1::Default;
const A: Bitfield1 = Bitfield1::DEFAULT;

Default values are used as-is, even if they affect bits that aren't defined within the bitfield.

Setting all fields at once using the builder syntax (experimental)

It is possible to set all fields at once, like this:

const T: Test = Test::builder()
    .with_baudrate(0x12)
    .with_some_other_bits(u4::new(0x2))
    .with_array([1, 2, 3, 4])
    .build();

Using builder() it is impossible to forget setting any fields. This is checked at compile time: If any field is not set, build() can not be called.

At the moment, it is required to set all fields in the same order as they are specified. As Rust's const generics become more powerful, this restriction might be lifted.

For the builder() to be available, the following has to be true:

  • The bitfield has to be completely filled with writable fields (no gaps) OR there has to be a default value specified,
  • No writable fields overlap.

This syntax is experimental. Use the experimental_builder_syntax feature to enable it and please provide feedback.

Dependencies

Arbitrary bit widths like u5 or u67 do not exist in Rust at the moment. Therefore, the following dependency is required:

arbitrary-int = "1.2.6"

Usage

Even though bitfields feel somewhat like structs, they are internally implemented as simple data types like u32. Therefore, they provide an immutable interface: Instead of changing the value of a field, any change operation will return a new bitfield with that field modified.

let a = NibbleBits64::new_with_raw_value(0x12345678_ABCDEFFF);
// Read a value
assert_eq!(u4::new(0xE), a.nibble(3));
// Change a value
let b = a.with_nibble(0, u4::new(0x3))
assert_eq!(0x12345678_ABCDEFF3, b.raw_value());

bitfield's People

Contributors

danlehmann avatar hecatia-elegua avatar vortexofdoom avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.