Outline

DSIL Design Notes

Why commit c8ae7ad avoids value wrap and keeps the Rx I/Q channels balanced, traced from new40kHz4.v and its parent. | 依 new40kHz4.v 與其上一版,說明 commit c8ae7ad 為何能避免數值翻轉(wrap),同時維持 Rx I/Q 兩通道平衡。

commit c8ae7adnew40kHz4.v2026-07-08

1. Summary | 摘要

The Rx image reject needs Q to be 1.03× I. The parent commit got there by amplifying Q by 1.03; commit c8ae7ad (c8ae7adcc4303f2aea6df1e43753075b590e02f1) attenuates I by 1/1.03 instead. Both give the same ratio Q/I = 1.03, so the balance is unchanged. But a gain below 1 can never push a 14-bit sample past its range, while ×1.03 turns full scale into ±8437, which wraps to the opposite sign.

Rx 鏡像抑制需要 Q 是 I 的 1.03 倍。上一版的做法是把 Q 放大 1.03 倍;commit c8ae7ad 改成把 I 縮小 為 1/1.03。兩者的比值 Q/I 都是 1.03,所以平衡不變。但小於 1 的增益永遠不會讓 14 位元取樣超出範圍;×1.03 則會把滿刻度變成 ±8437,翻轉成相反的正負號。

2. The change | 改了什麼

Only the Rx ADC input stage of new40kHz4.v changed.

只改了 new40kHz4.v 的 Rx ADC 輸入級。

2.1 Before (c8ae7ad^): I × 1, Q × 1.03 in floating point | 改動前:I × 1,Q × 1.03(浮點)

new40kHz4.vc8ae7ad^
328	assign Rx_Q_ = {~ADC_DA[13], ADC_DA[12:0]};  //Q Channel for Rx
329	assign Rx_I_ = {~ADC_DB[13], ADC_DB[12:0]};  //I Channel for Rx

