Integer as Bit-Fields

Note

Nothing here cannot be done via standard bit-manipulation (i.e. shifting and masking).

Built-in support is more elegant and performant since it usually replaces a sequence of multiple steps.

Since bit-wise operators are defined on integer numbers, individual bits can also be accessed and manipulated via an indexing syntax.

If a bit is set (i.e. 1), the index access returns true.

If a bit is not set (i.e. 0), the index access returns false.

When a range is used, the bits within the range are shifted and extracted as an integer value.

Bit-fields are very commonly used in embedded systems which must squeeze data into limited memory. Built-in support makes handling them efficient.

Indexing an integer as a bit-field is disabled for the no_index feature.

Syntax

From Least-Significant Bit (LSB)

Bits in a bit-field are accessed with zero-based, non-negative integer indices:

integer [ index from 0 to 63 or 31 ]

integer [ index from 0 to 63 or 31 ] = true or false ;

Ranges can also be used:

integer [ start .. end ]
integer [ start ..= end ]

integer [ start .. end ] = new integer value ;
integer [ start ..= end ] = new integer value ;

Number of bits

The maximum bit number that can be accessed is 63 (or 31 under only_i32).

Bits outside of the range are ignored.

From Most-Significant Bit (MSB)

A negative index accesses a bit in the bit-field counting from the end, or from the most-significant bit, with −1 being the highest bit.

integer [ index from −1 to −64 or −32 ]

integer [ index from −1 to −64 or −32 ] = true or false ;

Ranges always count from the least-significant bit (LSB) and has no support for negative positions.

Number of bits

The maximum bit number that can be accessed is −64 (or −32 under only_i32).

Bit-Field Functions

The following standard functions (defined in the BitFieldPackage but excluded if using a raw Engine) operate on INT bit-fields.

These functions are available even under the no_index feature.

FunctionParameter(s)Description
get_bitbit number, counting from MSB if < 0returns the state of a bit: true if 1, false if 0
set_bit
  1. bit number, counting from MSB if < 0
  2. new state: true if 1, false if 0
sets the state of a bit
get_bits
  1. starting bit number, counting from MSB if < 0
  2. number of bits to extract, none if < 1, to MSB if ≥ length
extracts a number of bits, shifted towards LSB
get_bitsrange of bitsextracts a number of bits, shifted towards LSB
set_bits
  1. starting bit number, counting from MSB if < 0
  2. number of bits to set, none if < 1, to MSB if ≥ length
    3) new value
sets a number of bits from the new value
set_bits
  1. range of bits
  2. new value
sets a number of bits from the new value
bits method and property
  1. (optional) starting bit number, counting from MSB if < 0
  2. (optional) number of bits to extract, none if < 1, to MSB if ≥ length
allows iteration over the bits of a bit-field
bitsrange of bitsallows iteration over the bits of a bit-field

Example

// Assume the following bits fields in a single 16-bit word:
// ┌─────────┬────────────┬──────┬─────────┐
// │  15-12  │    11-4    │  3   │   2-0   │
// ├─────────┼────────────┼──────┼─────────┤
// │    0    │ 0-255 data │ flag │ command │
// └─────────┴────────────┴──────┴─────────┘

let value = read_start_hw_register(42);

let command = value.get_bits(0, 3);         // Command = bits 0-2

let flag = value[3];                        // Flag = bit 3

let data = value[4..=11];                   // Data = bits 4-11
let data = value.get_bits(4..=11);          // <- above is the same as this

let reserved = value.get_bits(-4);          // Reserved = last 4 bits

if reserved != 0 {
    throw reserved;
}

switch command {
    0 => print(`Data = ${data}`),
    1 => value[4..=11] = data / 2,
    2 => value[3] = !flag,
    _ => print(`Unknown: ${command}`)
}