Merge "Add Zero Forcing for for power saving"
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() }
+    }
+  }
+}