blob: 79a3ab6d8d95befb0b7aa0a499fd1fb0eef20d0a [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// WARNING: DO NOT USE THIS MODULE IF YOU DO NOT HAVE A GOOD REASON TO DO SO.
//
// This module is only meant to be used in special cases where a handshake synchronizer
// is not viable (this is for instance the case for the multibit life cycle signals).
// For handshake-based synchronization, consider using prim_sync_reqack_data.
//
//
// Description:
//
// This module implements a multibit synchronizer that employs a data consistency check to
// decide whether the synchronized multibit signal is stable and can be output or not.
//
// The number of consistency checkers can be controlled via NumChecks. Each check adds another
// delay register after the 2-flop synchronizer, and corresponding comparator that checks whether
// the register input is equal to the output of the last register in the chain. If all checks are
// successful, the output register is enabled such that the data can propagate to the output.
//
// This is illustrated bellow for NumChecks = 1:
//
// /--------\ /--------\ /--------\
// | | | | | |
// data_i --/--> | flop | --x--> | flop | --x--> | flop | --/--> data_o
// Width | 2 sync | | | | | | |
// | | | | | | | en |
// \--------/ | \--------/ | \--------/
// | v ^
// | /----\ |
// \-------------> | == | ----/
// \----/
//
// Note: CDC tools will likely flag this module due to re-convergent logic.
//
`include "prim_assert.sv"
module prim_multibit_sync #(
// Width of the multibit signal.
parameter int Width = 8,
// Number of cycles the synchronized multi-bit signal needs to
// be stable until it is relased to the output. Each check adds
// a comparator and an additional delay register.
parameter int NumChecks = 1,
// Reset value of the multibit signal.
parameter logic [Width-1:0] ResetValue = '0
) (
input clk_i,
input rst_ni,
input logic [Width-1:0] data_i,
output logic [Width-1:0] data_o
);
`ASSERT_INIT(NumChecks_A, NumChecks >= 1)
// First, synchronize the input data to this clock domain.
logic [NumChecks:0][Width-1:0] data_check_d;
logic [NumChecks-1:0][Width-1:0] data_check_q;
prim_flop_2sync #(
.Width(Width),
.ResetValue(ResetValue)
) i_prim_flop_2sync (
.clk_i,
.rst_ni,
.d_i(data_i),
.q_o(data_check_d[0])
);
// Shift register.
assign data_check_d[NumChecks:1] = data_check_q[NumChecks-1:0];
// Consistency check. Always compare to the output of the last register.
logic [NumChecks-1:0] checks;
for (genvar k = 0; k < NumChecks; k++) begin : gen_checks
assign checks[k] = (data_check_d[k] == data_check_d[NumChecks]);
// Output is only allowed to change when all checks have passed.
`ASSERT(StableCheck_A,
data_o != $past(data_o)
|->
$past(data_check_d[k]) == $past(data_check_d[NumChecks]))
end : gen_checks
// Only propagate to output register if all checks have passed.
logic [Width-1:0] data_synced_d, data_synced_q;
assign data_synced_d = (&checks) ? data_check_d[NumChecks] : data_synced_q;
assign data_o = data_synced_q;
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
if (!rst_ni) begin
data_synced_q <= ResetValue;
data_check_q <= {NumChecks{ResetValue}};
end else begin
data_synced_q <= data_synced_d;
data_check_q <= data_check_d[NumChecks-1:0];
end
end
`ASSERT_KNOWN(DataKnown_A, data_o)
endmodule : prim_multibit_sync