Summary
Calling the width-inferring, argument-less form of .resize on a sub-design instance's OUT port aborts elaboration with an internal java.util.NoSuchElementException instead of either working or producing a diagnostic. The same expression with an explicit width (.resize(N)) works, and the same argument-less form on a locally declared VAR works, so the failure is specific to the combination.
Minimal reproduction
Two files. Child.scala:
import dfhdl.*
class Child(
val A: Int <> CONST = 3,
val B: Int <> CONST = 8
) extends EDDesign:
val OUT_W = A * B
val o = Bits(OUT_W) <> OUT
o <> all(0)
end Child
Probe.scala:
import dfhdl.*
class Probe extends EDDesign:
val p = Bits(16) <> OUT
val c = new Child()
p <> c.o.resize
end Probe
Run with:
scala run src/ --scala 3.8.4 \
--dep "io.github.dfianthdl::dfhdl::$V" \
--compiler-plugin "io.github.dfianthdl:::dfhdl-plugin::$V" \
-O -deprecation -M Probe -- commit
Scala compilation succeeds; the failure is at elaboration.
Actual behavior
Exception in thread "main" java.util.NoSuchElementException: key not found: Dcl(DFBits("TR_b2c48732_0_30"),OUT,List(),"OW_b2c48732_0_31",Meta(Some(o),src/Child.scala:7:11 - 7:29,None,List()),Map())
at scala.collection.MapOps.default(Map.scala:310)
at scala.collection.MapOps.default$(Map.scala:104)
at scala.collection.AbstractMap.default(Map.scala:464)
at scala.collection.mutable.HashMap.apply(HashMap.scala:446)
at dfhdl.core.DesignContext.setMember(MutableDB.scala:165)
at dfhdl.core.MutableDB.setMember(MutableDB.scala:977)
at dfhdl.core.MutableDB$getSet$.set(MutableDB.scala:1236)
at dfhdl.compiler.ir.DFMember.setTags(DFMember.scala:22)
at dfhdl.compiler.ir.DFMember.setTags$(DFMember.scala:10)
at dfhdl.compiler.ir.DFVal$Dcl.setTags(DFMember.scala:727)
at dfhdl.core.DFVal$.tag(DFVal.scala:442)
at dfhdl.core.DFBits$package$DFBits$Val$Ops$.resize(DFBits.scala:746)
at dfhdl.hdl$.resize(hdl.scala:7)
at dfhdl.hdl$package$.resize(hdl.scala:120)
at Probe.<init>(Probe.scala:5)
at Probe$$anon$3.<init>(Probe.scala:2)
at Probe$.mkDsn$1(Probe.scala:2)
at Probe$.main$$anonfun$1(Probe.scala:2)
at dfhdl.internals.DiskCache$Step.task$lzyINIT1$$anonfun$4(DiskCache.scala:129)
at factum.Task.cached$$anonfun$1(Task.scala:51)
at factum.Evaluator$Run.compute(Evaluator.scala:199)
(trace truncated at the factum.Evaluator frame)
Note the Meta in the missing key points at the child's declaration site (src/Child.scala:7, i.e. val o = Bits(OUT_W) <> OUT), while setMember is being called in the parent's DesignContext. DFVal.tag is trying to re-tag a member that does not belong to the design context it is being set in.
Expected behavior
Either the argument-less .resize infers the target width from the connection context (as it does for a local VAR), or it reports a normal DFHDL elaboration error. An internal NoSuchElementException with a raw IR Dcl(...) dump gives the user nothing to act on: there is no source position for the offending line in the message itself, only inside the stack frames, and the printed key is compiler-internal representation rather than the code that was written.
Boundary of the bug (each verified by running it)
| Variant |
Result |
p <> c.o.resize (child instance's OUT port, no argument) |
crash |
p <> c.o.resize(16) (child instance's OUT port, explicit width) |
works |
p <> v.resize where val v = Bits(24) <> VAR (local VAR, no argument) |
works |
crash variant run with --nocache |
crash (unchanged) |
| child made hierarchical (child instantiates its own sub-design) |
not required; a leaf child reproduces it |
The --nocache row matters because the missing key's name carries a digest prefix (TR_b2c48732_0_30), which initially looked like a sub-design elaboration cache interaction. It is not: the crash reproduces identically with caching disabled.
How it was hit in practice
Translating Mahmoud-geberty/HOG_fpga's norm_block.v (learner run hog-fpga-hist-1). The line was:
block_histograms <> cell_line_buffer.kernel.resize
where cell_line_buffer is a lin_buff instance and kernel is its Bits(OUTPUT_WIDTH) <> OUT port. Original trace, same signature:
Exception in thread "main" java.util.NoSuchElementException: key not found: Dcl(DFBits("TR_665f36f3_0_76"),OUT,List(),"OW_665f36f3_0_77",Meta(Some(kernel),src/lin_buff.scala:30:16 - 30:41,None,List()),Map())
at dfhdl.core.DesignContext.setMember(MutableDB.scala:165)
at dfhdl.compiler.ir.DFVal$Dcl.setTags(DFMember.scala:727)
at dfhdl.core.DFBits$package$DFBits$Val$Ops$.resize(DFBits.scala:746)
at norm_block.<init>(norm_block.scala:46)
Worth noting how the user got here: the previous attempt's own error message recommended .resize. That attempt failed with a pair of elaboration errors on the same line, reported twice with the operand roles swapped:
DFiant HDL elaboration error!
Position: src/norm_block.scala:46:3 - 46:46
Message: The argument width (cell_line_buffer.OUTPUT_WIDTH) is different than the receiver width (BLOCK_HIST_WIDTH).
Consider applying `.resize` to resolve this issue.
DFiant HDL elaboration error!
Position: src/norm_block.scala:46:3 - 46:46
Message: The argument width (BLOCK_HIST_WIDTH) is different than the receiver width (cell_line_buffer.OUTPUT_WIDTH).
Consider applying `.resize` to resolve this issue.
So the documented remedy for the width mismatch is what triggers the crash on this operand kind. That pairing is what makes this worth prioritizing over an ordinary internal-error report.
Environment
- DFHDL:
v0.22.0+63-477e737a-SNAPSHOT
- Scala: 3.8.4, JVM 21
- Yosys (unrelated to the crash, listed for run context):
0.68+40 (git sha1 0f2bcb94b-dirty)
Summary
Calling the width-inferring, argument-less form of
.resizeon a sub-design instance'sOUTport aborts elaboration with an internaljava.util.NoSuchElementExceptioninstead of either working or producing a diagnostic. The same expression with an explicit width (.resize(N)) works, and the same argument-less form on a locally declaredVARworks, so the failure is specific to the combination.Minimal reproduction
Two files.
Child.scala:Probe.scala:Run with:
Scala compilation succeeds; the failure is at elaboration.
Actual behavior
(trace truncated at the
factum.Evaluatorframe)Note the
Metain the missing key points at the child's declaration site (src/Child.scala:7, i.e.val o = Bits(OUT_W) <> OUT), whilesetMemberis being called in the parent'sDesignContext.DFVal.tagis trying to re-tag a member that does not belong to the design context it is being set in.Expected behavior
Either the argument-less
.resizeinfers the target width from the connection context (as it does for a localVAR), or it reports a normal DFHDL elaboration error. An internalNoSuchElementExceptionwith a raw IRDcl(...)dump gives the user nothing to act on: there is no source position for the offending line in the message itself, only inside the stack frames, and the printed key is compiler-internal representation rather than the code that was written.Boundary of the bug (each verified by running it)
p <> c.o.resize(child instance's OUT port, no argument)p <> c.o.resize(16)(child instance's OUT port, explicit width)p <> v.resizewhereval v = Bits(24) <> VAR(local VAR, no argument)--nocacheThe
--nocacherow matters because the missing key's name carries a digest prefix (TR_b2c48732_0_30), which initially looked like a sub-design elaboration cache interaction. It is not: the crash reproduces identically with caching disabled.How it was hit in practice
Translating
Mahmoud-geberty/HOG_fpga'snorm_block.v(learner runhog-fpga-hist-1). The line was:block_histograms <> cell_line_buffer.kernel.resizewhere
cell_line_bufferis alin_buffinstance andkernelis itsBits(OUTPUT_WIDTH) <> OUTport. Original trace, same signature:Worth noting how the user got here: the previous attempt's own error message recommended
.resize. That attempt failed with a pair of elaboration errors on the same line, reported twice with the operand roles swapped:So the documented remedy for the width mismatch is what triggers the crash on this operand kind. That pairing is what makes this worth prioritizing over an ordinary internal-error report.
Environment
v0.22.0+63-477e737a-SNAPSHOT0.68+40 (git sha1 0f2bcb94b-dirty)