[prim] Add a new slow to fast clock synchronizer

The new prom is designed to save area when synchronizing data values
from a slow clock to a much faster clock. The module samples the
negative edge of the slow clock and uses it as an enable for the data
registers in the fast domain.

Signed-off-by: Tom Roberts <tomroberts@lowrisc.org>
diff --git a/hw/ip/prim/prim.core b/hw/ip/prim/prim.core
index 855b4c4..18e8d72 100644
--- a/hw/ip/prim/prim.core
+++ b/hw/ip/prim/prim.core
@@ -35,6 +35,7 @@
       - rtl/prim_slicer.sv
       - rtl/prim_sync_reqack.sv
       - rtl/prim_sync_reqack_data.sv
+      - rtl/prim_sync_slow_fast.sv
       - rtl/prim_keccak.sv
       - rtl/prim_packer.sv
       - rtl/prim_packer_fifo.sv
diff --git a/hw/ip/prim/rtl/prim_sync_slow_fast.sv b/hw/ip/prim/rtl/prim_sync_slow_fast.sv
new file mode 100644
index 0000000..8bffa1d
--- /dev/null
+++ b/hw/ip/prim/rtl/prim_sync_slow_fast.sv
@@ -0,0 +1,59 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// Slow to fast clock synchronizer
+// This module is designed to be used for efficient sampling of signals from a slow clock to a much
+// faster clock.
+//
+// The data is captured into flops on the negative edge of the slow clock (when the data should be
+// stable). Because the slow clock passes through a two-flop synchronizer, the ratio of clock speeds
+// needs to be high to guarantee that the data will be stable when sampled.
+//
+// A ratio of at-least 10:1 in clock speeds is recommended.
+
+module prim_sync_slow_fast #(
+  parameter int unsigned Width = 32
+) (
+  input  logic             clk_slow_i,
+  input  logic             clk_fast_i,
+  input  logic             rst_fast_ni,
+  input  logic [Width-1:0] wdata_i,    // Slow domain
+  output logic [Width-1:0] rdata_o     // Fast domain
+);
+
+  logic             sync_clk_slow, sync_clk_slow_q;
+  logic             wdata_en;
+  logic [Width-1:0] wdata_q;
+
+  // Synchronize the slow clock to the fast domain
+  prim_flop_2sync #(.Width(1)) sync_slow_clk (
+    .clk_i    (clk_fast_i),
+    .rst_ni   (rst_fast_ni),
+    .d_i      (clk_slow_i),
+    .q_o      (sync_clk_slow));
+
+  // Register the synchronized clk
+  always_ff @(posedge clk_fast_i or negedge rst_fast_ni) begin
+    if (!rst_fast_ni) begin
+      sync_clk_slow_q <= 1'b0;
+    end else begin
+      sync_clk_slow_q <= sync_clk_slow;
+    end
+  end
+
+  // Find the negative edge of the synchronized slow clk
+  assign wdata_en = sync_clk_slow_q & !sync_clk_slow;
+
+  // Sample the slow data on the negative edge
+  always_ff @(posedge clk_fast_i or negedge rst_fast_ni) begin
+    if (!rst_fast_ni) begin
+      wdata_q <= '0;
+    end else if (wdata_en) begin
+      wdata_q <= wdata_i;
+    end
+  end
+
+  assign rdata_o = wdata_q;
+
+endmodule