diff --git a/src/main/scala/chisel3/util/Valid.scala b/src/main/scala/chisel3/util/Valid.scala index 39f26f0b890..7e1a751cf97 100644 --- a/src/main/scala/chisel3/util/Valid.scala +++ b/src/main/scala/chisel3/util/Valid.scala @@ -42,6 +42,18 @@ class Valid[+T <: Data](gen: T) extends Bundle { * Inserts the parameterized generator's typeName, e.g. Valid_UInt4 */ override def typeName = s"${simpleClassName(this.getClass)}_${gen.typeName}" + + /** Applies the supplied functor to the bits of this interface, returning a new typed Valid interface. + * @param f The function to apply to this Valid's 'bits' with return type B + * @return a new Valid of type B + */ + def map[B <: Data](f: T => B): Valid[B] = { + val _map_bits = f(bits) + val _map = Wire(Valid(chiselTypeOf(_map_bits))) + _map.bits := _map_bits + _map.valid := valid + _map.readOnly + } } /** Factory for generating "valid" interfaces. A "valid" interface is a data-communicating interface between a producer diff --git a/src/test/scala/chiselTests/ValidSpec.scala b/src/test/scala/chiselTests/ValidSpec.scala new file mode 100644 index 00000000000..42d1c16ee8b --- /dev/null +++ b/src/test/scala/chiselTests/ValidSpec.scala @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests + +import chisel3._ +import circt.stage.ChiselStage +import chisel3.util.Valid + +class ValidSpec extends ChiselFlatSpec { + "Valid.map" should "apply a function to the wrapped Data" in { + val chirrtl = ChiselStage.emitCHIRRTL(new Module { + val in = IO(Flipped(Valid(UInt(8.W)))) + val out = IO(Valid(UInt(8.W))) + out :#= in.map(_ + 1.U) + }) + + // Check for data assignment + chirrtl should include("""node _out_map_bits_T = add(in.bits, UInt<1>(0h1))""") + chirrtl should include("""node _out_map_bits = tail(_out_map_bits_T, 1)""") + chirrtl should include("""connect _out_map.bits, _out_map_bits""") + chirrtl should include("""connect out.bits, _out_map.bits""") + + // Check for valid assignment + chirrtl should include("""connect _out_map.valid, in.valid""") + chirrtl should include("""connect out.valid, _out_map.valid""") + } + + "Valid.map" should "apply a function to a wrapped Bundle" in { + class TestBundle extends Bundle { + val foo = UInt(8.W) + val bar = UInt(8.W) + val fizz = Bool() + val buzz = Bool() + } + + // Add one to foo, subtract one from bar, set fizz to false and buzz to true + def func(t: TestBundle): TestBundle = { + val res = Wire(new TestBundle) + + res.foo := t.foo + 1.U + res.bar := t.bar - 1.U + res.fizz := false.B + res.buzz := true.B + + res + } + + val chirrtl = ChiselStage + .emitCHIRRTL(new Module { + val in = IO(Flipped(Valid(new TestBundle))) + val out = IO(Valid(new TestBundle)) + out :#= in.map(func) + }) + + // Check for data assignment + chirrtl should include("""wire _out_map_bits : { foo : UInt<8>, bar : UInt<8>, fizz : UInt<1>, buzz : UInt<1>}""") + + chirrtl should include("""node _out_map_bits_res_foo_T = add(in.bits.foo, UInt<1>(0h1)""") + chirrtl should include("""node _out_map_bits_res_foo_T_1 = tail(_out_map_bits_res_foo_T, 1)""") + chirrtl should include("""connect _out_map_bits.foo, _out_map_bits_res_foo_T_1""") + + chirrtl should include("""node _out_map_bits_res_bar_T = sub(in.bits.bar, UInt<1>(0h1)""") + chirrtl should include("""node _out_map_bits_res_bar_T_1 = tail(_out_map_bits_res_bar_T, 1)""") + chirrtl should include("""connect _out_map_bits.bar, _out_map_bits_res_bar_T_1""") + + chirrtl should include("""connect _out_map_bits.fizz, UInt<1>(0h0)""") + chirrtl should include("""connect _out_map_bits.buzz, UInt<1>(0h1)""") + + chirrtl should include("""connect _out_map.bits, _out_map_bits""") + for ((field, _) <- (new TestBundle).elements) { + chirrtl should include(s"""connect out.bits.$field, _out_map.bits.$field""") + } + + // Check for valid assignment + chirrtl should include("""connect _out_map.valid, in.valid""") + chirrtl should include("""connect out.valid, _out_map.valid""") + } + + "Valid.map" should "apply a function to a wrapped Bundle and return a different typed Valid" in { + class TestBundle extends Bundle { + val foo = UInt(8.W) + val bar = UInt(8.W) + } + + val chirrtl = ChiselStage + .emitCHIRRTL(new Module { + val in = IO(Flipped(Valid(new TestBundle))) + val out = IO(Valid(UInt(8.W))) + out :#= in.map(bundle => bundle.foo & bundle.bar) + }) + + // Check that the _map wire wraps a UInt and not a TestBundle + chirrtl should include("""wire _out_map : { valid : UInt<1>, bits : UInt<8>}""") + + // Check for data assignment + chirrtl should include("""node _out_map_bits = and(in.bits.foo, in.bits.bar)""") + chirrtl should include("""connect _out_map.bits, _out_map_bits""") + chirrtl should include("""connect out.bits, _out_map.bits""") + + // Check for valid assignment + chirrtl should include("""connect _out_map.valid, in.valid""") + chirrtl should include("""connect out.valid, _out_map.valid""") + } +}