Lowent Manual←↑→

wire — dividing one word into fields

Source
lib/wire.low
Layer
L1 — pure computation
Capabilities
none

When several values sit side by side inside one integer, this handles each field with a single mask. The mask says both where the field starts (position) and how many bits it has (width), so those two numbers, which always drift apart when written by hand, cannot drift. Carrying a date-time as one word (clock.local_packed), putting shard, slot and generation in a handle (budget), reading and writing device register fields — every place one word is used as several fields.

mask = 0x0f00        →  position 8 · width 4
wire.pick  mask w    →  (w & mask) >> 8       extract the field's value
wire.put   mask v    →  (v << 8) & mask       move a value into the field's position
wire.merge mask w v  →  (w & ~mask) | put     replace only that field of the word
fn m_month output u64 . do return 64424509440 . end
let mo u64 be wire.pick (m_month) w .
guard wire.fits (m_month) 9 . else return 1 .
let w2 u64 be wire.merge (m_month) w 9 .

What this module guards

Position and width come from one number — “fixed the position but not the width” cannot happen. This module’s tests keep the record of a hand-written complement mask off by 232 that a test with zero arguments let through. A zero mask is rejected — every op carries requires ne mask 0. The cost is zero — trailing_zeros of a constant mask folds at compile time, and the machine instruction count equals hand-written code (x86-64 gcc -O2: extract 18 = 18, replace 27 = 27, measured by tests every run).
opWhat it does
shift_of · max_of · solidMask position · largest value fitting the field · is the mask contiguous
pick · put · mergeExtract · move into position · replace only that field
fitsDoes the value fit the field
polarity v mInvert only the mask’s bits (active-low)
reverse_bits v wReverse bit order within width w
onwire order inv v wApply a wire descriptor at once (compile-time values)
permute v tab · is_identity tabBit permutation · is it the identity
to_set now want · to_clear now wantSet mask · clear mask

Table 50.1 — Ops of wire

Byte order is the builtin byte_swap. The axes for going onto a wire (polarity, bit order, byte order, permutation) are independent of each other.

Why set/clear pairs, not “a value”. Writing a whole value to a port means read → modify → write. If an interrupt turns on another bit in between, my write erases it. So hardware provides set-mask and clear-mask registers (STM32′s BSRR) — nothing is read, so nothing is lost. This is correctness, not convenience (chapter 30). Identity is free — onwire 0 0 v w has the same instruction count as code returning the value unchanged. A permutation table is a runtime value and does not fold, so is_identity lets you ask before calling.

Counter-example. Writing the position again separately from the mask

bit_and (shr w 32) 15 has position (32) and width (15) as two numbers, so fixing one silently breaks. wire.pick takes one number.

Counter-example. Inserting without asking whether it fits

A month is four bits. wire.merge (m_month) w 20 silently inserts 4 (= 20 & 15). wire.fits answers that question — the two are not one op because answering failure as a value would force unwrapping an option everywhere.

Cautions. Write each mask in one place (one name, like fn m_month output u64 .). Masks with holes (non-contiguous bits) are not handled — solid answers that. There is no width/frame axis (an 8-bit value in a 12-bit frame). Why the ops are not get or read — get is a word the processor already uses, so it would never be called, and read collided with C’s read and broke native builds. Library op names must avoid the processor’s words, the specification’s tables and libc alike.