Fix ddc_input_interface 18->16 bit overflow: add saturation at positive full scale

Bug: rounding logic 'adc_i <= ddc_i[17:2] + ddc_i[1]' overflows when
ddc_i[17:2]=0x7FFF and ddc_i[1]=1, causing 0x7FFF+1=0x8000 (sign flip
from max positive to most negative value).

Fix: add explicit saturation — clamp to 0x7FFF when truncated value is
max positive and round bit is set. Negative values cannot overflow since
rounding only moves toward zero.

New testbench: tb_ddc_input_interface.v with 26 tests covering rounding,
truncation, overflow saturation at positive boundary, negative full scale,
valid synchronization, and sync error detection.
This commit is contained in:
Jason
2026-03-16 18:14:06 +02:00
parent 17731dd482
commit a5a5e96a57
2 changed files with 393 additions and 8 deletions
+20 -8
View File
@@ -39,14 +39,26 @@ always @(posedge clk or negedge reset_n) begin
end
end
// Scale 18-bit to 16-bit with rounding
// Option: Keep most significant 16 bits with rounding
always @(posedge clk) begin
if (valid_sync) begin
// Round to nearest: add 0.5 LSB before truncation
adc_i <= ddc_i[17:2] + ddc_i[1]; // Rounding
adc_q <= ddc_q[17:2] + ddc_q[1]; // Rounding
end
// Scale 18-bit to 16-bit with convergent rounding + saturation
// ddc_i[17:2] extracts the upper 16 bits; ddc_i[1] is the rounding bit.
// Without saturation, 0x7FFF + 1 = 0x8000 (sign flip at positive full scale).
// Fix: saturate to 0x7FFF when rounding would overflow a positive value.
// Negative values cannot overflow: the most negative 18-bit value (-131072)
// truncates to -8192 (0x8000 as 16-bit) and rounding only moves toward zero.
wire [15:0] trunc_i = ddc_i[17:2];
wire [15:0] trunc_q = ddc_q[17:2];
wire round_i = ddc_i[1];
wire round_q = ddc_q[1];
// Overflow occurs only when truncated value is max positive AND round bit set
wire sat_i = (trunc_i == 16'h7FFF) & round_i;
wire sat_q = (trunc_q == 16'h7FFF) & round_q;
always @(posedge clk) begin
if (valid_sync) begin
adc_i <= sat_i ? 16'sh7FFF : (trunc_i + {15'b0, round_i});
adc_q <= sat_q ? 16'sh7FFF : (trunc_q + {15'b0, round_q});
end
end
// Error detection