diff --git a/cdc/rtl/BUILD.bazel b/cdc/rtl/BUILD.bazel index 5a371e8bd..c012ae3e6 100644 --- a/cdc/rtl/BUILD.bazel +++ b/cdc/rtl/BUILD.bazel @@ -315,3 +315,73 @@ br_verilog_elab_and_lint_test_suite( "//gate/rtl:br_gate_mock", ], ) + +verilog_library( + name = "br_cdc_stable_data", + srcs = ["br_cdc_stable_data.sv"], + deps = [ + ":br_cdc_reg", + "//macros:br_asserts_internal", + "//macros:br_registers", + ], +) + +br_verilog_elab_and_lint_test_suite( + name = "br_cdc_stable_data_test_suite", + params = { + "Width": [ + "8", + ], + "InitValue": [ + "0", + "1", + ], + "RegisterResetActive": [ + "0", + "1", + ], + "NumSyncStages": [ + "2", + "3", + ], + }, + top = "br_cdc_stable_data", + deps = [ + ":br_cdc_stable_data", + "//gate/rtl:br_gate_mock", + ], +) + +verilog_library( + name = "br_cdc_stable_data_autoupdate", + srcs = ["br_cdc_stable_data_autoupdate.sv"], + deps = [ + ":br_cdc_stable_data", + "//macros:br_asserts_internal", + "//macros:br_registers", + ], +) + +br_verilog_elab_and_lint_test_suite( + name = "br_cdc_stable_data_autoupdate_test_suite", + params = { + "Width": ["8"], + "InitValue": [ + "0", + "1", + ], + "RegisterResetActive": [ + "0", + "1", + ], + "NumSyncStages": [ + "2", + "3", + ], + }, + top = "br_cdc_stable_data_autoupdate", + deps = [ + ":br_cdc_stable_data_autoupdate", + "//gate/rtl:br_gate_mock", + ], +) diff --git a/cdc/rtl/br_cdc_stable_data.sv b/cdc/rtl/br_cdc_stable_data.sv new file mode 100644 index 000000000..e13e4f7a6 --- /dev/null +++ b/cdc/rtl/br_cdc_stable_data.sv @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Bedrock-RTL Stable Data CDC +// +// This is a thin wrapper around br_cdc_reg for synchronizing an infrequently +// changing multi-bit value. The register updates with the value of src_data +// when src_valid is asserted and holds the value on dst_data until the next time +// src_valid is asserted. +// + +`include "br_asserts_internal.svh" +`include "br_registers.svh" +`include "br_unused.svh" + +module br_cdc_stable_data #( + parameter int Width = 1, // Must be at least 1 + // The initial value of the destination-side register. + // dst_data will hold this value until the first time src_valid=1 + parameter logic [Width-1:0] InitValue = '0, + // If 1 (the default), register push_rst on push_clk and pop_rst on pop_clk + // before sending to the CDC synchronizers. This adds one cycle to the cut-through + // latency and one cycle to the backpressure latency. + // Do not set this to 0 unless push_rst and pop_rst are driven directly by + // registers. + parameter bit RegisterResetActive = 1, + // Number of synchronization stages to use. Must be at least 1. + // WARNING: Setting this parameter correctly is critical to + // ensuring a low probability of metastability. + // The recommended value is 3 for most technology nodes. + // Do not decrease below that unless you have a good reason. + parameter int NumSyncStages = 3 +) ( + input logic src_clk, + input logic src_rst, + input logic src_valid, + input logic [Width-1:0] src_data, + + input logic dst_clk, + input logic dst_rst, + output logic dst_updated, + output logic [Width-1:0] dst_data +); + // Integration Checks + // Only used for assertion + logic src_ready; + + // Rely on submodules for static parameter checks + + `BR_ASSERT_CR_INTG(no_reg_overflow_A, src_valid |-> src_ready, src_clk, src_rst) + + // Implementation + logic dst_updated_next; + logic [Width-1:0] dst_data_next; + + br_cdc_reg #( + .Width(Width), + .RegisterResetActive(RegisterResetActive), + .NumSyncStages(NumSyncStages), + // There shouldn't be push backpressure + .EnableCoverPushBackpressure(0) + ) br_cdc_reg_inst ( + .push_clk (src_clk), // ri lint_check_waive SAME_CLOCK_NAME + .push_rst (src_rst), + .push_valid(src_valid), + .push_ready(src_ready), + .push_data (src_data), + + .pop_clk(dst_clk), // ri lint_check_waive SAME_CLOCK_NAME + .pop_rst(dst_rst), + .pop_ready(1'b1), + .pop_valid(dst_updated_next), + .pop_data(dst_data_next) + ); + + `BR_REGX(dst_updated, dst_updated_next, dst_clk, dst_rst) + `BR_REGLIX(dst_data, dst_data_next, dst_updated_next, InitValue, dst_clk, dst_rst) + `BR_UNUSED(src_ready) + + // Implementation checks + // TODO(zhemao): Add some here + +endmodule diff --git a/cdc/rtl/br_cdc_stable_data_autoupdate.sv b/cdc/rtl/br_cdc_stable_data_autoupdate.sv new file mode 100644 index 000000000..3388c3f0a --- /dev/null +++ b/cdc/rtl/br_cdc_stable_data_autoupdate.sv @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Bedrock-RTL Stable Data CDC with Automatic Update +// +// This is a thin wrapper around br_cdc_stable_data that generates the src_valid signal +// automatically based on whether the src_data signal has been changed. +// + +`include "br_asserts_internal.svh" +`include "br_registers.svh" + +module br_cdc_stable_data_autoupdate #( + parameter int Width = 1, + parameter logic [Width-1:0] InitValue = '0, + parameter bit RegisterResetActive = 1, + parameter int NumSyncStages = 3 +) ( + input logic src_clk, + input logic src_rst, + input logic [Width-1:0] src_data, + + input logic dst_clk, + input logic dst_rst, + output logic dst_updated, + output logic [Width-1:0] dst_data +); + // Integration Checks + // Rely on checks in br_cdc_stable_data submodule + + // Implementation + logic src_valid; + logic [Width-1:0] src_data_reg; + + `BR_REGLIX(src_data_reg, src_data, src_valid, InitValue, src_clk, src_rst) + + assign src_valid = src_data != src_data_reg; + + br_cdc_stable_data #( + .Width(Width), + .InitValue(InitValue), + .RegisterResetActive(RegisterResetActive), + .NumSyncStages(NumSyncStages) + ) br_cdc_stable_data_inst ( + .src_clk, + .src_rst, + .src_valid, + .src_data, + .dst_clk, + .dst_rst, + .dst_updated, + .dst_data + ); + + // Implementation checks + // Rely on checks in br_cdc_stable_data submodule + +endmodule : br_cdc_stable_data_autoupdate diff --git a/cdc/sim/BUILD.bazel b/cdc/sim/BUILD.bazel index dc7f542a2..9d3bd6a73 100644 --- a/cdc/sim/BUILD.bazel +++ b/cdc/sim/BUILD.bazel @@ -253,3 +253,107 @@ br_verilog_sim_test_tools_suite( "//gate/rtl:br_gate_mock", ], ) + +verilog_library( + name = "br_cdc_stable_data_tb", + srcs = ["br_cdc_stable_data_tb.sv"], + deps = [ + "//cdc/rtl:br_cdc_stable_data", + "//misc/sim:br_test_driver", + ], +) + +verilog_elab_test( + name = "br_cdc_stable_data_tb_elab_test", + tool = "verific", + top = "br_cdc_stable_data_tb", + deps = [ + ":br_cdc_stable_data_tb", + "//gate/rtl:br_gate_mock", + ], +) + +br_verilog_sim_test_tools_suite( + name = "br_cdc_stable_data_sim_test_tools_suite", + params = { + "Width": ["8"], + "InitValue": [ + "0", + "1", + ], + "RegisterResetActive": [ + "0", + "1", + ], + "NumSyncStages": [ + "2", + "3", + ], + "SrcClkPeriodNs": [ + "10", + "16", + ], + "DstClkPeriodNs": [ + "10", + "16", + ], + }, + tools = ["vcs"], + top = "br_cdc_stable_data_tb", + deps = [ + ":br_cdc_stable_data_tb", + "//gate/rtl:br_gate_mock", + ], +) + +verilog_library( + name = "br_cdc_stable_data_autoupdate_tb", + srcs = ["br_cdc_stable_data_autoupdate_tb.sv"], + deps = [ + "//cdc/rtl:br_cdc_stable_data_autoupdate", + "//misc/sim:br_test_driver", + ], +) + +verilog_elab_test( + name = "br_cdc_stable_data_autoupdate_tb_elab_test", + tool = "verific", + top = "br_cdc_stable_data_autoupdate_tb", + deps = [ + ":br_cdc_stable_data_autoupdate_tb", + "//gate/rtl:br_gate_mock", + ], +) + +br_verilog_sim_test_tools_suite( + name = "br_cdc_stable_data_autoupdate_sim_test_tools_suite", + params = { + "Width": ["8"], + "InitValue": [ + "0", + "1", + ], + "RegisterResetActive": [ + "0", + "1", + ], + "NumSyncStages": [ + "2", + "3", + ], + "SrcClkPeriodNs": [ + "10", + "16", + ], + "DstClkPeriodNs": [ + "10", + "16", + ], + }, + tools = ["vcs"], + top = "br_cdc_stable_data_autoupdate_tb", + deps = [ + ":br_cdc_stable_data_autoupdate_tb", + "//gate/rtl:br_gate_mock", + ], +) diff --git a/cdc/sim/br_cdc_stable_data_autoupdate_tb.sv b/cdc/sim/br_cdc_stable_data_autoupdate_tb.sv new file mode 100644 index 000000000..b8c7cac6f --- /dev/null +++ b/cdc/sim/br_cdc_stable_data_autoupdate_tb.sv @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Bedrock-RTL Stable Data CDC Unit Test +// + +module br_cdc_stable_data_autoupdate_tb; + parameter int Width = 8; + parameter logic [Width-1:0] InitValue = '0; + parameter bit RegisterResetActive = 1; + parameter int NumSyncStages = 3; + parameter int SrcClkPeriodNs = 10; + parameter int DstClkPeriodNs = 10; + + logic src_clk; + logic src_rst; + logic [Width-1:0] src_data; + logic dst_clk; + logic dst_rst; + logic dst_updated; + logic [Width-1:0] dst_data; + + br_cdc_stable_data_autoupdate #( + .Width(Width), + .InitValue(InitValue), + .RegisterResetActive(RegisterResetActive), + .NumSyncStages(NumSyncStages) + ) dut ( + .src_clk, + .src_rst, + .src_data, + .dst_clk, + .dst_rst, + .dst_updated, + .dst_data + ); + + br_test_driver #( + .ResetCycles(14) + ) td ( + .clk(src_clk), + .rst(src_rst) + ); + + initial dst_clk = 1'b0; + always #(DstClkPeriodNs / 2) dst_clk = ~dst_clk; + + initial begin + dst_rst = 1'b1; + src_data = InitValue; + + td.reset_dut(); + + @(negedge dst_clk); + dst_rst = 1'b0; + + @(posedge dst_clk); + td.check_integer(dst_data, InitValue, "dst_data initial value mismatch"); + + for (int i = 5; i < 15; i++) begin + // Send the update + @(negedge src_clk); + src_data = i; + + // Wait until we see the update on the destination side + @(posedge dst_clk); + while (!dst_updated) @(posedge dst_clk); + td.check_integer(dst_data, i, "dst_data value mismatch"); + // The data should remain stable after update + @(posedge dst_clk); + td.check_integer(dst_data, i, "dst_data value not stable"); + + // Need to wait some cycles for the source side to be ready again + td.wait_cycles(10); + end + + td.finish(); + end + +endmodule diff --git a/cdc/sim/br_cdc_stable_data_tb.sv b/cdc/sim/br_cdc_stable_data_tb.sv new file mode 100644 index 000000000..ca453da4f --- /dev/null +++ b/cdc/sim/br_cdc_stable_data_tb.sv @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Bedrock-RTL Stable Data CDC Unit Test +// + +module br_cdc_stable_data_tb; + parameter int Width = 8; + parameter logic [Width-1:0] InitValue = '0; + parameter bit RegisterResetActive = 1; + parameter int NumSyncStages = 3; + parameter int SrcClkPeriodNs = 10; + parameter int DstClkPeriodNs = 10; + + logic src_clk; + logic src_rst; + logic src_valid; + logic [Width-1:0] src_data; + logic dst_clk; + logic dst_rst; + logic dst_updated; + logic [Width-1:0] dst_data; + + br_cdc_stable_data #( + .Width(Width), + .InitValue(InitValue), + .RegisterResetActive(RegisterResetActive), + .NumSyncStages(NumSyncStages) + ) dut ( + .src_clk, + .src_rst, + .src_valid, + .src_data, + .dst_clk, + .dst_rst, + .dst_updated, + .dst_data + ); + + br_test_driver #( + .ResetCycles(14) + ) td ( + .clk(src_clk), + .rst(src_rst) + ); + + initial dst_clk = 1'b0; + always #(DstClkPeriodNs / 2) dst_clk = ~dst_clk; + + initial begin + dst_rst = 1'b1; + src_valid = 1'b0; + src_data = '0; + + td.reset_dut(); + + @(negedge dst_clk); + dst_rst = 1'b0; + + @(posedge dst_clk); + td.check_integer(dst_data, InitValue, "dst_data initial value mismatch"); + + for (int i = 5; i < 15; i++) begin + // Send the update + @(negedge src_clk); + src_valid = 1'b1; + src_data = i; + @(negedge src_clk); + src_valid = 1'b0; + + // Wait until we see the update on the destination side + @(posedge dst_clk); + while (!dst_updated) @(posedge dst_clk); + td.check_integer(dst_data, i, "dst_data value mismatch"); + // The data should remain stable after update + @(posedge dst_clk); + td.check_integer(dst_data, i, "dst_data value not stable"); + + // Need to wait some cycles for the source side to be ready again + td.wait_cycles(10); + end + + td.finish(); + end + +endmodule