[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