Add MakeWireBundle

This allows Bundle (wire) instances to be created and (partially)
connected in one call, and provides some readability improvements:
- avoid repeating the name of the instance
- avoid naming the instance if we're just going to return it

This change applies the trick to common/Library and rvv only.

Change-Id: I56a7f220f6de63920d55227e23e5f7955b2d4b33
diff --git a/hdl/chisel/src/common/Library.scala b/hdl/chisel/src/common/Library.scala
index 4824074..5f1123f 100644
--- a/hdl/chisel/src/common/Library.scala
+++ b/hdl/chisel/src/common/Library.scala
@@ -27,12 +27,36 @@
   }
 }
 
+// An easy way to build a Wire(T) and (partially) connect some of its
+// contents.
+// This is essentially somewhere between WireDefault (which does not like
+// bundle members) and bundle literals (which only likes literals)
+// See MakeValid for usage example.
+// TODO: add more examples for advanced usage:
+// - _.member1.member2 on LHS
+// - _.something on RHS (only, or both)
+// - _ (no member) on LHS
+// TODO: add tests
+object MakeWireBundle {
+  def apply[T <: Bundle](gen: T, exp: T => (Data, Data)*): T = {
+    val ret = Wire(gen)
+    exp.foreach { e =>
+      val (x, y) = e(ret)
+      x := y
+    }
+    ret
+  }
+}
+
 object MakeValid {
   def apply[T <: Data](valid: Bool, bits: T): ValidIO[T] = {
-    val result = Wire(Valid(chiselTypeOf(bits)))
-    result.valid := valid
-    result.bits := bits
-    result
+    // Explicitly stating the type here allows the function types to be
+    // inferred.
+    MakeWireBundle[ValidIO[T]](
+      Valid(chiselTypeOf(bits)),
+      _.valid -> valid,
+      _.bits -> bits,
+    )
   }
 
   def apply[T <: Data](bits: T): ValidIO[T] = {
@@ -41,23 +65,21 @@
 }
 
 object MakeInvalid {
-  def apply[T <: Data](gen: T): ValidIO[T] = {
-    val result = Wire(Valid(gen))
-      result.valid := false.B
-      result.bits := 0.U.asTypeOf(gen)
-      result
-  }
+  def apply[T <: Data](gen: T): ValidIO[T] = MakeWireBundle[ValidIO[T]](
+    Valid(gen),
+    _.valid -> false.B,
+    _.bits -> 0.U.asTypeOf(gen),
+  )
 }
 
 // Gate the bits of an interface based on it's validity bit. This prevents
 // invalid data from propagating down stream, thus reducing dynamic power
 object ForceZero {
-  def apply[T <: Data](input: ValidIO[T]): ValidIO[T] = {
-    val result = Wire(chiselTypeOf(input))
-    result.valid := input.valid
-    result.bits  := Mux(input.valid, input.bits, 0.U.asTypeOf(input).bits)
-    result
-  }
+  def apply[T <: Data](input: ValidIO[T]): ValidIO[T] = MakeWireBundle[ValidIO[T]](
+    chiselTypeOf(input),
+    _.valid -> input.valid,
+    _.bits  -> Mux(input.valid, input.bits, 0.U.asTypeOf(input).bits),
+  )
 }
 
 object Clz {
diff --git a/hdl/chisel/src/kelvin/rvv/RvvDecode.scala b/hdl/chisel/src/kelvin/rvv/RvvDecode.scala
index d42c96b..f557aff 100644
--- a/hdl/chisel/src/kelvin/rvv/RvvDecode.scala
+++ b/hdl/chisel/src/kelvin/rvv/RvvDecode.scala
@@ -16,7 +16,7 @@
 
 import chisel3._
 import chisel3.util._
-import common.{ForceZero, MakeInvalid, MakeValid}
+import common.{ForceZero, MakeInvalid, MakeValid, MakeWireBundle}
 
 
 object RvvCompressedOpcode extends ChiselEnum {
@@ -41,10 +41,14 @@
       "b0100111".U -> MakeValid(RvvCompressedOpcode.RVVSTORE),
       "b1010111".U -> MakeValid(RvvCompressedOpcode.RVVALU),
     ))
-    val compressed = Wire(new RvvCompressedInstruction)
-    compressed.opcode := new_opcode.bits
-    compressed.bits := bits
-    MakeValid(new_opcode.valid, compressed)
+
+    // Fancy way to MakeValid.
+    MakeWireBundle[ValidIO[RvvCompressedInstruction]](
+      Valid(new RvvCompressedInstruction),
+      _.valid -> new_opcode.valid,
+      _.bits.opcode -> new_opcode.bits,
+      _.bits.bits -> bits,
+    )
   }
 }
 
@@ -104,10 +108,11 @@
       (f6vm === BitPat("b101111_?")) -> MakeValid(RvvAluOp.VNCLIP),
     ))
 
-    val d = Wire(new RvvS1DecodedInstruction())
-    d.op := op.bits
-
-    ForceZero(MakeValid(op.valid, d))
+    ForceZero(MakeWireBundle[ValidIO[RvvS1DecodedInstruction]](
+      Valid(new RvvS1DecodedInstruction),
+      _.valid -> op.valid,
+      _.bits.op -> op.bits,
+    ))
   }
 
   private def s1decode_opivx(f6vm: UInt, vs2: UInt, rs1: UInt, vd: UInt): Valid[RvvS1DecodedInstruction] = {
@@ -161,10 +166,11 @@
       (f6vm === BitPat("b101111_?")) -> MakeValid(RvvAluOp.VNCLIP),
     ))
 
-    val d = Wire(new RvvS1DecodedInstruction())
-    d.op := op.bits
-
-    ForceZero(MakeValid(op.valid, d))
+    ForceZero(MakeWireBundle[ValidIO[RvvS1DecodedInstruction]](
+      Valid(new RvvS1DecodedInstruction),
+      _.valid -> op.valid,
+      _.bits.op -> op.bits,
+    ))
   }
 
   private def s1decode_opivi(f6vm: UInt, vs2: UInt, imm5: UInt, vd: UInt): Valid[RvvS1DecodedInstruction] = {
@@ -222,10 +228,11 @@
       (f6vm === BitPat("b101111_?")) -> MakeValid(RvvAluOp.VNCLIP),
     ))
 
-    val d = Wire(new RvvS1DecodedInstruction())
-    d.op := op.bits
-
-    ForceZero(MakeValid(op.valid, d))
+    ForceZero(MakeWireBundle[ValidIO[RvvS1DecodedInstruction]](
+      Valid(new RvvS1DecodedInstruction),
+      _.valid -> op.valid,
+      _.bits.op -> op.bits,
+    ))
   }
 
   protected def s1decode_opv(bits: UInt): Valid[RvvS1DecodedInstruction] = {