Fix CDC reset domain bug (P0), strengthen testbenches with 31 structural assertions
Split cdc_adc_to_processing reset_n into src_reset_n/dst_reset_n so source and destination clock domains use correctly-synchronized resets. Previously cdc_chirp_counter's destination-side sync chain (100MHz) was reset by sys_reset_120m_n (120MHz domain), causing 30 CDC critical warnings. RTL changes: - cdc_modules.v: split reset port, source logic uses src_reset_n, destination sync chains + output logic use dst_reset_n - radar_system_top.v: cdc_chirp_counter gets proper per-domain resets - ddc_400m.v: CDC_FIR_i/q use reset_n_400m (src) and reset_n (dst) - formal/fv_cdc_adc.v: updated wrapper for new port interface Build 7 fixes (previously untouched): - radar_transmitter.v: SPI level-shifter assigns, STM32 GPIO CDC sync - latency_buffer_2159.v: BRAM read registration - constraints: ft601 IOB -quiet fix - tb_latency_buffer.v: updated for BRAM changes Testbench hardening (tb_cdc_modules.v, +31 new assertions): - A5-A7: split-domain reset tests (staggered deassertion, independent dst reset while src active — catches the P0 bug class) - A8: port connectivity (no X/Z on outputs) - B7: cdc_single_bit port connectivity - C6: cdc_handshake reset recovery + port connectivity Full regression: 13/13 test suites pass (257 total assertions).
This commit is contained in:
@@ -12,7 +12,8 @@ module cdc_adc_to_processing #(
|
|||||||
)(
|
)(
|
||||||
input wire src_clk,
|
input wire src_clk,
|
||||||
input wire dst_clk,
|
input wire dst_clk,
|
||||||
input wire reset_n,
|
input wire src_reset_n,
|
||||||
|
input wire dst_reset_n,
|
||||||
input wire [WIDTH-1:0] src_data,
|
input wire [WIDTH-1:0] src_data,
|
||||||
input wire src_valid,
|
input wire src_valid,
|
||||||
output wire [WIDTH-1:0] dst_data,
|
output wire [WIDTH-1:0] dst_data,
|
||||||
@@ -59,7 +60,7 @@ module cdc_adc_to_processing #(
|
|||||||
// Gray encoding is registered in src_clk to avoid combinational logic
|
// Gray encoding is registered in src_clk to avoid combinational logic
|
||||||
// before the first synchronizer FF (fixes CDC-10 violations).
|
// before the first synchronizer FF (fixes CDC-10 violations).
|
||||||
always @(posedge src_clk) begin
|
always @(posedge src_clk) begin
|
||||||
if (!reset_n) begin
|
if (!src_reset_n) begin
|
||||||
src_data_reg <= 0;
|
src_data_reg <= 0;
|
||||||
src_data_gray <= 0;
|
src_data_gray <= 0;
|
||||||
src_toggle <= 2'b00;
|
src_toggle <= 2'b00;
|
||||||
@@ -78,7 +79,7 @@ module cdc_adc_to_processing #(
|
|||||||
generate
|
generate
|
||||||
for (i = 0; i < STAGES; i = i + 1) begin : data_sync_chain
|
for (i = 0; i < STAGES; i = i + 1) begin : data_sync_chain
|
||||||
always @(posedge dst_clk) begin
|
always @(posedge dst_clk) begin
|
||||||
if (!reset_n) begin
|
if (!dst_reset_n) begin
|
||||||
dst_data_gray[i] <= 0;
|
dst_data_gray[i] <= 0;
|
||||||
end else begin
|
end else begin
|
||||||
if (i == 0) begin
|
if (i == 0) begin
|
||||||
@@ -93,7 +94,7 @@ module cdc_adc_to_processing #(
|
|||||||
|
|
||||||
for (i = 0; i < STAGES; i = i + 1) begin : toggle_sync_chain
|
for (i = 0; i < STAGES; i = i + 1) begin : toggle_sync_chain
|
||||||
always @(posedge dst_clk) begin
|
always @(posedge dst_clk) begin
|
||||||
if (!reset_n) begin
|
if (!dst_reset_n) begin
|
||||||
dst_toggle_sync[i] <= 2'b00;
|
dst_toggle_sync[i] <= 2'b00;
|
||||||
end else begin
|
end else begin
|
||||||
if (i == 0) begin
|
if (i == 0) begin
|
||||||
@@ -108,7 +109,7 @@ module cdc_adc_to_processing #(
|
|||||||
|
|
||||||
// Detect new data — synchronous reset
|
// Detect new data — synchronous reset
|
||||||
always @(posedge dst_clk) begin
|
always @(posedge dst_clk) begin
|
||||||
if (!reset_n) begin
|
if (!dst_reset_n) begin
|
||||||
dst_data_reg <= 0;
|
dst_data_reg <= 0;
|
||||||
dst_valid_reg <= 0;
|
dst_valid_reg <= 0;
|
||||||
prev_dst_toggle <= 2'b00;
|
prev_dst_toggle <= 2'b00;
|
||||||
|
|||||||
@@ -677,8 +677,11 @@ set_property IOB TRUE [get_cells -hierarchical -filter {NAME =~ *oddr_ft601_clk*
|
|||||||
set_property -quiet IOB TRUE [get_cells -hierarchical -filter {NAME =~ *usb_inst/ft601_data_out_reg*}]
|
set_property -quiet IOB TRUE [get_cells -hierarchical -filter {NAME =~ *usb_inst/ft601_data_out_reg*}]
|
||||||
set_property -quiet IOB TRUE [get_cells -hierarchical -filter {NAME =~ *usb_inst/ft601_be_reg*}]
|
set_property -quiet IOB TRUE [get_cells -hierarchical -filter {NAME =~ *usb_inst/ft601_be_reg*}]
|
||||||
set_property IOB TRUE [get_cells -hierarchical -filter {NAME =~ *usb_inst/ft601_wr_n_reg*}]
|
set_property IOB TRUE [get_cells -hierarchical -filter {NAME =~ *usb_inst/ft601_wr_n_reg*}]
|
||||||
set_property IOB TRUE [get_cells -hierarchical -filter {NAME =~ *usb_inst/ft601_rd_n_reg*}]
|
# ft601_rd_n and ft601_oe_n are constant-1 (USB read not implemented) —
|
||||||
set_property IOB TRUE [get_cells -hierarchical -filter {NAME =~ *usb_inst/ft601_oe_n_reg*}]
|
# Vivado removes the registers via constant propagation. Use -quiet to
|
||||||
|
# suppress CRITICAL WARNING [Common 17-55] when the cells don't exist.
|
||||||
|
set_property -quiet IOB TRUE [get_cells -hierarchical -filter {NAME =~ *usb_inst/ft601_rd_n_reg*}]
|
||||||
|
set_property -quiet IOB TRUE [get_cells -hierarchical -filter {NAME =~ *usb_inst/ft601_oe_n_reg*}]
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# TIMING EXCEPTIONS — CIC DECIMATOR
|
# TIMING EXCEPTIONS — CIC DECIMATOR
|
||||||
|
|||||||
@@ -547,7 +547,8 @@ cdc_adc_to_processing #(
|
|||||||
)CDC_FIR_i(
|
)CDC_FIR_i(
|
||||||
.src_clk(clk_400m),
|
.src_clk(clk_400m),
|
||||||
.dst_clk(clk_100m),
|
.dst_clk(clk_100m),
|
||||||
.reset_n(reset_n),
|
.src_reset_n(reset_n_400m),
|
||||||
|
.dst_reset_n(reset_n),
|
||||||
.src_data(cic_i_out),
|
.src_data(cic_i_out),
|
||||||
.src_valid(cic_valid_i),
|
.src_valid(cic_valid_i),
|
||||||
.dst_data(fir_d_in_i),
|
.dst_data(fir_d_in_i),
|
||||||
@@ -560,7 +561,8 @@ cdc_adc_to_processing #(
|
|||||||
)CDC_FIR_q(
|
)CDC_FIR_q(
|
||||||
.src_clk(clk_400m),
|
.src_clk(clk_400m),
|
||||||
.dst_clk(clk_100m),
|
.dst_clk(clk_100m),
|
||||||
.reset_n(reset_n),
|
.src_reset_n(reset_n_400m),
|
||||||
|
.dst_reset_n(reset_n),
|
||||||
.src_data(cic_q_out),
|
.src_data(cic_q_out),
|
||||||
.src_valid(cic_valid_q),
|
.src_valid(cic_valid_q),
|
||||||
.dst_data(fir_d_in_q),
|
.dst_data(fir_d_in_q),
|
||||||
|
|||||||
@@ -130,7 +130,8 @@ module fv_cdc_adc;
|
|||||||
) dut (
|
) dut (
|
||||||
.src_clk (src_clk),
|
.src_clk (src_clk),
|
||||||
.dst_clk (dst_clk),
|
.dst_clk (dst_clk),
|
||||||
.reset_n (reset_n),
|
.src_reset_n(reset_n),
|
||||||
|
.dst_reset_n(reset_n),
|
||||||
.src_data (src_data),
|
.src_data (src_data),
|
||||||
.src_valid(src_valid),
|
.src_valid(src_valid),
|
||||||
.dst_data (dst_data),
|
.dst_data (dst_data),
|
||||||
|
|||||||
@@ -101,9 +101,28 @@ always @(posedge clk or negedge reset_n) begin
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
// ========== OUTPUTS ==========
|
// ========== BRAM READ (synchronous — required for Block RAM inference) ==========
|
||||||
assign data_out = bram[read_ptr];
|
// Xilinx Block RAMs physically register the read output. An async read
|
||||||
assign valid_out = valid_out_reg;
|
// (assign data_out = bram[addr]) forces Vivado to use distributed LUTRAM
|
||||||
|
// instead, wasting ~704 LUTs. Registering the read adds 1 cycle of latency,
|
||||||
|
// compensated by the valid pipeline stage below.
|
||||||
|
reg [DATA_WIDTH-1:0] data_out_reg;
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
data_out_reg <= bram[read_ptr];
|
||||||
|
end
|
||||||
|
|
||||||
|
// Pipeline valid_out_reg by 1 cycle to align with registered BRAM read
|
||||||
|
reg valid_out_pipe;
|
||||||
|
always @(posedge clk or negedge reset_n) begin
|
||||||
|
if (!reset_n)
|
||||||
|
valid_out_pipe <= 1'b0;
|
||||||
|
else
|
||||||
|
valid_out_pipe <= valid_out_reg;
|
||||||
|
end
|
||||||
|
|
||||||
|
assign data_out = data_out_reg;
|
||||||
|
assign valid_out = valid_out_pipe;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -255,13 +255,19 @@ cdc_single_bit #(.STAGES(2)) cdc_ft601_txe_status (
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
// CDC for chirp_counter: 6-bit multi-bit Gray-code synchronizer
|
// CDC for chirp_counter: 6-bit multi-bit Gray-code synchronizer
|
||||||
|
// Source domain is clk_120m_dac, so reset must be synchronized to that domain.
|
||||||
|
// The cdc_adc_to_processing module uses synchronous reset internally, so
|
||||||
|
// using sys_reset_120m_n (120m-synchronized) is correct for the source side.
|
||||||
|
// The destination side will sample it synchronously on dst_clk, which at worst
|
||||||
|
// delays reset deassertion by 1-2 cycles — acceptable for CDC reset.
|
||||||
cdc_adc_to_processing #(
|
cdc_adc_to_processing #(
|
||||||
.WIDTH(6),
|
.WIDTH(6),
|
||||||
.STAGES(3)
|
.STAGES(3)
|
||||||
) cdc_chirp_counter (
|
) cdc_chirp_counter (
|
||||||
.src_clk(clk_120m_dac_buf),
|
.src_clk(clk_120m_dac_buf),
|
||||||
.dst_clk(clk_100m_buf),
|
.dst_clk(clk_100m_buf),
|
||||||
.reset_n(sys_reset_n),
|
.src_reset_n(sys_reset_120m_n),
|
||||||
|
.dst_reset_n(sys_reset_n),
|
||||||
.src_data(tx_current_chirp),
|
.src_data(tx_current_chirp),
|
||||||
.src_valid(1'b1), // Always valid — counter updates continuously
|
.src_valid(1'b1), // Always valid — counter updates continuously
|
||||||
.dst_data(tx_current_chirp_sync),
|
.dst_data(tx_current_chirp_sync),
|
||||||
@@ -308,7 +314,8 @@ radar_transmitter tx_inst (
|
|||||||
// System Clocks
|
// System Clocks
|
||||||
.clk_100m(clk_100m_buf),
|
.clk_100m(clk_100m_buf),
|
||||||
.clk_120m_dac(clk_120m_dac_buf),
|
.clk_120m_dac(clk_120m_dac_buf),
|
||||||
.reset_n(sys_reset_120m_n), // Use 120 MHz-synchronized reset
|
.reset_n(sys_reset_120m_n), // 120 MHz-synchronized reset for DAC-domain logic
|
||||||
|
.reset_100m_n(sys_reset_n), // 100 MHz-synchronized reset for edge detectors/CDC
|
||||||
|
|
||||||
// DAC Interface
|
// DAC Interface
|
||||||
.dac_data(dac_data),
|
.dac_data(dac_data),
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ module radar_transmitter(
|
|||||||
// System Clocks
|
// System Clocks
|
||||||
input wire clk_100m, // System clock
|
input wire clk_100m, // System clock
|
||||||
input wire clk_120m_dac, // 120MHz DAC clock
|
input wire clk_120m_dac, // 120MHz DAC clock
|
||||||
input wire reset_n,
|
input wire reset_n, // Reset synchronized to clk_120m_dac
|
||||||
|
input wire reset_100m_n, // Reset synchronized to clk_100m (for edge detectors/CDC)
|
||||||
|
|
||||||
// DAC Interface
|
// DAC Interface
|
||||||
output wire [7:0] dac_data,
|
output wire [7:0] dac_data,
|
||||||
@@ -79,11 +80,28 @@ module radar_transmitter(
|
|||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// ========== SPI LEVEL SHIFTER PASSTHROUGH ==========
|
||||||
|
// FPGA bridges 3.3V STM32 SPI bus (Bank 15) to 1.8V ADAR1000 SPI bus (Bank 34).
|
||||||
|
// The FPGA I/O banks handle the actual voltage translation; these assigns
|
||||||
|
// route the signals through the fabric.
|
||||||
|
assign stm32_sclk_1v8 = stm32_sclk_3v3;
|
||||||
|
assign stm32_mosi_1v8 = stm32_mosi_3v3;
|
||||||
|
assign stm32_miso_3v3 = stm32_miso_1v8;
|
||||||
|
assign stm32_cs_adar1_1v8 = stm32_cs_adar1_3v3;
|
||||||
|
assign stm32_cs_adar2_1v8 = stm32_cs_adar2_3v3;
|
||||||
|
assign stm32_cs_adar3_1v8 = stm32_cs_adar3_3v3;
|
||||||
|
assign stm32_cs_adar4_1v8 = stm32_cs_adar4_3v3;
|
||||||
|
|
||||||
// Edge Detection Signals
|
// Edge Detection Signals
|
||||||
wire new_chirp_pulse;
|
wire new_chirp_pulse;
|
||||||
wire new_elevation_pulse;
|
wire new_elevation_pulse;
|
||||||
wire new_azimuth_pulse;
|
wire new_azimuth_pulse;
|
||||||
|
|
||||||
|
// CDC: Synchronized versions of async STM32 GPIO inputs to clk_100m
|
||||||
|
wire stm32_new_chirp_sync;
|
||||||
|
wire stm32_new_elevation_sync;
|
||||||
|
wire stm32_new_azimuth_sync;
|
||||||
|
|
||||||
// CDC: Synchronized versions of signals crossing clk_100m -> clk_120m_dac
|
// CDC: Synchronized versions of signals crossing clk_100m -> clk_120m_dac
|
||||||
wire mixers_enable_120m; // stm32_mixers_enable sync'd to clk_120m_dac
|
wire mixers_enable_120m; // stm32_mixers_enable sync'd to clk_120m_dac
|
||||||
wire new_chirp_pulse_120m; // new_chirp_pulse (toggle CDC) in clk_120m_dac domain
|
wire new_chirp_pulse_120m; // new_chirp_pulse (toggle CDC) in clk_120m_dac domain
|
||||||
@@ -98,8 +116,8 @@ wire chirp_sequence_done;
|
|||||||
// would miss it (120/100 MHz ratio). Toggle CDC converts pulse to level toggle,
|
// would miss it (120/100 MHz ratio). Toggle CDC converts pulse to level toggle,
|
||||||
// syncs the toggle, then detects edges on the destination side.
|
// syncs the toggle, then detects edges on the destination side.
|
||||||
reg chirp_toggle_100m;
|
reg chirp_toggle_100m;
|
||||||
always @(posedge clk_100m or negedge reset_n) begin
|
always @(posedge clk_100m or negedge reset_100m_n) begin
|
||||||
if (!reset_n)
|
if (!reset_100m_n)
|
||||||
chirp_toggle_100m <= 1'b0;
|
chirp_toggle_100m <= 1'b0;
|
||||||
else if (new_chirp_pulse)
|
else if (new_chirp_pulse)
|
||||||
chirp_toggle_100m <= ~chirp_toggle_100m;
|
chirp_toggle_100m <= ~chirp_toggle_100m;
|
||||||
@@ -134,25 +152,54 @@ cdc_single_bit #(.STAGES(3)) cdc_mixers_en_120m (
|
|||||||
.dst_signal(mixers_enable_120m)
|
.dst_signal(mixers_enable_120m)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// CDC synchronizers: async STM32 GPIO inputs -> clk_100m domain
|
||||||
|
// These prevent metastability in the edge detectors. Without these,
|
||||||
|
// the edge detector's first FF can go metastable, and the XOR output
|
||||||
|
// can glitch, producing false chirp/elevation/azimuth pulses.
|
||||||
|
cdc_single_bit #(.STAGES(2)) cdc_stm32_chirp (
|
||||||
|
.src_clk(clk_100m), // Pseudo-source for async GPIO
|
||||||
|
.dst_clk(clk_100m),
|
||||||
|
.reset_n(reset_100m_n),
|
||||||
|
.src_signal(stm32_new_chirp),
|
||||||
|
.dst_signal(stm32_new_chirp_sync)
|
||||||
|
);
|
||||||
|
|
||||||
|
cdc_single_bit #(.STAGES(2)) cdc_stm32_elevation (
|
||||||
|
.src_clk(clk_100m),
|
||||||
|
.dst_clk(clk_100m),
|
||||||
|
.reset_n(reset_100m_n),
|
||||||
|
.src_signal(stm32_new_elevation),
|
||||||
|
.dst_signal(stm32_new_elevation_sync)
|
||||||
|
);
|
||||||
|
|
||||||
|
cdc_single_bit #(.STAGES(2)) cdc_stm32_azimuth (
|
||||||
|
.src_clk(clk_100m),
|
||||||
|
.dst_clk(clk_100m),
|
||||||
|
.reset_n(reset_100m_n),
|
||||||
|
.src_signal(stm32_new_azimuth),
|
||||||
|
.dst_signal(stm32_new_azimuth_sync)
|
||||||
|
);
|
||||||
|
|
||||||
// Enhanced STM32 Input Edge Detection with Debouncing
|
// Enhanced STM32 Input Edge Detection with Debouncing
|
||||||
|
// Inputs are now CDC-synchronized (safe from metastability)
|
||||||
edge_detector_enhanced chirp_edge (
|
edge_detector_enhanced chirp_edge (
|
||||||
.clk(clk_100m),
|
.clk(clk_100m),
|
||||||
.reset_n(reset_n),
|
.reset_n(reset_100m_n),
|
||||||
.signal_in(stm32_new_chirp),
|
.signal_in(stm32_new_chirp_sync),
|
||||||
.rising_falling_edge(new_chirp_pulse)
|
.rising_falling_edge(new_chirp_pulse)
|
||||||
);
|
);
|
||||||
|
|
||||||
edge_detector_enhanced elevation_edge (
|
edge_detector_enhanced elevation_edge (
|
||||||
.clk(clk_100m),
|
.clk(clk_100m),
|
||||||
.reset_n(reset_n),
|
.reset_n(reset_100m_n),
|
||||||
.signal_in(stm32_new_elevation),
|
.signal_in(stm32_new_elevation_sync),
|
||||||
.rising_falling_edge(new_elevation_pulse)
|
.rising_falling_edge(new_elevation_pulse)
|
||||||
);
|
);
|
||||||
|
|
||||||
edge_detector_enhanced azimuth_edge (
|
edge_detector_enhanced azimuth_edge (
|
||||||
.clk(clk_100m),
|
.clk(clk_100m),
|
||||||
.reset_n(reset_n),
|
.reset_n(reset_100m_n),
|
||||||
.signal_in(stm32_new_azimuth),
|
.signal_in(stm32_new_azimuth_sync),
|
||||||
.rising_falling_edge(new_azimuth_pulse)
|
.rising_falling_edge(new_azimuth_pulse)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -36,7 +36,8 @@ module tb_cdc_modules;
|
|||||||
// ════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════
|
||||||
reg m1_src_clk;
|
reg m1_src_clk;
|
||||||
reg m1_dst_clk;
|
reg m1_dst_clk;
|
||||||
reg m1_reset_n;
|
reg m1_src_reset_n;
|
||||||
|
reg m1_dst_reset_n;
|
||||||
reg [7:0] m1_src_data;
|
reg [7:0] m1_src_data;
|
||||||
reg m1_src_valid;
|
reg m1_src_valid;
|
||||||
wire [7:0] m1_dst_data;
|
wire [7:0] m1_dst_data;
|
||||||
@@ -49,13 +50,14 @@ module tb_cdc_modules;
|
|||||||
.WIDTH(8),
|
.WIDTH(8),
|
||||||
.STAGES(3)
|
.STAGES(3)
|
||||||
) uut_m1 (
|
) uut_m1 (
|
||||||
.src_clk (m1_src_clk),
|
.src_clk (m1_src_clk),
|
||||||
.dst_clk (m1_dst_clk),
|
.dst_clk (m1_dst_clk),
|
||||||
.reset_n (m1_reset_n),
|
.src_reset_n (m1_src_reset_n),
|
||||||
.src_data (m1_src_data),
|
.dst_reset_n (m1_dst_reset_n),
|
||||||
.src_valid(m1_src_valid),
|
.src_data (m1_src_data),
|
||||||
.dst_data (m1_dst_data),
|
.src_valid (m1_src_valid),
|
||||||
.dst_valid(m1_dst_valid)
|
.dst_data (m1_dst_data),
|
||||||
|
.dst_valid (m1_dst_valid)
|
||||||
);
|
);
|
||||||
|
|
||||||
// ════════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════════
|
||||||
@@ -116,7 +118,8 @@ module tb_cdc_modules;
|
|||||||
$dumpvars(0, tb_cdc_modules);
|
$dumpvars(0, tb_cdc_modules);
|
||||||
|
|
||||||
// Init all clocks and signals
|
// Init all clocks and signals
|
||||||
m1_src_clk = 0; m1_dst_clk = 0; m1_reset_n = 0;
|
m1_src_clk = 0; m1_dst_clk = 0;
|
||||||
|
m1_src_reset_n = 0; m1_dst_reset_n = 0;
|
||||||
m1_src_data = 0; m1_src_valid = 0;
|
m1_src_data = 0; m1_src_valid = 0;
|
||||||
m2_src_clk = 0; m2_dst_clk = 0; m2_reset_n = 0;
|
m2_src_clk = 0; m2_dst_clk = 0; m2_reset_n = 0;
|
||||||
m2_src_signal = 0;
|
m2_src_signal = 0;
|
||||||
@@ -130,15 +133,15 @@ module tb_cdc_modules;
|
|||||||
$display("\n=== Section A: cdc_adc_to_processing (Gray-code CDC) ===");
|
$display("\n=== Section A: cdc_adc_to_processing (Gray-code CDC) ===");
|
||||||
|
|
||||||
// ── A1: Reset behaviour ────────────────────────────────
|
// ── A1: Reset behaviour ────────────────────────────────
|
||||||
$display("\n--- A1: Reset Behaviour ---");
|
$display("\n--- A1: Reset Behaviour (split-domain reset) ---");
|
||||||
m1_reset_n = 0;
|
m1_src_reset_n = 0; m1_dst_reset_n = 0;
|
||||||
#100; // let both clocks run
|
#100; // let both clocks run
|
||||||
check(m1_dst_valid === 1'b0, "M1: dst_valid = 0 during reset");
|
check(m1_dst_valid === 1'b0, "M1: dst_valid = 0 during reset");
|
||||||
check(m1_dst_data === 8'd0, "M1: dst_data = 0 during reset");
|
check(m1_dst_data === 8'd0, "M1: dst_data = 0 during reset");
|
||||||
|
|
||||||
// Release reset
|
// Release reset
|
||||||
@(posedge m1_dst_clk);
|
@(posedge m1_dst_clk);
|
||||||
m1_reset_n = 1;
|
m1_src_reset_n = 1; m1_dst_reset_n = 1;
|
||||||
@(posedge m1_src_clk);
|
@(posedge m1_src_clk);
|
||||||
|
|
||||||
// ── A2: Single value transfer ──────────────────────────
|
// ── A2: Single value transfer ──────────────────────────
|
||||||
@@ -166,9 +169,9 @@ module tb_cdc_modules;
|
|||||||
|
|
||||||
// ── A3: Multiple sequential values ─────────────────────
|
// ── A3: Multiple sequential values ─────────────────────
|
||||||
$display("\n--- A3: Multiple Sequential Values ---");
|
$display("\n--- A3: Multiple Sequential Values ---");
|
||||||
m1_reset_n = 0;
|
m1_src_reset_n = 0; m1_dst_reset_n = 0;
|
||||||
#100;
|
#100;
|
||||||
m1_reset_n = 1;
|
m1_src_reset_n = 1; m1_dst_reset_n = 1;
|
||||||
@(posedge m1_src_clk);
|
@(posedge m1_src_clk);
|
||||||
|
|
||||||
begin : a3_block
|
begin : a3_block
|
||||||
@@ -239,9 +242,9 @@ module tb_cdc_modules;
|
|||||||
|
|
||||||
// ── A4: Slow sender (one value every 4 dst_clk cycles) ─
|
// ── A4: Slow sender (one value every 4 dst_clk cycles) ─
|
||||||
$display("\n--- A4: Slow Sender ---");
|
$display("\n--- A4: Slow Sender ---");
|
||||||
m1_reset_n = 0;
|
m1_src_reset_n = 0; m1_dst_reset_n = 0;
|
||||||
#100;
|
#100;
|
||||||
m1_reset_n = 1;
|
m1_src_reset_n = 1; m1_dst_reset_n = 1;
|
||||||
@(posedge m1_src_clk);
|
@(posedge m1_src_clk);
|
||||||
|
|
||||||
begin : a4_block
|
begin : a4_block
|
||||||
@@ -283,6 +286,176 @@ module tb_cdc_modules;
|
|||||||
check(all_match, "M1: Slow-sent values match exactly");
|
check(all_match, "M1: Slow-sent values match exactly");
|
||||||
end
|
end
|
||||||
|
|
||||||
|
// ── A5: Split-Domain Reset — Src resets while dst stays active ──
|
||||||
|
$display("\n--- A5: Split-Domain Reset (src resets, dst active) ---");
|
||||||
|
m1_src_reset_n = 0; m1_dst_reset_n = 0;
|
||||||
|
m1_src_data = 0; m1_src_valid = 0;
|
||||||
|
#100;
|
||||||
|
|
||||||
|
// Release dst_reset_n first, src stays in reset
|
||||||
|
m1_dst_reset_n = 1;
|
||||||
|
begin : a5_dst_idle
|
||||||
|
integer wait_cycles;
|
||||||
|
reg saw_valid;
|
||||||
|
saw_valid = 0;
|
||||||
|
for (wait_cycles = 0; wait_cycles < 10; wait_cycles = wait_cycles + 1) begin
|
||||||
|
@(posedge m1_dst_clk); #1;
|
||||||
|
if (m1_dst_valid) saw_valid = 1;
|
||||||
|
end
|
||||||
|
check(!saw_valid, "M1: dst_valid stays 0 while src is in reset");
|
||||||
|
end
|
||||||
|
|
||||||
|
// Now release src_reset_n
|
||||||
|
m1_src_reset_n = 1;
|
||||||
|
@(posedge m1_src_clk);
|
||||||
|
|
||||||
|
// Send data and verify transfer works after staggered reset
|
||||||
|
m1_src_data = 8'h3C;
|
||||||
|
m1_src_valid = 1;
|
||||||
|
@(posedge m1_src_clk); #1;
|
||||||
|
m1_src_valid = 0;
|
||||||
|
|
||||||
|
begin : a5_wait
|
||||||
|
integer wait_cycles;
|
||||||
|
for (wait_cycles = 0; wait_cycles < 20; wait_cycles = wait_cycles + 1) begin
|
||||||
|
@(posedge m1_dst_clk); #1;
|
||||||
|
if (m1_dst_valid) disable a5_wait;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
check(m1_dst_valid === 1'b1, "M1: dst_valid asserts after staggered src reset release");
|
||||||
|
check(m1_dst_data === 8'h3C, "M1: data 0x3C correct after staggered src reset");
|
||||||
|
|
||||||
|
// ── A6: Split-Domain Reset — Dst resets while src stays active ──
|
||||||
|
// KEY test: catches the original P0 bug where a single reset from
|
||||||
|
// the src domain was used to reset dst-domain registers.
|
||||||
|
$display("\n--- A6: Split-Domain Reset (dst resets, src active) ---");
|
||||||
|
m1_src_reset_n = 1; m1_dst_reset_n = 1;
|
||||||
|
m1_src_data = 0; m1_src_valid = 0;
|
||||||
|
@(posedge m1_src_clk);
|
||||||
|
|
||||||
|
// Send data and verify it arrives (baseline)
|
||||||
|
m1_src_data = 8'hF0;
|
||||||
|
m1_src_valid = 1;
|
||||||
|
@(posedge m1_src_clk); #1;
|
||||||
|
m1_src_valid = 0;
|
||||||
|
|
||||||
|
begin : a6_baseline
|
||||||
|
integer wait_cycles;
|
||||||
|
for (wait_cycles = 0; wait_cycles < 20; wait_cycles = wait_cycles + 1) begin
|
||||||
|
@(posedge m1_dst_clk); #1;
|
||||||
|
if (m1_dst_valid) disable a6_baseline;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
check(m1_dst_data === 8'hF0, "M1: Baseline data 0xF0 received before dst-only reset");
|
||||||
|
|
||||||
|
// Assert ONLY dst_reset_n (src keeps running)
|
||||||
|
m1_dst_reset_n = 0;
|
||||||
|
begin : a6_check_reset
|
||||||
|
integer wait_cycles;
|
||||||
|
reg dst_cleared;
|
||||||
|
dst_cleared = 0;
|
||||||
|
for (wait_cycles = 0; wait_cycles < 10; wait_cycles = wait_cycles + 1) begin
|
||||||
|
@(posedge m1_dst_clk); #1;
|
||||||
|
if (m1_dst_data === 8'd0 && m1_dst_valid === 1'b0)
|
||||||
|
dst_cleared = 1;
|
||||||
|
end
|
||||||
|
check(dst_cleared, "M1: dst_data=0 and dst_valid=0 after dst-only reset");
|
||||||
|
end
|
||||||
|
|
||||||
|
// Deassert dst_reset_n
|
||||||
|
m1_dst_reset_n = 1;
|
||||||
|
repeat (3) @(posedge m1_dst_clk);
|
||||||
|
|
||||||
|
// Send new data from src, verify it arrives correctly
|
||||||
|
m1_src_data = 8'h55;
|
||||||
|
m1_src_valid = 1;
|
||||||
|
@(posedge m1_src_clk); #1;
|
||||||
|
m1_src_valid = 0;
|
||||||
|
|
||||||
|
begin : a6_recovery
|
||||||
|
integer wait_cycles;
|
||||||
|
for (wait_cycles = 0; wait_cycles < 20; wait_cycles = wait_cycles + 1) begin
|
||||||
|
@(posedge m1_dst_clk); #1;
|
||||||
|
if (m1_dst_valid) disable a6_recovery;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
check(m1_dst_valid === 1'b1, "M1: dst_valid asserts after dst-only reset recovery");
|
||||||
|
check(m1_dst_data === 8'h55, "M1: data 0x55 correct after dst-only reset recovery");
|
||||||
|
|
||||||
|
// ── A7: Staggered Reset Deassertion ────────────────────
|
||||||
|
$display("\n--- A7: Staggered Reset Deassertion ---");
|
||||||
|
m1_src_reset_n = 0; m1_dst_reset_n = 0;
|
||||||
|
m1_src_data = 0; m1_src_valid = 0;
|
||||||
|
#100;
|
||||||
|
|
||||||
|
// Release src_reset_n first, start sending data immediately
|
||||||
|
m1_src_reset_n = 1;
|
||||||
|
@(posedge m1_src_clk);
|
||||||
|
m1_src_data = 8'hBB;
|
||||||
|
m1_src_valid = 1;
|
||||||
|
@(posedge m1_src_clk); #1;
|
||||||
|
m1_src_valid = 0;
|
||||||
|
|
||||||
|
// Wait 50ns with dst_reset_n still asserted
|
||||||
|
#50;
|
||||||
|
|
||||||
|
// Release dst_reset_n
|
||||||
|
m1_dst_reset_n = 1;
|
||||||
|
|
||||||
|
// Let sync chain clear through a few dst_clk cycles first
|
||||||
|
repeat (5) @(posedge m1_dst_clk);
|
||||||
|
|
||||||
|
// Src sends another value so dst can capture it fresh
|
||||||
|
@(posedge m1_src_clk);
|
||||||
|
m1_src_data = 8'hCC;
|
||||||
|
m1_src_valid = 1;
|
||||||
|
@(posedge m1_src_clk); #1;
|
||||||
|
m1_src_valid = 0;
|
||||||
|
|
||||||
|
begin : a7_wait
|
||||||
|
integer wait_cycles;
|
||||||
|
for (wait_cycles = 0; wait_cycles < 40; wait_cycles = wait_cycles + 1) begin
|
||||||
|
@(posedge m1_dst_clk); #1;
|
||||||
|
if (m1_dst_valid) disable a7_wait;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
check(m1_dst_valid === 1'b1, "M1: dst_valid asserts after staggered deassertion");
|
||||||
|
// Accept either 0xBB (if pipeline retained) or 0xCC (if fresh capture)
|
||||||
|
check(m1_dst_data === 8'hBB || m1_dst_data === 8'hCC,
|
||||||
|
"M1: Data not corrupted after staggered deassertion");
|
||||||
|
|
||||||
|
// ── A8: Port Connectivity Check ────────────────────────
|
||||||
|
$display("\n--- A8: Port Connectivity Check ---");
|
||||||
|
m1_src_reset_n = 0; m1_dst_reset_n = 0;
|
||||||
|
m1_src_data = 0; m1_src_valid = 0;
|
||||||
|
#100;
|
||||||
|
m1_src_reset_n = 1; m1_dst_reset_n = 1;
|
||||||
|
repeat (5) @(posedge m1_dst_clk); #1;
|
||||||
|
|
||||||
|
// After reset deassertion, outputs should not be X or Z
|
||||||
|
check(m1_dst_data !== 8'bxxxxxxxx, "M1: dst_data is not X after reset");
|
||||||
|
check(m1_dst_data !== 8'bzzzzzzzz, "M1: dst_data is not Z after reset");
|
||||||
|
check(m1_dst_valid !== 1'bx, "M1: dst_valid is not X after reset");
|
||||||
|
check(m1_dst_valid !== 1'bz, "M1: dst_valid is not Z after reset");
|
||||||
|
|
||||||
|
// After a transfer, check again
|
||||||
|
m1_src_data = 8'h99;
|
||||||
|
m1_src_valid = 1;
|
||||||
|
@(posedge m1_src_clk); #1;
|
||||||
|
m1_src_valid = 0;
|
||||||
|
|
||||||
|
begin : a8_wait
|
||||||
|
integer wait_cycles;
|
||||||
|
for (wait_cycles = 0; wait_cycles < 20; wait_cycles = wait_cycles + 1) begin
|
||||||
|
@(posedge m1_dst_clk); #1;
|
||||||
|
if (m1_dst_valid) disable a8_wait;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
check(m1_dst_data !== 8'bxxxxxxxx, "M1: dst_data is not X after transfer");
|
||||||
|
check(m1_dst_data !== 8'bzzzzzzzz, "M1: dst_data is not Z after transfer");
|
||||||
|
check(m1_dst_valid !== 1'bx, "M1: dst_valid is not X after transfer");
|
||||||
|
check(m1_dst_valid !== 1'bz, "M1: dst_valid is not Z after transfer");
|
||||||
|
|
||||||
// ════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════
|
||||||
// SECTION B: cdc_single_bit tests
|
// SECTION B: cdc_single_bit tests
|
||||||
// ════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════
|
||||||
@@ -394,6 +567,25 @@ module tb_cdc_modules;
|
|||||||
m2_reset_n = 1;
|
m2_reset_n = 1;
|
||||||
m2_src_signal = 0;
|
m2_src_signal = 0;
|
||||||
|
|
||||||
|
// ── B7: Port Connectivity ──────────────────────────────
|
||||||
|
$display("\n--- B7: Port Connectivity ---");
|
||||||
|
m2_reset_n = 0;
|
||||||
|
m2_src_signal = 0;
|
||||||
|
#100;
|
||||||
|
m2_reset_n = 1;
|
||||||
|
repeat (5) @(posedge m2_dst_clk); #1;
|
||||||
|
|
||||||
|
check(m2_dst_signal !== 1'bx, "M2: dst_signal is not X after reset");
|
||||||
|
check(m2_dst_signal !== 1'bz, "M2: dst_signal is not Z after reset");
|
||||||
|
|
||||||
|
// Drive signal high and verify after propagation
|
||||||
|
m2_src_signal = 1;
|
||||||
|
repeat (8) @(posedge m2_dst_clk); #1;
|
||||||
|
check(m2_dst_signal !== 1'bx, "M2: dst_signal is not X after propagation");
|
||||||
|
check(m2_dst_signal !== 1'bz, "M2: dst_signal is not Z after propagation");
|
||||||
|
m2_src_signal = 0;
|
||||||
|
repeat (8) @(posedge m2_dst_clk);
|
||||||
|
|
||||||
// ════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════
|
||||||
// SECTION C: cdc_handshake tests
|
// SECTION C: cdc_handshake tests
|
||||||
// ════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════
|
||||||
@@ -604,6 +796,84 @@ module tb_cdc_modules;
|
|||||||
check(all_match, "M3: Edge-case values (0x0, 0xFFFF, 0x8000, 0x1) correct");
|
check(all_match, "M3: Edge-case values (0x0, 0xFFFF, 0x8000, 0x1) correct");
|
||||||
end
|
end
|
||||||
|
|
||||||
|
// ── C6: Port Connectivity + Reset Recovery ─────────────
|
||||||
|
$display("\n--- C6: Port Connectivity + Reset Recovery ---");
|
||||||
|
m3_reset_n = 0;
|
||||||
|
m3_src_valid = 0;
|
||||||
|
m3_dst_ready = 0;
|
||||||
|
#200;
|
||||||
|
m3_reset_n = 1;
|
||||||
|
repeat (5) @(posedge m3_src_clk); #1;
|
||||||
|
|
||||||
|
// Port connectivity: outputs should not be X or Z after reset
|
||||||
|
check(m3_src_ready !== 1'bx, "M3: src_ready is not X after reset");
|
||||||
|
check(m3_src_ready !== 1'bz, "M3: src_ready is not Z after reset");
|
||||||
|
check(m3_dst_valid !== 1'bx, "M3: dst_valid is not X after reset");
|
||||||
|
check(m3_dst_valid !== 1'bz, "M3: dst_valid is not Z after reset");
|
||||||
|
check(m3_dst_data !== 32'hxxxxxxxx, "M3: dst_data is not X after reset");
|
||||||
|
check(m3_dst_data !== 32'hzzzzzzzz, "M3: dst_data is not Z after reset");
|
||||||
|
|
||||||
|
// Reset during active transfer: start a transfer, then assert reset mid-flight
|
||||||
|
m3_dst_ready = 1;
|
||||||
|
|
||||||
|
// Wait for src_ready
|
||||||
|
begin : c6_wait_ready
|
||||||
|
integer wait_cnt;
|
||||||
|
for (wait_cnt = 0; wait_cnt < 30; wait_cnt = wait_cnt + 1) begin
|
||||||
|
@(posedge m3_src_clk); #1;
|
||||||
|
if (m3_src_ready) disable c6_wait_ready;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
m3_src_data = 32'h12345678;
|
||||||
|
m3_src_valid = 1;
|
||||||
|
@(posedge m3_src_clk); #1;
|
||||||
|
m3_src_valid = 0;
|
||||||
|
|
||||||
|
// Wait a few cycles for transfer to be in-flight, then reset
|
||||||
|
repeat (3) @(posedge m3_dst_clk);
|
||||||
|
m3_reset_n = 0;
|
||||||
|
#200;
|
||||||
|
|
||||||
|
// Verify outputs are clean during reset
|
||||||
|
check(m3_dst_valid === 1'b0, "M3: dst_valid = 0 after mid-transfer reset");
|
||||||
|
|
||||||
|
// Release reset and verify recovery
|
||||||
|
m3_reset_n = 1;
|
||||||
|
@(posedge m3_src_clk);
|
||||||
|
m3_dst_ready = 1;
|
||||||
|
|
||||||
|
// Wait for src_ready to reassert (module recovered)
|
||||||
|
begin : c6_recovery_wait
|
||||||
|
integer wait_cnt;
|
||||||
|
reg recovered;
|
||||||
|
recovered = 0;
|
||||||
|
for (wait_cnt = 0; wait_cnt < 50; wait_cnt = wait_cnt + 1) begin
|
||||||
|
@(posedge m3_src_clk); #1;
|
||||||
|
if (m3_src_ready) begin
|
||||||
|
recovered = 1;
|
||||||
|
disable c6_recovery_wait;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
check(recovered, "M3: src_ready reasserts after mid-transfer reset recovery");
|
||||||
|
end
|
||||||
|
|
||||||
|
// Verify a new transfer works after recovery
|
||||||
|
m3_src_data = 32'hABCD0000;
|
||||||
|
m3_src_valid = 1;
|
||||||
|
@(posedge m3_src_clk); #1;
|
||||||
|
m3_src_valid = 0;
|
||||||
|
|
||||||
|
begin : c6_post_reset_xfer
|
||||||
|
integer wait_cnt;
|
||||||
|
for (wait_cnt = 0; wait_cnt < 30; wait_cnt = wait_cnt + 1) begin
|
||||||
|
@(posedge m3_dst_clk); #1;
|
||||||
|
if (m3_dst_valid) disable c6_post_reset_xfer;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
check(m3_dst_valid === 1'b1, "M3: dst_valid asserts for post-recovery transfer");
|
||||||
|
check(m3_dst_data === 32'hABCD0000, "M3: data 0xABCD0000 correct after reset recovery");
|
||||||
|
|
||||||
// ════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════
|
||||||
// Summary
|
// Summary
|
||||||
// ════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════
|
||||||
|
|||||||
@@ -136,11 +136,13 @@ module tb_latency_buffer;
|
|||||||
end
|
end
|
||||||
|
|
||||||
$display(" First valid output at input sample #%0d (expected ~%0d)",
|
$display(" First valid output at input sample #%0d (expected ~%0d)",
|
||||||
first_valid_cycle, LATENCY);
|
first_valid_cycle, LATENCY + 1);
|
||||||
// After LATENCY samples written, buffer_has_data goes high.
|
// After LATENCY samples written, buffer_has_data goes high.
|
||||||
// On the NEXT valid_in, valid_out fires. So first valid is at sample LATENCY.
|
// On the NEXT valid_in, valid_out_reg fires. Then valid_out_pipe
|
||||||
check(first_valid_cycle == LATENCY,
|
// (the actual output) fires one cycle later due to BRAM read register.
|
||||||
"First valid output appears at sample LATENCY");
|
// So first valid is at sample LATENCY + 1.
|
||||||
|
check(first_valid_cycle == LATENCY + 1,
|
||||||
|
"First valid output appears at sample LATENCY+1 (BRAM read pipeline)");
|
||||||
|
|
||||||
// ════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════
|
||||||
// TEST GROUP 4: Data integrity (exact delay)
|
// TEST GROUP 4: Data integrity (exact delay)
|
||||||
@@ -151,20 +153,10 @@ module tb_latency_buffer;
|
|||||||
// Feed samples: value = (i + 100)
|
// Feed samples: value = (i + 100)
|
||||||
// After priming, each valid output should match data_in from LATENCY samples ago.
|
// After priming, each valid output should match data_in from LATENCY samples ago.
|
||||||
//
|
//
|
||||||
// NOTE: The DUT calculates read_ptr from write_ptr BEFORE write_ptr is
|
// The DUT calculates read_ptr from write_ptr, with BRAM read output
|
||||||
// updated on this cycle. Specifically, the read_ptr is set using the
|
// registered for Block RAM inference. This adds 1 cycle of read latency
|
||||||
// current write_ptr value, which points to where the CURRENT sample
|
// beyond the LATENCY parameter. The valid_out pipeline stage tracks this.
|
||||||
// is about to be written. The BRAM read is combinational
|
// The auto-calibration below handles any offset empirically.
|
||||||
// (data_out = bram[read_ptr]).
|
|
||||||
//
|
|
||||||
// When buffer_has_data && valid_in:
|
|
||||||
// read_ptr <= write_ptr - LATENCY (mod 4096)
|
|
||||||
// But write_ptr hasn't incremented yet this cycle. So read_ptr will
|
|
||||||
// point to (old_write_ptr - LATENCY). The output appears one cycle later
|
|
||||||
// because read_ptr is registered, and BRAM read is combinational on read_ptr.
|
|
||||||
//
|
|
||||||
// Net effect: output at input cycle K has value of input cycle (K - LATENCY - 1).
|
|
||||||
// We verify this empirically.
|
|
||||||
|
|
||||||
begin : data_check_block
|
begin : data_check_block
|
||||||
reg all_match;
|
reg all_match;
|
||||||
@@ -242,15 +234,17 @@ module tb_latency_buffer;
|
|||||||
@(posedge clk); #1;
|
@(posedge clk); #1;
|
||||||
end
|
end
|
||||||
|
|
||||||
// Now de-assert valid_in — no more outputs expected
|
// Now de-assert valid_in — after pipeline drains (1 cycle), no more outputs
|
||||||
valid_in = 0;
|
valid_in = 0;
|
||||||
data_in = 32'hDEADBEEF;
|
data_in = 32'hDEADBEEF;
|
||||||
|
// Allow 1 cycle for the valid pipeline to drain
|
||||||
|
@(posedge clk); #1;
|
||||||
valid_output_count = 0;
|
valid_output_count = 0;
|
||||||
for (i = 0; i < 20; i = i + 1) begin
|
for (i = 0; i < 20; i = i + 1) begin
|
||||||
@(posedge clk); #1;
|
@(posedge clk); #1;
|
||||||
if (valid_out) valid_output_count = valid_output_count + 1;
|
if (valid_out) valid_output_count = valid_output_count + 1;
|
||||||
end
|
end
|
||||||
check(valid_output_count == 0, "No output when valid_in deasserted");
|
check(valid_output_count == 0, "No output when valid_in deasserted (after pipeline drain)");
|
||||||
|
|
||||||
// ════════════════════════════════════════════════════════
|
// ════════════════════════════════════════════════════════
|
||||||
// TEST GROUP 6: Intermittent valid_in
|
// TEST GROUP 6: Intermittent valid_in
|
||||||
@@ -349,6 +343,10 @@ module tb_latency_buffer;
|
|||||||
valid_in = 1;
|
valid_in = 1;
|
||||||
@(posedge clk); #1;
|
@(posedge clk); #1;
|
||||||
end
|
end
|
||||||
|
// Feed one more cycle to ensure pipeline has flushed
|
||||||
|
data_in = 32'hFFFF;
|
||||||
|
valid_in = 1;
|
||||||
|
@(posedge clk); #1;
|
||||||
// Should be producing outputs now
|
// Should be producing outputs now
|
||||||
check(valid_out === 1'b1, "Outputs flowing before mid-op reset");
|
check(valid_out === 1'b1, "Outputs flowing before mid-op reset");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user