| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| // This module should be instantiated within flops of CDC synchronization primitives, |
| // and allows DV to model real CDC delays within simulations, especially useful at the chip level |
| // or in IPs that communicate across clock domains. |
| // |
| // If not, delay randomization is enabled - the faster of the two clocks is used to latch src_data, |
| // dst_data is synchronously driven with a random combination of the current src_data and |
| // the delayed version of src_data. |
| // |
| // If a source clock isn't used, the input src_data is latched after a parameterizable latency |
| // as `src_data_with_latency`, and an internal version of the output data `src_data_delayed` is set |
| // to this same value after a parameterizable jitter period. |
| // |
| // This is meant to model skew between synchronizer bits and wire delay between the src and dst |
| // flops. |
| // |
| // Five (5) different random delay modes are available: |
| // |
| // - PrimCdcRandDelayDisable: If this delay mode is picked, this module acts as a simple |
| // passthrough. |
| // |
| // - PrimCdcRandDelaySlow: If this delay mode is picked, the output to the dst domain is |
| // continuously driven to `src_data_delayed`. |
| // |
| // - PrimCdcRandDelayOnce: If this delay mode is picked, the mask `out_data_mask` used to combine |
| // `src_data_with_latency` and `src_data_delayed` is fully randomized |
| // once at the beginning of the simulation. |
| // |
| // - PrimCdcRandDelayInterval: If this delay mode is picked, the mask `out_data_mask` used to |
| // combine `src_data_with_latency` and `src_data_delayed` is fully |
| // randomized every `prim_cdc_num_src_data_changes` times that |
| // `src_data_with_latency` changes. |
| // |
| // - PrimCdcRandDelayFull: If this delay mode is picked, the mask `out_data_mask` used to combine |
| // `src_data_with_latency` and `src_data_delayed` is fully randomized |
| // any time `src_data_with_latency` changes. |
| // |
| // DV has control of the weights corresponding to each random delay mode when the delay mode is |
| // randomized, but can also directly override the delay mode as desired. |
| // |
| // DV also has control over whether the source clock is used, the latency, and the jitter values - |
| // these can be modified through the provided setter tasks. |
| |
| module prim_cdc_rand_delay #( |
| parameter int DataWidth = 1, |
| parameter bit UseSourceClock = 1, |
| parameter int LatencyPs = 500, |
| parameter int JitterPs = 500 |
| ) ( |
| input logic src_clk, |
| input logic [DataWidth-1:0] src_data, |
| |
| input logic dst_clk, |
| output logic [DataWidth-1:0] dst_data |
| ); |
| |
| `ASSERT_INIT(LegalWidth_A, DataWidth > 0) |
| |
| int prim_cdc_latency_ps = LatencyPs; |
| int prim_cdc_jitter_ps = JitterPs; |
| |
| // Only applies with using CdcRandDelayInterval randomization mode. |
| // |
| // This is the number of times that `src_data_with_latency` is allowed to change before |
| // we re-randomize `out_data_mask`. |
| int prim_cdc_num_src_data_changes = 10; |
| int counter = 0; |
| |
| task automatic set_prim_cdc_latency_ps(int val); |
| prim_cdc_latency_ps = val; |
| `ASSERT_I(LegalLatency_A, prim_cdc_latency_ps > 0) |
| endtask |
| |
| task automatic set_prim_cdc_jitter_ps(int val); |
| prim_cdc_jitter_ps = val; |
| `ASSERT_I(LegalJitter_A, prim_cdc_jitter_ps > 0) |
| endtask |
| |
| task automatic set_prim_cdc_num_src_data_changes(int val); |
| prim_cdc_num_src_data_changes = val; |
| endtask |
| |
| typedef enum bit [2:0] { |
| PrimCdcRandDelayDisable, |
| PrimCdcRandDelaySlow, |
| PrimCdcRandDelayOnce, |
| PrimCdcRandDelayInterval, |
| PrimCdcRandDelayFull |
| } prim_cdc_rand_delay_mode_e; |
| |
| prim_cdc_rand_delay_mode_e prim_cdc_rand_mode; |
| |
| logic [DataWidth-1:0] out_data_mask; |
| |
| bit en_passthru = 1'b0; |
| |
| bit out_randomize_en = 1'b0; |
| |
| bit en_rand_interval_mask = 1'b0; |
| |
| logic [DataWidth-1:0] src_data_with_latency; |
| logic [DataWidth-1:0] src_data_delayed; |
| |
| int unsigned prim_cdc_rand_disable_weight = 0; |
| int unsigned prim_cdc_rand_slow_weight = 20; |
| int unsigned prim_cdc_rand_once_weight = 50; |
| int unsigned prim_cdc_rand_interval_weight = 20; |
| int unsigned prim_cdc_rand_full_weight = 10; |
| |
| initial begin |
| // DV can override these from command line as desired. |
| void'($value$plusargs("prim_cdc_latency_ps=%0d", prim_cdc_latency_ps)); |
| void'($value$plusargs("prim_cdc_jitter_ps=%0d", prim_cdc_jitter_ps)); |
| void'($value$plusargs("prim_cdc_num_src_data_changes=%0d", prim_cdc_num_src_data_changes)); |
| void'($value$plusargs("prim_cdc_rand_disable_weight=%0d", prim_cdc_rand_disable_weight)); |
| void'($value$plusargs("prim_cdc_rand_slow_weight=%0d", prim_cdc_rand_slow_weight)); |
| void'($value$plusargs("prim_cdc_rand_once_weight=%0d", prim_cdc_rand_once_weight)); |
| void'($value$plusargs("prim_cdc_rand_interval_weight=%0d", prim_cdc_rand_interval_weight)); |
| void'($value$plusargs("prim_cdc_rand_full_weight=%0d", prim_cdc_rand_full_weight)); |
| |
| if (!$value$plusargs("prim_cdc_rand_mode=%0d", prim_cdc_rand_mode)) begin |
| // By default pick the most performant(*) random delay mode for normal test |
| // development/simulation. |
| // |
| // (*): Need to do some performance experiments to check that the chosen mode is actually the |
| // most performant. |
| prim_cdc_rand_mode = PrimCdcRandDelaySlow; |
| end |
| |
| unique case (prim_cdc_rand_mode) |
| PrimCdcRandDelayDisable: begin |
| // If CDC randomization disabled, behave like a passthrough |
| en_passthru = 1'b1; |
| end |
| PrimCdcRandDelaySlow: begin |
| out_data_mask = '1; |
| end |
| PrimCdcRandDelayOnce: begin |
| void'(std::randomize(out_data_mask)); |
| end |
| PrimCdcRandDelayInterval: begin |
| out_randomize_en = 1'b1; |
| en_rand_interval_mask = 1'b1; |
| end |
| PrimCdcRandDelayFull: begin |
| out_randomize_en = 1'b1; |
| end |
| default: begin |
| $fatal("%0d is an invalid randomization mode", prim_cdc_rand_mode); |
| end |
| endcase |
| end |
| |
| // TODO: Run some performance experiments using this implementation versus an implementation that |
| // primarily uses `forever` blocks rather than RTL constructs. |
| // Need to also check if this alternate implementation is still valid when |
| // compiling/simulating the design. |
| if (UseSourceClock) begin : gen_use_source_clock |
| // If relying on src_clk, insert a delay on the fastest clock |
| always_ff @(posedge src_clk or posedge dst_clk) begin |
| src_data_delayed <= src_data; |
| end |
| assign src_data_with_latency = src_data; |
| end else begin : gen_no_use_source_clock |
| // If not relying on src_clk, delay by a fixed number of ps determined by the module parameters |
| always_comb begin |
| src_data_with_latency <= #(prim_cdc_latency_ps * 1ps) src_data; |
| end |
| |
| always_comb begin |
| src_data_delayed <= #(prim_cdc_jitter_ps * 1ps) src_data_with_latency; |
| end |
| end : gen_no_use_source_clock |
| |
| // Randomize delayed random data selection only when input data changes |
| always @(src_data_with_latency) begin |
| if ((out_randomize_en && !en_rand_interval_mask) || |
| (en_rand_interval_mask && counter == prim_cdc_num_src_data_changes) begin |
| for (int i = 0; i < DataWidth; i += 32) begin |
| // As of VCS 2017.12-SP2-6, it is slower to randomize for a DataWidth <= 32 with |
| // std::randomize() than using $urandom(), which may be more noticeable here as this module |
| // can potentially have a large number of instances. |
| // |
| // Whenever time permits, it will be interesting to run some perf tests with the current VCS |
| // version and see what updated performance looks like. |
| out_data_mask = (out_data_mask << 32) | $urandom(); |
| end |
| end |
| |
| if (en_rand_interval_mask) begin |
| counter <= (counter == prim_cdc_num_src_data_changes) ? 0 : counter + 1; |
| end |
| end |
| |
| assign dst_data = (en_passthru) ? |
| (src_data) : |
| ((src_data_delayed & out_data_mask) | (src_data_with_latency & ~out_data_mask)); |
| |
| //TODO: coverage |
| |
| endmodule |