Repository navigation
[Bug] "Unsupported read-to-read connection" for a plain scalar VAR and for index-0 of a Bit-vector, contradicting #447's fix #467
Description
Summary
#447 ("Unsupported read-to-read connection" for vector elements >= 1 when the element width is a parameter) was closed as fixed, but the same error message still fires on DFHDL 0.22.0+63-477e737a-SNAPSHOT (newer build than #447's +25) for two shapes #447 explicitly said were unaffected:
- A plain scalar
Bit <> VAR(not a vector element at all). - Index 0 of a
Bit X N <> VAR(single-bit element vector) —#447states "indexv(0)is accepted" and "ABit X Nvector is also unaffected (only multi-bit elements carry a width)". Neither holds for the repro below.
So either the underlying defect is broader than #447's fix covered, or this is a related-but-distinct bug that happens to produce an identical message. Filing separately per the "verify before filing" rule, since I could not get this to reproduce via #447's own repro shape (parametric multi-bit element width) — mine reproduces with only Bit values, no vector-element width involved for the first case.
Minimal reproduction (verified against DFHDL 0.22.0+63-477e737a-SNAPSHOT, Scala 3.8.4)
// Child.scala
import dfhdl.*
class Child extends EDDesign:
val a = Bit <> IN
val y = Bit <> OUT
y <> a// Probe.scala
import dfhdl.*
class Probe extends EDDesign:
val a = Bit <> IN
val b = Bit <> OUT
val w = Bit <> VAR
val c = new Child
b <> w // read w (as producer) -- fine on its own
w <> c.y // drive w (as consumer, from child's output port)
c.a <> aThis 2-file, ~12-line case (isolated) actually compiles fine — I include it to show the minimal non-failing shape, since it rules out "any read-before-drive of a VAR" as the trigger and narrows the real trigger to a specific combination. The failing case needs the read side to be a bit-select of an ambiguous (non-port) Bits/Bit-vector VAR, established before its own whole-vector driver is written. Minimal failing repro:
// Kid.scala
import dfhdl.*
class Kid extends EDDesign:
val a = Bit <> IN
val y = Bit <> OUT
y <> a// Probe2.scala
import dfhdl.*
class Probe2 extends EDDesign:
val extIn = Bit <> IN
val extOut = Bits(3) <> OUT
val k_out_ready = Bits(3) <> VAR // plain multi-bit VAR, bit-selected below
val k_to_f_ready = Bit <> VAR // plain scalar VAR — NOT a vector element
val kid = new Kid
k_out_ready(2) <> extIn
k_out_ready(0) <> k_to_f_ready // read k_to_f_ready (producer) — BEFORE its own drive
k_to_f_ready <> kid.y // drive k_to_f_ready (consumer) from a child's output port
kid.a <> extIn
extOut <> k_out_readyDFiant HDL connectivity error!
Position: src/Probe2.scala:<line>:3 - <line>:<col>
Hierarchy: Probe2
LHS: k_to_f_ready
RHS: kid.y
Message: Unsupported read-to-read connection.
Swapping the order of the two k_to_f_ready connections does not fix it — the error simply moves to whichever of the two touches k_to_f_ready is written second:
k_to_f_ready <> kid.y // now first: drive
k_out_ready(0) <> k_to_f_ready // now second: read -- THIS one now errors insteadDirectly connecting k_out_ready(0) <> kid.y (skipping the k_to_f_ready intermediate entirely) also fails the same way, with k_out_ready(0) itself now named as the conflicting LHS. So the trigger is not specific to the intermediate scalar VAR either; it is some property of k_out_ready (a multi-bit VAR whose bits are driven individually from ambiguous sources and which is read as a whole elsewhere, e.g. childInput <> k_out_ready) combined with a bit-select connection sourced from something not yet resolved as a definite producer.
This exact shape was found translating Mahmoud-geberty/HOG_fpga's lin_buff.v (same upstream source #447 mentions) — the real, non-minimized failure:
DFiant HDL connectivity error!
Position: src/lin_buff.scala:123:3 - 123:37
Hierarchy: lin_buff
LHS: k_to_f_ready
RHS: first_line.w_ready
Message: Unsupported read-to-read connection.
DFiant HDL connectivity error!
Position: src/lin_buff.scala:140:5 - 140:41
Hierarchy: lin_buff
LHS: k_to_b_ready(0)
RHS: line_buff.w_ready
Message: Unsupported read-to-read connection.
Both k_to_f_ready (a plain scalar Bit <> VAR) and k_to_b_ready(0) (index 0, contradicting #447's "index 0 is accepted") are affected. first_line.w_ready / line_buff.w_ready are ordinary child-design Bit <> OUT ports, no parametric multi-bit width involved anywhere in this chain.
Expected
k_to_f_ready <> first_line.w_ready (a VAR driven once by a child's output port, elsewhere read to build another signal) should compile — this is the completely ordinary "one driver, one or more readers" wire pattern shown in the docs' own IODesign1 example (tmp <> i; o <> tmp), just with the driving and reading connections written in the opposite relative order.
Workaround used
Build the multi-bit consumer (k_out_ready in the real design) via elaboration-time bit concatenation (the docs' LaneConcat idiom, (x, acc).toBits) instead of per-bit <> connections on a VAR, placed textually after the child instances that drive the individual ready signals. This avoids the construct entirely. Full context in dfhdl_by_agents gap ticket (learner run hog-fpga-buffers-1) and dfhdl_training timeout ticket for the same module.
Environment
- DFHDL
0.22.0+63-477e737a-SNAPSHOT - Scala 3.8.4, JVM 21
- Backend: SystemVerilog (
-- commit)
Related
#447(closed) — same error message, but for a different trigger shape (parametric multi-bit element width, index >= 1) that this repro explicitly does not require.
Activity
ir+lib+compiler_stages: connection flow never defers to parameters (#467
soronpo commentedon Aug 12, 2026
Fixed in #477