blob: 840d86d8c38ad9ea0a81dac82970b75d1d5a8d4a [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// REQ/ACK synchronizer with associated data.
//
// This module synchronizes a REQ/ACK handshake with associated data across a clock domain
// crossing (CDC). Both domains will see a handshake with the duration of one clock cycle. By
// default, the data itself is not registered. The main purpose of feeding the data through this
// module to have an anchor point for waiving CDC violations. If the data is configured to flow
// from the destination (DST) to the source (SRC) domain, an additional register stage can be
// inserted for data buffering.
//
// Under the hood, this module uses a prim_sync_reqack primitive for synchronizing the
// REQ/ACK handshake. See prim_sync_reqack.sv for more details.
`include "prim_assert.sv"
module prim_sync_reqack_data #(
parameter int unsigned Width = 1,
parameter bit DataSrc2Dst = 1'b1, // Direction of data flow: 1'b1 = SRC to DST,
// 1'b0 = DST to SRC
parameter bit DataReg = 1'b0 // Enable optional register stage for data,
// only usable with DataSrc2Dst == 1'b0.
) (
input clk_src_i, // REQ side, SRC domain
input rst_src_ni, // REQ side, SRC domain
input clk_dst_i, // ACK side, DST domain
input rst_dst_ni, // ACK side, DST domain
input logic req_chk_i, // Used for gating assertions. Drive to 1 during normal operation.
input logic src_req_i, // REQ side, SRC domain
output logic src_ack_o, // REQ side, SRC domain
output logic dst_req_o, // ACK side, DST domain
input logic dst_ack_i, // ACK side, DST domain
input logic [Width-1:0] data_i,
output logic [Width-1:0] data_o
);
////////////////////////////////////
// REQ/ACK synchronizer primitive //
////////////////////////////////////
prim_sync_reqack u_prim_sync_reqack (
.clk_src_i,
.rst_src_ni,
.clk_dst_i,
.rst_dst_ni,
.req_chk_i,
.src_req_i,
.src_ack_o,
.dst_req_o,
.dst_ack_i
);
/////////////////////////
// Data register stage //
/////////////////////////
// Optional - Only relevant if the data flows from DST to SRC. In this case, it must be ensured
// that the data remains stable until the ACK becomes visible in the SRC domain.
//
// Note that for larger data widths, it is recommended to adjust the data sender to hold the data
// stable until the next REQ in order to save the cost of this register stage.
if (DataSrc2Dst == 1'b0 && DataReg == 1'b1) begin : gen_data_reg
logic data_we;
logic [Width-1:0] data_d, data_q;
// Sample the data when seing the REQ/ACK handshake in the DST domain.
assign data_we = dst_req_o & dst_ack_i;
assign data_d = data_i;
always_ff @(posedge clk_dst_i or negedge rst_dst_ni) begin
if (!rst_dst_ni) begin
data_q <= '0;
end else if (data_we) begin
data_q <= data_d;
end
end
assign data_o = data_q;
end else begin : gen_no_data_reg
// Just feed through the data.
assign data_o = data_i;
end
////////////////
// Assertions //
////////////////
if (DataSrc2Dst == 1'b1) begin : gen_assert_data_src2dst
// SRC domain cannot change data while waiting for ACK.
`ASSERT(SyncReqAckDataHoldSrc2Dst, !$stable(data_i) |->
(!src_req_i || (src_req_i && src_ack_o)),
clk_src_i, !rst_src_ni)
// Register stage cannot be used.
`ASSERT_INIT(SyncReqAckDataReg, DataSrc2Dst && !DataReg)
end else if (DataSrc2Dst == 1'b0 && DataReg == 1'b0) begin : gen_assert_data_dst2src
// DST domain shall not change data while waiting for SRC domain to latch it (together with
// receiving ACK). It takes 2 SRC cycles for ACK to cross over from DST to SRC, and 1 SRC cycle
// for the next REQ to cross over from SRC to DST.
//
// Denote the src clock where REQ & ACK as time zero. The data flowing through the CDC could be
// corrupted if data_o was not stable over the previous 2 clock cycles (so we want to check time
// points -2, -1 and 0). Moreover, the DST domain cannot know that it is allowed to change value
// until at least one more SRC cycle (the time taken for REQ to cross back from SRC to DST).
//
// To make this assertion, we will sample at each of 4 time points (-2, -1, 0 and +1), asserting
// that data_o is equal at each of these times. Note this won't detect glitches at intermediate
// timepoints.
//
// The SVAs below are designed not to consume time, which means that they can be disabled with
// an $assertoff(..) and won't linger to fail later. This wouldn't work properly if we used
// something like |=> instead of the $past(...) function. That means that we have to trigger at
// the "end" of the window. To make sure we don't miss a situation where the value changed at
// time -1 (causing corruption), but reset was asserted between time 0 and 1, we split the
// assertion into two pieces. The first (SyncReqAckDataHoldDst2SrcA) checks that data doesn't
// change in a way that could cause data corruption. The second (SyncReqAckDataHoldDst2SrcB)
// checks that the DST side doesn't do anything that it shouldn't know is safe.
`ASSERT(SyncReqAckDataHoldDst2SrcA,
src_req_i && src_ack_o |-> $past(data_o, 2) == data_o && $past(data_o) == data_o,
clk_src_i, !rst_src_ni)
`ASSERT(SyncReqAckDataHoldDst2SrcB,
$past(src_req_i && src_ack_o) |-> $past(data_o) == data_o,
clk_src_i, !rst_src_ni)
end
endmodule