Add Zero Forcing for for power saving

Gate the bits of an interface based on it's validity bit. This prevents
invalid data from propagating down stream, thus reducing dynamic
power.

Change-Id: I5080fffc58602c00d37cd22165c3ad122c527a20
diff --git a/hdl/chisel/src/common/BUILD b/hdl/chisel/src/common/BUILD
index 78e3391..6b6147a 100644
--- a/hdl/chisel/src/common/BUILD
+++ b/hdl/chisel/src/common/BUILD
@@ -12,8 +12,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-load("@kelvin_hw//rules:chisel.bzl",
-     "chisel_cc_library", "chisel_library", "chisel_test")
+load(
+    "@kelvin_hw//rules:chisel.bzl",
+    "chisel_cc_library",
+    "chisel_library",
+    "chisel_test",
+)
 
 chisel_library(
     name = "library",
@@ -26,21 +30,21 @@
 chisel_library(
     name = "common",
     srcs = [
-        "FifoXe.scala",
-        "FifoX.scala",
-        "FifoIxO.scala",
         "Fifo.scala",
+        "FifoIxO.scala",
+        "FifoX.scala",
+        "FifoXe.scala",
         "IDiv.scala",
         "MathUtil.scala",
         "Slice.scala",
     ],
-    deps = [
-        ":library",
-    ],
+    visibility = ["//visibility:public"],
     exports = [
         ":library",
     ],
-    visibility = ["//visibility:public"],
+    deps = [
+        ":library",
+    ],
 )
 
 chisel_library(
@@ -48,10 +52,10 @@
     srcs = [
         "Fp.scala",
     ],
+    visibility = ["//visibility:public"],
     deps = [
         ":library",
     ],
-    visibility = ["//visibility:public"],
 )
 
 chisel_library(
@@ -59,11 +63,11 @@
     srcs = [
         "FpTestUtils.scala",
     ],
-    deps = [
-        "@edu_berkeley_cs_chiseltest//jar",
-        ":fp",
-    ],
     visibility = ["//visibility:public"],
+    deps = [
+        ":fp",
+        "@edu_berkeley_cs_chiseltest//jar",
+    ],
 )
 
 chisel_test(
@@ -77,16 +81,27 @@
     ],
 )
 
+chisel_test(
+    name = "library_test",
+    srcs = [
+        "LibraryTest.scala",
+    ],
+    deps = [
+        ":library",
+        # ":fp_test_utils",
+    ],
+)
+
 chisel_library(
     name = "fma",
     srcs = [
         "Fma.scala",
     ],
+    visibility = ["//visibility:public"],
     deps = [
         ":common",
         ":fp",
     ],
-    visibility = ["//visibility:public"],
 )
 
 chisel_test(
@@ -95,7 +110,7 @@
         "FmaTest.scala",
     ],
     deps = [
-        ":fp",
         ":fma",
+        ":fp",
     ],
 )
diff --git a/hdl/chisel/src/common/Library.scala b/hdl/chisel/src/common/Library.scala
index 381519f..5e31771 100644
--- a/hdl/chisel/src/common/Library.scala
+++ b/hdl/chisel/src/common/Library.scala
@@ -36,8 +36,19 @@
   }
 }
 
+// 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
+  }
+}
+
 object Clz {
   def apply(bits: UInt): UInt = {
     PriorityEncoder(Cat(1.U(1.W), Reverse(bits)))
   }
-}
\ No newline at end of file
+}
diff --git a/hdl/chisel/src/common/LibraryTest.scala b/hdl/chisel/src/common/LibraryTest.scala
new file mode 100644
index 0000000..346e831
--- /dev/null
+++ b/hdl/chisel/src/common/LibraryTest.scala
@@ -0,0 +1,50 @@
+// Copyright 2024 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package common
+
+import chisel3._
+import chisel3.util._
+import chiseltest._
+import org.scalatest.freespec.AnyFreeSpec
+import chisel3.experimental.BundleLiterals._
+
+class ForceZeroTester extends Module {
+  val io = IO(new Bundle {
+    val in  = Input(Valid(SInt(32.W)))
+    val out = Output(Valid(SInt(32.W)))
+  })
+
+  io.out := ForceZero(io.in)
+}
+
+class LibrarySpec extends AnyFreeSpec with ChiselScalatestTester {
+  "ForceZero when invalid" in {
+    test(new ForceZeroTester) { dut =>
+      dut.io.in.bits.poke(9001)
+      dut.io.in.valid.poke(0)
+      dut.clock.step()
+      assertResult(0) { dut.io.out.bits.peekInt() }
+    }
+  }
+
+  "ForceZeroForceZero propogates when valid" in {
+    test(new ForceZeroTester) { dut =>
+      dut.io.in.bits.poke(9001)
+      dut.io.in.valid.poke(1)
+      dut.clock.step()
+      assertResult(9001) { dut.io.out.bits.peekInt() }
+    }
+  }
+}