Dynamic Writes
A dynamic write stores into an element chosen by a runtime signal: the selected element takes the new value, every other element holds. Kathryn builds the per-element write-enable decode for you — a guarded clocked assign on each element’s register.
Dynamic writes require a reg backing and the clocked operator |=. There
is no combinational variant: a wire cannot “hold” the non-selected elements.
Field write through a dynamic index
Section titled “Field write through a dynamic index”The write-side mirror of a dynamic read — index with a signal (binary address)
or oh(signal) (one-hot), then assign the field:
class RfEntry(Karray): valid = kaf(1) data = kaf(8)
class worker(Module): @init def decl(self): self.rf = RfEntry(HwComponentType.REG, (4,), "rf") self.sel = reg(2) # binary address self.ohs = reg(4) # one-hot select self.src = reg(8)
@flow def f(self): with seq(): self.rf[self.sel].data |= self.src # binary: enable is (sel == k) self.rf[oh(self.ohs)].data |= self.src # one-hot: enable is ohs[k]For each element k, the emitted register write is guarded by that element’s
enable:
always @(posedge clk) begin if (seq_state) begin if (EXPR_sel_EQ1) begin // sel == 1 REG_rf_E1_data[7:0] <= VAL_src[7:0]; // only element 1 written end endendThe decode enables exactly the selected element; every other element holds:
flowchart TB
SEL["sel (binary address)"] --> E0["element 0: en=(sel==0)"]
SEL --> E1["element 1: en=(sel==1)"]
SEL --> E2["element 2: en=(sel==2)"]
SEL --> E3["element 3: en=(sel==3)"]
E0 --> H0["holds"]
E1 --> H1["written from src"]
E2 --> H2["holds"]
E3 --> H3["holds"]
Whole-element write map
Section titled “Whole-element write map”A dynamic index also accepts the {field_name: source} map form — each named
source lands on the field of that name, on the runtime-selected element:
self.rf[self.sel] |= {"valid": self.v, "data": self.d}Pitfalls
Section titled “Pitfalls”Custom write enables: cus_dynamic_assign
Section titled “Custom write enables: cus_dynamic_assign”The built-in decodes cover “exactly one element, selected by binary or one-hot
address”. When you need anything else — a range of elements, a comparison, a
mask you compute yourself — use cus_dynamic_assign and write the enable
logic in a callback:
Karray.cus_dynamic_assign(dims, src, write_fn, clocked=True)dims— one entry per dimension: anintpins the dimension (only that index’s slice is touched),Spreadfans it out over its extent. At least one dimension must beSpread.src— a{field_name: source}map; sources are matched to fields by name, full-width.write_fn— called once per spread element with aWriteView. The view exposesview.coord, the element’s static coordinate (a list of ints). The function must return a 1-bit write-enable signal; the element is written fromsrconly when the enable is high, and holds otherwise.
Close over whatever runtime signals you like to build the enable — the coordinate is compile-time, the comparison is hardware:
self.rf = RegFile(HwComponentType.REG, (4,), "rf")self.sel = reg(2)self.src = reg(8)
with seq(): # equivalent to the built-in binary decode: enable = (sel == coord) self.rf.cus_dynamic_assign( [Spread], {"data": self.src}, lambda v: self.sel == v.coord[0], )The power is in non-trivial enables. This writes a sentinel into every element whose index is below a threshold — a multi-element write, which the built-in decodes cannot express:
# write SENTINEL wherever thr > coord (elements 0 and 1 when thr == 2)self.rg.cus_dynamic_assign( [Spread], {"data": self.c_sn}, lambda v: self.thr > v.coord[0],)On a 2-D array you can pin one dimension and spread the other:
dims=[1, Spread] fans out over row 1 only.
Spread fans the callback out over the dimension’s extent — one enable per
element, each built from that element’s static coord:
flowchart TB
CUS["cus_dynamic_assign<br/>dims=[Spread]"] --> C0["coord=0: write_fn -> 1b enable"]
CUS --> C1["coord=1: write_fn -> 1b enable"]
CUS --> C2["coord=2: write_fn -> 1b enable"]
CUS --> C3["coord=3: write_fn -> 1b enable"]
C0 --> R["element written from src<br/>when enable high, else holds"]
C1 --> R
C2 --> R
C3 --> R
Worked examples
Section titled “Worked examples”- tc32 exercises all three built-in write decodes on one register file — binary, one-hot, and a whole-element map — each landing on exactly one element while the others hold.
- tc33 exercises
cus_dynamic_assign: per-element enables built fromview.coordagainst a closed-over selector, plus the threshold range write above.
Both are listed in the Examples Gallery.