Conversion & Resize
This page covers moving data between structures: copying regions of one
Karray into another, what happens when an assignment source’s width does not
match its destination, and — as the running worked example — the register-file
pattern from tc25.
Karray-to-karray assignment
Section titled “Karray-to-karray assignment”Assigning one Karray element or region to another copies field by field, paired by exact name and width:
class SrcArray(Karray): valid = kaf(1) data = kaf(8) note = kaf(4)
class DstArray(Karray): valid = kaf(1) data = kaf(8) tag = kaf(4)
self.src = SrcArray(HwComponentType.REG, (4,), "src")self.dst = DstArray(HwComponentType.REG, (4,), "dst")
with seq(): self.dst[0:2] |= self.src[1:3] # region copy: dst0<-src1, dst1<-src2 self.dst[3] |= self.src[0] # single-element copyThe rules:
- Shapes must match. A range slice keeps its dimension; an int collapses
it.
dst[0:2]andsrc[1:3]are both shape[2], so they pair up element-wise. Assigning a whole Karray (dst |= src) copies every element. - Fields pair by name + width. Here
validanddatacopy across.note(source-only) andtag(destination-only) have no partner, so they are skipped — and a Python warning fires naming the skipped field. A skipped destination field keeps whatever value it already had. - The operator follows the destination’s backing as usual:
|=for reg/mem-backed,*=for wire-backed. - Slice step must be 1, and slices cannot mix with dynamic (signal) indices. A dynamic-index destination cannot take a Karray region source at all.
Fields pair by name and width; unpaired fields on either side are skipped:
flowchart LR
SV["src.valid"] --> DV["dst.valid"]
SD["src.data"] --> DD["dst.data"]
SN["src.note (source-only)"] --> SK["skipped, warns"]
DT["dst.tag (destination-only)"] --> DK["unpaired, keeps old value"]
The element/region forms all route through the same machinery: dst[i] |= src[j],
dst[a:b] |= src[c:d], and dst |= src differ only in how much of each array
the selectors keep.
Assignment-source auto-resize
Section titled “Assignment-source auto-resize”Whenever any assignment’s source width differs from its destination — plain signals and Karray fields alike — the connector sanitizes the source rather than erroring:
| Mismatch | Behaviour |
|---|---|
| source narrower than destination | zero-extended (unsigned); high bits become 0 |
| source wider than destination | MSBs dropped; only the low bits land |
self.wide = reg(8) # 0xABself.narrow = reg(4) # 0xD
self.trunc = reg(4)self.extend = reg(8)
with seq(): self.trunc |= self.wide # 8 -> 4 : low nibble lands (0xB), warns self.extend |= self.narrow # 4 -> 8 : zero-extended (0x0D), warnsflowchart TB
N["source narrower than dest"] --> NZ["zero-extended (unsigned)<br/>high bits = 0, warns"]
W["source wider than dest"] --> WD["MSBs dropped<br/>only low bits land, warns"]
Worked example: a tiny register file (tc25)
Section titled “Worked example: a tiny register file (tc25)”The register-file pattern brings the pieces together: a 1-D reg-backed Karray as storage, static writes in both styles, and read-back through ordinary output registers.
class RfEntry(Karray): valid = kaf(1) data = kaf(7)
class tc25_karray_regfile(Module): @init def com_declare(self): # 4-entry register file; each field its own reg self.rf = RfEntry(HwComponentType.REG, (4,), "rf")
self.c_valid = val(1, 1, "c_valid") self.c_data = val(7, 42, "c_data")
# outputs mirroring the read-back fields self.o_valid = reg(1, "o_valid"); self.o_valid.mark_output("my_v") self.o_data = reg(7, "o_data"); self.o_data.mark_output("my_d")
@flow def my_flow(self): self.o_valid.reset(0) self.o_data.reset(0)
with seq(): # entry 0 — field-wise writes into the per-field regs self.rf[0].valid |= self.c_valid self.rf[0].data |= self.c_data
# entry 1 — whole-element write; each named source lands on its field self.rf[1] |= {"valid": self.c_valid, "data": self.c_data}
# read entry 0 back out through the output regs self.o_valid |= self.rf[0].valid self.o_data |= self.rf[0].dataEmitted per (element, field) — one register and one guarded clocked write each:
reg [0:0] REG_rf_E0_valid_5159;reg [6:0] REG_rf_E0_data_5160;
always @(posedge WIRE_clk) begin if (SR_ST_seq_state_0) begin REG_rf_E0_valid_5159[0:0] <= VAL_c_valid[0:0]; endendFrom here the pattern grows naturally:
- swap the constant indices for signals to get a real read/write port (Indexing, Dynamic Writes);
- add a Reduce to pick an entry by priority;
- switch the backing to
MEM_BLOCKwhen the table outgrows discrete registers (Backings).
The full, simulated versions are tc25 (register file), tc27 (resize), and
tc28 (karray-to-karray) in the
Examples Gallery.