342	Convert_b2FP Converter_Rx_I_ADC (
344		.dataa(Rx_I_),                              // I x 1
347	MULT Multiplier_Rx_ADC (
349		.dataa(Rx_Q__FP),
350		.datab(32'h3f83d70a),                       // Q x 1.03

2.2 After (c8ae7ad): I × 1/1.03 in fixed point, Q × 1 | 改動後:I × 1/1.03(定點),Q × 1

new40kHz4.vc8ae7ad
335	wire signed [31:0] Rx_I_scaled = Rx_I_ * 18'sd63627;   // 63627/65536 = 0.970871
336	wire signed [13:0] Rx_I_bal    = Rx_I_scaled >>> 16;

349	Convert_b2FP Converter_Rx_I_ADC (
351		.dataa(Rx_I_bal),                           // I x 0.970871
354	MULT Multiplier_Rx_ADC (
356		.dataa(Rx_Q__FP),
357		.datab(32'h3f800000),                       // Q x 1.0, kept to preserve pipeline latency

3. Why the balance is maintained | 為何平衡得以維持

The image-reject sum is Rx = Rx_I + Rx_Q_delay, where Q has been delayed by 90°. The image cancels only when the two legs have the same amplitude. The analog front end makes Q about 3% weaker than I, so the digital gains must satisfy gain_Q / gain_I = 1.03.

鏡像抑制的和是 Rx = Rx_I + Rx_Q_delay,其中 Q 已延遲 90°。只有兩路振幅相同時鏡像才會抵消。類比前端讓 Q 比 I 弱約 3%,所以數位增益必須滿足 gain_Q / gain_I = 1.03。

With exact quadrature, the image left over relative to the wanted signal is |1 − r| / (1 + r), where r = (gain_Q · h_Q) / (gain_I · h_I) and h_I, h_Q are the analog gains. Only the ratio r appears, so multiplying both channels by the same constant changes nothing.

在正交完全精確時,殘留鏡像相對於有用訊號是 |1 − r| / (1 + r),其中 r = (gain_Q · h_Q) / (gain_I · h_I),h_I、h_Q 是類比增益。式中只出現比值 r,所以兩通道同乘一個常數不會改變任何結果。

gain_Igain_Qgain_Q / gain_I
Before | 改動前11.031.030000
After | 改動後63627/65536 = 0.97087111.030003
Bottom line | 結論

The commit multiplies both channels by the same factor, 1/1.03. The ratio, and with it the image rejection, stays the same to within 3 ppm (the rounding of K = 63627). The only side effect is that the overall signal is 2.9% (0.26 dB) smaller.

這個 commit 等於把兩通道同乘 1/1.03。比值以及鏡像抑制都維持不變,差異在 3 ppm 以內(來自 K = 63627 的捨入)。唯一的副作用是整體訊號小了 2.9%(0.26 dB)。

4. Why it avoids wrap | 為何能避免翻轉

The ADC samples are 14-bit two's complement, −8192 … +8191.

ADC 取樣是 14 位元二補數,範圍 −8192 … +8191。

  • Before: ×1.03 makes the output larger than the input. A full-scale sample becomes +8191 × 1.03 = +8436.7, or −8192 × 1.03 = −8437.8, which is outside the 14-bit range. Whenever that value is converted back into a 14-bit integer, it doesn't clip; it wraps to the opposite sign. +8436 becomes −7948, a full-scale spike in the wrong direction. ×1.03 is only safe while the input stays below 8191/1.03 ≈ 7952 (97% of full scale).
    改動前:×1.03 讓輸出大於輸入。滿刻度取樣變成 +8191 × 1.03 = +8436.7 或 −8192 × 1.03 = −8437.8,超出 14 位元範圍。只要這個值被轉回 14 位元整數,它不會被截在上限,而是翻轉成相反正負號:+8436 變成 −7948,一個方向相反的滿刻度尖峰。×1.03 只有在輸入低於 8191/1.03 ≈ 7952(滿刻度的 97%)時才安全。
  • After: ×0.9709 always makes the output smaller in magnitude than the input: |K·x| ≤ |x| ≤ 8192 for every possible input. Full scale gives +7952 or −7954, which always fit in 14 bits. No input level can overflow, and no downstream stage needs headroom.
    改動後:×0.9709 永遠讓輸出的絕對值小於輸入:對任何輸入都有 |K·x| ≤ |x| ≤ 8192。滿刻度得到 +7952 或 −7954,一定放得進 14 位元。任何輸入都不會溢位,下游也不需要預留餘量。
Full-scale input | 滿刻度輸入Before: Q × 1.03 | 改動前After: I × 0.970871 | 改動後
+8191+8436.7 → −7948 (wrap)+7952
−8192−8437.8 → +7947 (wrap)−7954
|I + Q| in a 15-bit sum (limit 16383) | 15 位元的 I + Q 和(上限 16383)8192 + 8437 = 166297954 + 8192 = 16146
14-bit limit +8191 ADC input 8191 Q × 1.03 (before) 8437 → wraps to −7948 I × 0.9709 (after) 7952
A full-scale sample after each gain. Only the ×1.03 path crosses the 14-bit limit. | 滿刻度取樣經過兩種增益後的值。只有 ×1.03 會越過 14 位元上限。

5. Where the wrap could actually happen in this commit | 在這個 commit 裡翻轉實際會發生在哪裡

In the parent commit, the ×1.03 was an IEEE-754 floating-point multiply, and floating point doesn't wrap by itself. The risk was at the points where values return to 14-bit integers. The parent file contains exactly that path, commented out:

上一版的 ×1.03 是 IEEE-754 浮點乘法,浮點本身不會翻轉。風險在數值轉回 14 位元整數的地方。上一版的檔案裡正好有這條路徑,只是被註解掉:

new40kHz4.vc8ae7ad^
353//	Convert_FP2b Converter_Rx_ADC_b (.dataa(Rx_Q__Amped_FP), .result(Rx_Q_Amped));  // FP -> 14-bit
315//	wire signed [13:0] Rx_I, Rx_Q;
372//	assign Rx = Rx_I + Rx_Q_delay;                                                  // integer sum

That 14-bit Rx_Q_Amped path is where Q × 1.03 would wrap. Moving the balance to a gain below 1 on I removes the risk on every path at once, whether it stays floating point or returns to integers.

Q × 1.03 會翻轉的地方就是這條 14 位元的 Rx_Q_Amped 路徑。把平衡改成在 I 上乘小於 1 的增益,一次消除了所有路徑的風險,不論數值留在浮點還是轉回整數。

That's why it mattered later. On 2026-09-04 the whole Rx chain was converted to fixed point, and this exact I × 63627 >>> 16 code was carried into adc_iface.v (lines 36–41). Today the 15-bit I + Q90 sum in _1Mhz_beta010.v fits only because neither channel is ever amplified.

這也是它後來變得重要的原因。2026-09-04 整條 Rx 鏈改成定點,這段 I × 63627 >>> 16 原封不動搬進了 adc_iface.v(第 36–41 行)。今天 _1Mhz_beta010.v 裡 15 位元的 I + Q90 和之所以放得下,正是因為兩通道都沒有被放大。

6. Side effects | 副作用

  • Latency is unchanged. The I multiply is combinational, and the Q MULT was left in place at ×1.0 instead of being removed. Removing it would have shortened the Q pipeline and broken the Rx_I_d1/d2/ctrl vs Rx_Q_delay alignment.
    延遲不變。I 的乘法是組合邏輯,Q 的 MULT 則保留為 ×1.0 而沒有移除。若移除,Q 的管線會變短,破壞 Rx_I_d1/d2/ctrl 與 Rx_Q_delay 的對齊。
  • Truncation offset. >>> 16 rounds toward −∞, so I gets an average offset of −½ LSB, which is negligible next to ADC noise.
    截位偏移。>>> 16 向 −∞ 捨入,所以 I 有 −½ LSB 的平均偏移,相對 ADC 雜訊可忽略。
Same rule on Tx | Tx 端同一原則

The commit message says it "mirrors the Tx amplitude-balance change". The Tx side follows the same rule: balance by attenuating the stronger channel (Q × 1/1.022), never by amplifying the weaker one.

commit 訊息說它「對應 Tx 端的振幅平衡修改」。Tx 端遵循同一原則:靠縮小較強的通道來平衡(Q × 1/1.022),而不是放大較弱的通道。