Contents

40 kHz Radar IQ Signal Processing System

Module Reference Guide — _1Mhz_beta010.v (Cyclone V, Quartus Standard)

Verilog Cyclone V Quartus Standard 10 MHz clock 40 kHz resonance

1. System Overview系統總覽

Theory ref — Yu & Horng §0, §3 (Fig. 2): This whole pipeline is the all-digital realization of Yu's phase-canceling feedback demodulation loop. The PI loop pins the round-trip phase at the anti-phase point θ=π; the delay value itself is the radar output, x̂(t) = −½·c·[d(t) − a]. [Yu guide]
理論對照 — Yu & Horng §0、§3(Fig. 2): 整條流水線就是 Yu 的相位消除回授解調迴路的全數位實現。PI 迴路把來回相位釘在反相點 θ=π;延遲量本身就是雷達輸出,x̂(t) = −½·c·[d(t) − a][Yu 導讀]

The _1Mhz_beta010 design implements a 40 kHz IQ radar signal processor on a Cyclone V FPGA. A relay feedback oscillator generates a stable 40 kHz reference waveform. A PI phase-lock loop steers the oscillator phase via variable-delay injection. Quadrature transmit waveforms drive two DAC channels (90° apart). On receive, dual ADC inputs are processed through image rejection and clutter cancellation before peak detection and UART reporting.

_1Mhz_beta010 設計在 Cyclone V FPGA 上實作一個 40 kHz IQ 雷達訊號處理器。繼電回授振盪器產生穩定的 40 kHz 參考波形;PI 相位鎖定迴路透過可變延遲注入來調控振盪器相位;正交發射波形驅動兩個相差 90° 的 DAC 通道。接收端則由雙 ADC 輸入經影像抑制與雜波消除處理後,進行峰值偵測並由 UART 回報。

Module Relationships模組關係

Section章節Module(s)模組Produces產生Consumes使用Clock Domain時脈域
§2 40 kHz Oscillator40 kHz 振盪器 relay_feedback2 u, u_0d5, v u_inj (from §3) 10 MHz
§3 PI Phase-LockPI 相位鎖定 demodulator, PI_antiWindup, delay_line2 u_inj, pwm, data u, u_0d5 (§2); Rx_*_Amped_DB (§6) 10 MHz / 50 kHz / 50 MHz
§4 Tx DAC PathTx DAC 路徑 delay_62d5 (Tx), MULT, DAC_DA/DB DAC_DA[13:0], DAC_DB[13:0] v (§2) 10 MHz / 200 MHz
§5 IR Q-Channel Delay影像抑制 Q 通道延遲 hilbert_90 ×2 (Q→y_90, I→y_0), 15-bit adder (fixed point) Rx_Q_delay, Rx_IQ_Sum_B ADC_DA, ADC_DB 10 MHz
§6 CC & Amplitude雜波消除與振幅 clutter_cancel (amplitude_calc, div_fx, phase_detector, delay_line), smittch_trigger, peak_detector Rx_cc (Q24.8), Rx_*_Amped_Trig/DB, Rx_peak Rx_IQ_Sum_B (§5); v (§2) 10 MHz
§7 UART OutputUART 輸出 UART_A tx serial (230400 baud) data (§3), Rx_peak (§6) 50 MHz / 2.5 kHz

Full-System Signal Flow全系統信號流

── §4 Transmit Path ────────────────────────────────────────────────────────── ── §2 / §3 Phase-Lock Loop ────────────────────────────────────────────────── ── §5 / §6 Receive Path ───────────────────────────────────────────────────── relay_feedback2 40 kHz osc §2 u, u_0d5, v v [14b] delay_62d5 62.5 cyc = 90° b→FP→MULT×1.022 →FP→b→MSB flip DAC_DA I: sin 40kHz §4 → Tx MSB flip + d1→d3 align DAC_DB Q: cos 40kHz u,u_0d5 demodulator u × u12 → LPF² x256 (phase err) x256 PI_antiWindup kI=3/256 @50kHz kP=1/2¹⁵ LPF→data §3 d=PI_out delay_line2 128K RAM circ buf ← u_inj phase-correction feedback ← PWM data → §7 UART ADC_DA ADC_DB Image Rejection hilbert_90 ×2 + 15-bit add Rx = I + Q_delay §5 Rx Clutter Cancel Rx·ratio − Tx_Q (Q24.8) §6 Rx_cc Schmitt ×128 (SH=7) §6 peak_detector Rx_peak §6 Rx_peak UART_A §7 230400 baud 40-bit packets → tx pin → host PC amplitude_calc → div_fx (Q8.16) → KEY3 latch → Rx scaler §6 Rx_*_Amped_DB → uin2 mux → uin3 → §3 PI

2. 40 kHz Relay Feedback Oscillator — relay_feedback240 kHz 繼電回授振盪器 — relay_feedback2

Theory ref — Yu & Horng §1 (SIL), §3: relay_feedback2 is the SIL oscillator running at the 40 kHz IF (in Wu's design the SIL/Adler locking is digital; 5.8 GHz is only the over-the-air carrier). uinj is the injection-locking input — generalized Adler Δω = (ω/2Q)·η·sinθ/(1+η·cosθ), with η=0.5, Q=25. The relay / comparator element is exactly what Yu notes "makes an all-digital FPGA implementation possible." [Yu guide]
理論對照 — Yu & Horng §1(SIL)、§3: relay_feedback2 就是跑在 40 kHz 中頻的 SIL 振盪器(在 Wu 的設計中,SIL/Adler 鎖定是數位的;5.8 GHz 只是空中傳輸的載波)。uinj 是注入鎖定輸入——廣義 Adler 方程 Δω = (ω/2Q)·η·sinθ/(1+η·cosθ),其中 η=0.5Q=25。繼電器/比較器元件正是 Yu 所說「讓全數位 FPGA 實作成為可能」的關鍵。[Yu 導讀]

Purpose用途

relay_feedback2 is the core 40 kHz oscillator of the radar IQ signal processing system. It implements a relay feedback oscillator — a nonlinear control loop where a relay (sign function) element forces sustained sinusoidal-like oscillation at the natural resonant frequency of the embedded digital filter.

relay_feedback2 是整個雷達 IQ 訊號處理系統的核心 40 kHz 振盪器。它實作一個繼電回授振盪器——一種非線性控制迴路:由繼電器(符號函數)元件強迫系統在內嵌數位濾波器的自然諧振頻率上維持類似弦波的持續振盪。

The oscillation frequency is determined by the integrator gain coefficient 332/1024 ≈ 0.3242, chosen so that at a 10 MHz clock rate the closed-loop natural frequency lands at 40 kHz.

振盪頻率由積分器增益係數 332/1024 ≈ 0.3242 決定;此值的選擇使得在 10 MHz 時脈下,閉迴路自然頻率正好落在 40 kHz

Note: The comment on line 45 explicitly records the change from the original 306/1024 coefficient (prior frequency) to 332/1024 to achieve 40 kHz resonance.
注意: 第 45 行的註解明確記錄了從原本的 306/1024 係數(先前的頻率)改為 332/1024,以達到 40 kHz 諧振。

Port List埠列表

PortDir方向Width位寬Description說明
clk10MHzinput1 10 MHz master clock. F(z) updates on negedge; G(z) on posedge.10 MHz 主時脈。F(z) 在 negedge 更新;G(z) 在 posedge 更新。
uinjinput3 Injection signal from PI controller. Adds phase correction to the oscillator. Sign-extended to 18 bits internally as y_in.來自 PI 控制器的注入訊號。為振盪器加入相位修正。內部符號延伸成 18 位元的 y_in
uoutput13 (signed) Main oscillation output — f1024[23:11] (divided by 2). Feeds the demodulator.主振盪輸出——f1024[23:11](除以 2)。餵入解調器。
u_0d5output13 (signed) Half-period delayed version of u — average of current and previous sample. Used for the 12-step delay chain in top-level demodulation.u 的半週期延遲版——目前與前一取樣的平均。用於頂層解調的 12 級延遲鏈。
voutput14 (signed) Full-resolution oscillation — f1024[23:10]. Becomes the Tx baseband Q signal sent to the DAC.全解析度振盪——f1024[23:10]。成為送往 DAC 的 Tx 基頻 Q 訊號。

Internal State Registers內部狀態暫存器

Register暫存器Width位寬Description說明
f102424-bit signed Bandpass filter state F(z). Updated on negedge clk10MHz. The oscillation waveform lives in bits [23:10].帶通濾波器狀態 F(z)。在 negedge clk10MHz 更新。振盪波形位於位元 [23:10]。
g102428-bit signed Integrator state G(z). Updated on posedge clk10MHz. Initial value 4'b0001,24'b0 to seed oscillation from rest.積分器狀態 G(z)。在 posedge clk10MHz 更新。初始值 4'b0001,24'b0,作為從靜止起振的種子。
e_1, e_218-bit signed each One- and two-cycle delayed error signals for the F(z) recursion.供 F(z) 遞迴使用的延遲一與兩個週期的誤差訊號。
u_past13-bit signed Previous-cycle value of u, used to compute the half-period delayed output u_0d5.u 的前一週期值,用來計算半週期延遲輸出 u_0d5
res_in18-bit signed Relay output (sign of f1024) plus injection y_in. The nonlinear element input to F(z).繼電器輸出(f1024 的符號)加上注入 y_in。進入 F(z) 的非線性元件輸入。

Architecture & Signal Flow架構與信號流

Block Diagram方塊圖

uinj[2:0] from PI_antiWindup (→ y_in) sign(f1024) relay output, from F(z) state (→ ±256) sign-extend + res_in (relay+injection) + e F(z) bandpass filter (z⁻⁰·⁵+z⁻¹·⁵)/(1−1023/1024·z⁻¹) F(s) ≈ (2/T)/(s+ωc), ωc = 1/(1024T) f1024 u f1024[23:11] 13b → demodulator v f1024[23:10] 14b → DAC (Tx Q) u_0d5 (u+u_past)/2 → delay chain G(z) integrator g1024[27:10] g1024 → e → F(z) g1024

Cross-Reference — F(z) ↔ Yu's SIL Oscillator交叉對照——F(z) ↔ Yu 的 SIL 振盪器

Theory ref — Yu & Horng §1 (SIL) & §3 (Fig. 2): The F(z) bandpass filter — together with the G(z) integrator and the relay (sign(f1024)) — is Wu's all-digital realization of the SIL oscillator that Yu & Horng describe. In Yu's paper the oscillator is itself a relay-feedback (comparator → square wave) SIL oscillator whose frequency is pulled by the reflected echo (self-injection locking, generalized Adler Δω = (ω/2Q)·η·sinθ/(1+η·cosθ)) — so this design uses the same relay approach, synthesized in the FPGA. The resonator's selectivity (quality factor) is realized here by the 1/1024 leak (pole at 1023/1024) — the digital stand-in for the oscillator's finite quality factor. [Yu §1] [Yu §3, Fig. 2]
理論對照 — Yu & Horng §1(SIL)與 §3(Fig. 2): F(z) 帶通濾波器——連同 G(z) 積分器與繼電器(sign(f1024))——就是 Wu 對 Yu & Horng 所述 SIL 振盪器的全數位實現。在 Yu 的論文中,振盪器本身就是一個繼電回授(比較器 → 方波)SIL 振盪器,其頻率被反射回波拉動(自我注入鎖定,廣義 Adler Δω = (ω/2Q)·η·sinθ/(1+η·cosθ))——所以本設計採用同一種繼電器手法,在 FPGA 中合成。諧振器的選擇性(品質因數)在這裡由 1/1024 洩漏(極點在 1023/1024)實現——這是振盪器有限品質因數的數位替身。[Yu §1] [Yu §3, Fig. 2]

Yu & Horng's paper figure — FPGA implementation of the SIL oscillatorYu & Horng 論文圖——SIL 振盪器的 FPGA 實作

The paper's canonical block diagram (redrawn). It is the same topology as relay_feedback2: G₁ = the F(z) bandpass, G₂ = the integrator, sign(·)→2⁸ = the relay (±256), and 2⁷·u_inj = the injection.

論文的標準方塊圖(重繪)。它與 relay_feedback2 是同一個拓撲:G₁=F(z) 帶通、G₂=積分器、sign(·)→2⁸=繼電器(±256)、2⁷·u_inj=注入。

Paper vs this RTL: same topology as relay_feedback2G₁ = F(z), G₂ = integrator, sign(·)→2⁸ = relay (±256), 2⁷·u_inj = injection. The constants differ slightly: the pole is drawn as 2045/2¹¹ here vs 1023/1024 (= 2046/2¹¹) in this design, and the injection scale is 2⁷ vs ×2⁶ in the RTL — but the structure is identical.
論文 vs 本 RTL: 拓撲與 relay_feedback2 相同——G₁=F(z)、G₂=積分器、sign(·)→2⁸=繼電器(±256)、2⁷·u_inj=注入。常數略有差異:這裡極點畫成 2045/2¹¹,本設計則是 1023/1024(= 2046/2¹¹);注入縮放是 2⁷,RTL 中則是 ×2⁶——但結構完全一致。

Two Diagrams Compared — This RTL vs Yu's Paper Figure兩張方塊圖的比較——本 RTL vs Yu 論文圖

The §2 block diagram (this design's relay_feedback2) and Yu & Horng's paper figure are the same oscillator. Here is what matches and what differs.

§2 的方塊圖(本設計的 relay_feedback2)與 Yu & Horng 的論文圖是同一顆振盪器。以下列出相同與相異之處。

What's the same (topology):

相同之處(拓撲):

Element元件Both diagrams兩張圖皆同
Oscillator type振盪器類型relay-feedback limit-cycle oscillator
Resonator — F(z) / G₁諧振器——F(z)/G₁bandpass, numerator (1+z⁻¹)², leaky pole near z = 1
Integrator — G(z) / G₂積分器——G(z)/G₂1/(1−z⁻¹), gain 166
Relay element繼電器元件sign(state) × 2⁸ = ±256
Summing node加總節點e = relay + injection − scaled integrator → into F(z)/G₁
Output u輸出 uresonator state scaled down (≈ ÷2¹⁰)
Clock時脈10 MHz

What differs (constants & detail):

相異之處(常數與細節):

Aspect面向This RTL (§2 diagram)本 RTL(§2 圖)Yu paper figureYu 論文圖
Leak / pole洩漏/極點1023/1024 (= 2046/2¹¹)2045/2¹¹
Injection scale注入縮放×2⁶ (×64), uinj 3-bit2⁷ (×128)
u word lengthu 字長f1024[23:11] = 13-bit14-bit (1/2¹⁰)
Numerator written as分子寫法(z⁻⁰·⁵+z⁻¹·⁵) half-step form(1+2z⁻¹+z⁻²) expanded
Integrator-feedback scaling積分器回授縮放g1024[27:10] subtracted (−1/2¹⁰ implied)explicit −1/2¹⁰ block
Clock detail時脈細節F(z) negedge / G(z) posedge interleave (→ z⁻⁰·⁵)single 10 MHz shown
Extra outputs shown額外輸出v (DAC, 14b), u_0d5 (delay chain)only u

Bottom line: identical structure (relay + injection + integrator feedback around a (1+z⁻¹)² bandpass); the differences are word-lengths and exact constants (notably the pole 2046 vs 2045 over 2¹¹, and the injection scale 2⁶ vs 2⁷), plus this RTL exposes extra taps (v, u_0d5) and the negedge/posedge half-sample trick.

一句話總結:結構完全相同(繼電器+注入+積分器回授,包在 (1+z⁻¹)² 帶通外面);差異只在位寬與精確常數(特別是極點 2046 vs 2045(除以 2¹¹),以及注入縮放 2⁶ vs 2⁷),另外本 RTL 多引出了幾個抽頭(v、u_0d5),並使用 negedge/posedge 的半取樣技巧。

Adler Equation — Model, Not RTLAdler 方程——是模型,不是 RTL

A common confusion: Wu's resonator does not implement or compute the Adler equation. The Adler equation is an analytical model of injection-locking behavior, not a building block.

常見的混淆:Wu 的諧振器並沒有實作或計算 Adler 方程。Adler 方程是注入鎖定行為的解析模型,不是一個電路方塊。

What the resonator actually is諧振器實際上是什麼

relay_feedback2 is a plain relay-feedback digital oscillator: a linear F(z) bandpass + G(z) integrator (sets 40 kHz & Q), a relay (sign(f1024)) that sustains the limit cycle, and an injection (uinj → y_in) that is simply added to the relay output. There is no sin, no cos, no Adler formula in the Verilog — only adds, bit-shifts, and the sign function.

relay_feedback2 就是一顆單純的繼電回授數位振盪器:線性的 F(z) 帶通+G(z) 積分器(設定 40 kHz 與 Q)、一個維持極限環的繼電器sign(f1024)),以及單純相加進繼電器輸出的注入uinj → y_in)。Verilog 裡沒有 sin、沒有 cos、沒有 Adler 公式——只有加法、位移與符號函數。

Where Adler actually livesAdler 真正存在的地方

The Adler equation Δω = (ω/2Q)·η·sinθ/(1+η·cosθ) describes how an injection-locked oscillator's frequency shifts with injection phase. In Wu's all-digital design, injection locking is realized physically by adding y_in into the relay loop; the frequency-pulling that emerges obeys the generalized Adler relation, but the hardware never evaluates it. Adler is used by Yu (and this guide's theory refs) only to analyze the loop — e.g. to derive the small-signal plant gain g(θ) ∝ cosθ near θ = π.

Adler 方程 Δω = (ω/2Q)·η·sinθ/(1+η·cosθ) 描述注入鎖定振盪器的頻率如何隨注入相位偏移。在 Wu 的全數位設計中,注入鎖定是把 y_in 加進繼電迴路而物理性地實現的;由此產生的頻率牽引遵循廣義 Adler 關係,但硬體從未計算它。Adler 只被 Yu(以及本導讀的理論對照)用來分析迴路——例如推導 θ = π 附近的小訊號受控體增益 g(θ) ∝ cosθ

Thing項目In relay_feedback2.v?在 relay_feedback2.v 裡嗎?What it is它是什麼
Resonator諧振器yes — F(z) + G(z) + relay linear digital oscillator setting 40 kHz & Q設定 40 kHz 與 Q 的線性數位振盪器
Injection locking注入鎖定yes — uinj added to relay (→ y_in) physically realizes the locking物理性地實現鎖定
Adler equationAdler 方程no — no sin/cos in RTL analytical model that describes the locking; Yu uses it for the plant model描述鎖定行為的解析模型;Yu 用它建立受控體模型

In one line: Wu didn't use the Adler equation to build the resonator — the relay-feedback loop produces injection-locking behavior that the Adler equation merely describes. Adler is theory/analysis (Yu's plant model), not RTL.

一句話:Wu 並不是用 Adler 方程來建造諧振器——繼電回授迴路產生的注入鎖定行為,只是被 Adler 方程描述而已。Adler 屬於理論/分析(Yu 的受控體模型),不是 RTL。

Adler: analysis vs RTL — Yu vs WuAdler:分析 vs RTL——Yu vs Wu

The full 2×2 picture:

完整的 2×2 對照:

Adler in analysis / design?分析/設計中用 Adler?Adler computed in RTL?RTL 中計算 Adler?
Yu ✅ yes — nonlinearity (§2), plant g(θ) ∝ cosθ (§4) ❌ no
Wu ✅ yes — reuses Yu's Adler plant / operating-point framework ❌ no

Both have an RTL — Wu's relay_feedback2 and Yu's "FPGA implementation of the SIL oscillator" figure — and both are relay-feedback loops (F(z)/G(z) + sign() relay + added injection) with no sin/cos/Adler in the gates. Adler lives in the control-design math (both use it there); the RTL embodies the injection-locking behavior without ever computing it.

兩者都有 RTL —— Wu 的 relay_feedback2 與 Yu 論文中的「SIL 振盪器 FPGA 實作」圖 —— 而且兩者都是 繼電器回授迴路(F(z)/G(z) + sign() 繼電器 + 相加的注入), 閘級電路裡沒有 sin/cos/Adler。Adler 存在於控制設計的數學中(兩者都在此使用它);RTL 體現了注入鎖定的行為, 卻從未真正計算它。

Yu (theory) ↔ Wu (this design) mappingYu(理論)↔ Wu(本設計)對照

Concept概念Yu & Horng (ultrasonic)Yu & Horng(超音波)Wu / this design (5.8 GHz DSIL, FPGA)Wu/本設計(5.8 GHz DSIL,FPGA)
Resonant oscillator諧振振盪器 SIL cavity — "Oscillator u" block (§3, Fig. 2) relay_feedback2: F(z) bandpass + G(z) integrator + relay
Quality factor Q品質因數 Q physical cavity Q = 25 (§1) set by the 1/1024 leak (pole at 1023/1024) → Q ≈ 13
Injection locking注入鎖定 echo re-injected → Adler Δω = (ω/2Q)·η·sinθ/(1+η·cosθ) uinj (3-bit) → y_in added into res_in each cycle
Oscillator / waveform振盪器/波形 relay-feedback square wave (comparator → square); u_out, u_inj square, base B = 4/π·A_inj same relay-feedback square wave, realized digitally (relay_feedback2) at the 40 kHz IF — the 4/π factor cancels in B/A
Note — relay feedback is shared, not a difference: Both Yu's ultrasonic radar and this design use a relay-feedback (comparator) oscillator producing a square wave — Yu notes the relay element is exactly what "makes an all-digital FPGA implementation possible," which is what relay_feedback2 does. The sinusoidal carrier appears only in Wu's 5.8 GHz EM thesis as the analog RF carrier; in this FPGA design the 5.8 GHz is just an external up-conversion of the 40 kHz IF, while the SIL/Adler locking itself is the digital square-wave relay loop. [Yu §4 — 方波 vs 弦波]
注意——繼電回授是共同點,不是差異: Yu 的超音波雷達本設計都使用繼電回授(比較器)振盪器產生方波——Yu 指出繼電器元件正是「讓全數位 FPGA 實作成為可能」的關鍵,而這正是 relay_feedback2 做的事。弦波載波只出現在 Wu 的 5.8 GHz 電磁波論文中,作為類比射頻載波;在本 FPGA 設計裡,5.8 GHz 只是 40 kHz 中頻的外部升頻,SIL/Adler 鎖定本身就是數位方波繼電迴路。[Yu §4 — 方波 vs 弦波]
⚠ Don't confuse the two "F"s: Yu's guide also uses the symbol F(s), but for a different block — the demodulation low-pass filter in the small-signal plant P(s) = g(θ)·e−sT/8·F(s) (Yu §4, eq. 6 / A32). That F(s) belongs to the receive/demod chain (≈ this design's demodulator + PI LPF), not the oscillator. The F(z) on this page is the oscillator resonator and maps to Yu §1/§3, not Yu's §4 F(s). [Yu §4]
⚠ 別把兩個「F」搞混: Yu 的導讀也使用符號 F(s),但指的是不同的方塊——小訊號受控體 P(s) = g(θ)·e−sT/8·F(s)(Yu §4,式 6/A32)中的解調低通濾波器。那個 F(s) 屬於接收/解調鏈(≈ 本設計的 demodulator+PI 低通),不是振盪器。本頁的 F(z)振盪器諧振器,對應 Yu §1/§3,而非 Yu §4 的 F(s)[Yu §4]
Z-Domain Primer — How to Read These Formulasz 域入門——這些公式怎麼讀 (click to expand)

Every filter in this design is written as a z-domain transfer function (F(z), G(z), …). If that notation is unfamiliar, here is the minimum needed to read — and derive — them.

本設計的每個濾波器都寫成 z 域轉移函數F(z)G(z)…)。若不熟悉這種記法,以下是讀懂——並自行推導——它們所需的最少知識。

1. The one rule that matters: z⁻¹ = one-sample delay1. 唯一要緊的規則:z⁻¹=延遲一個取樣

z⁻¹ means "delay by one clock sample." In this design one sample = one clk10MHz tick = T = 100 ns. The dictionary:

z⁻¹ 的意思是「延遲一個時脈取樣」。在本設計中,一個取樣=一個 clk10MHz 週期=T = 100 ns。對照字典如下:

Time domain時域Z domainz 域Meaning意義
x[n]X(z)the signal now現在的訊號
x[n−1]z⁻¹·X(z)delayed 1 sample (1 register)延遲 1 個取樣(1 個暫存器)
x[n−k]z⁻ᵏ·X(z)delayed k samples (k registers)延遲 k 個取樣(k 個暫存器)
a·x[n] + b·y[n]a·X(z) + b·Y(z)linear: scale & add carry over線性:縮放與相加照樣成立

So a chain of D flip-flops is multiplication by z⁻¹ per stage — e.g. the e_1/e_2 registers are z⁻¹ and z⁻² taps of e.

所以一串 D 正反器就是每級乘上一個 z⁻¹——例如 e_1e_2 暫存器就是 ez⁻¹z⁻² 抽頭。

2. Difference equation ⇄ transfer function2. 差分方程 ⇄ 轉移函數

To turn RTL/recursion into a transfer function: replace each x[n−k] with z⁻ᵏ·X, then collect and divide. Worked on a simple leaky integrator y[n] = y[n−1] + x[n]:

要把 RTL/遞迴式轉成轉移函數:把每個 x[n−k] 換成 z⁻ᵏ·X,然後整理、相除。以簡單的累加器 y[n] = y[n−1] + x[n] 為例(推導見上式)。

y[n] = y[n-1] + x[n] Y = z⁻¹·Y + X (substitute: y[n-1] → z⁻¹·Y) Y − z⁻¹·Y = X (move the Y term to the left) Y·(1 − z⁻¹) = X (factor out Y) H(z) = Y/X = 1 / (1 − z⁻¹) (divide both sides by 1 − z⁻¹)

The middle two lines are ordinary algebra — treat z⁻¹ as a constant multiplier (like a in y = a·y + x → y(1 − a) = x). Subtract z⁻¹·Y from both sides, then factor Y out of Y − z⁻¹·Y.

中間兩行只是普通代數——把 z⁻¹ 當成常數乘子(就像 y = a·y + x → y(1 − a) = x 中的 a)。兩邊同減 z⁻¹·Y,再從 Y − z⁻¹·Y 提出 Y

That 1/(1−z⁻¹) is the pure accumulator/integrator — exactly the shape of G(z) here.

那個 1/(1−z⁻¹) 就是純累加器/積分器——正是這裡 G(z) 的形狀。

Why 1/(1−z⁻¹) accumulates — and how it differs from 1/z為什麼 1/(1−z⁻¹) 會累加——它與 1/z 有何不同

It is the transfer function of y[n] = y[n−1] + x[n]: each output adds the input onto the running total, so it sums all past inputs. Three ways to see it:

它是 y[n] = y[n−1] + x[n] 的轉移函數:每個輸出都把輸入加到累計總和上,因此它加總所有過去的輸入。可以從三個角度看:

1/z is a completely different thing — it is just z⁻¹, a pure one-sample delay (y[n] = x[n−1]): it shifts the signal one sample later and passes it through unchanged. It sums nothing.

1/z 是完全不同的東西——它就是 z⁻¹,一個純粹的單取樣延遲y[n] = x[n−1]):把訊號往後移一個取樣、原封不動地通過。它不做任何累加。

1/z = z⁻¹1/(1 − z⁻¹)
Does what做什麼delay by 1 sampleaccumulate all inputs
Equation方程式y[n] = x[n−1]y[n] = y[n−1] + x[n]
Impulse response脈衝響應one spike: 0,1,0,0,…step (all ones): 1,1,1,1,…
Structure結構feed-forward, no feedback (FIR)feedback loop (IIR)
Pole / zero極點/零點zero at z = 0 (just a delay)pole at z = 1
Hardware硬體one registeradder + register fed back

The crux: z⁻¹ alone = a delay; z⁻¹ in the denominator = feedback, and that feedback is what turns a delay into accumulation. Mirror image: the numerator (1 − z⁻¹) by itself is a differencer y[n] = x[n] − x[n−1] (discrete derivative, zero at z = 1) — the inverse of the accumulator.

重點:z⁻¹ 單獨出現=延遲;z⁻¹ 出現在分母=回授,而正是這個回授把延遲變成累加。鏡像對應:分子上的 (1 − z⁻¹) 本身是一個差分器 y[n] = x[n] − x[n−1](離散微分,零點在 z = 1)——累加器的反運算。

Impulse response of each (feed in a single 1 at n=0, watch the output):

兩者的脈衝響應(在 n=0 餵入單一個 1,觀察輸出):

1/z = z⁻¹ (delay) 1/(1−z⁻¹) (accumulator) impulse response h[n] — input = unit impulse δ[n] 1 0 0 1 2 3 4 5 6 7 one spike at n=1 — input passed through, delayed 1 0 0 1 2 3 4 5 6 7 stays at 1 forever — every input is summed and kept

Same input (a single 1 at n=0): the delay just relocates it to n=1; the accumulator adds it to a running total that never decreases. A leaky integrator (like F(z)'s 1/1024 pole) would instead decay slowly back toward 0 rather than holding flat at 1.

相同的輸入(n=0 的單一個 1):延遲只是把它搬到 n=1累加器把它加進永不下降的累計總和。有洩漏的積分器(像 F(z) 的 1/1024 極點)則會緩慢衰減回 0,而不是平平地停在 1。

3. What H(z) = Y(z)/X(z) tells you3. H(z) = Y(z)/X(z) 告訴你什麼

4. Poles, zeros & the unit circle (stability + Q)4. 極點、零點與單位圓(穩定度+Q)

Poles are the roots of the denominator. Plot them on the complex z-plane against the unit circle |z| = 1:

極點是分母的根。把它們畫在複數 z 平面上,對照單位圓 |z| = 1

Pole location極點位置Behavior行為
inside circle (|z| < 1)stable — response decays穩定——響應衰減
on circle (|z| = 1)marginal — oscillates forever (infinite Q)臨界——永遠振盪(無限 Q)
outside circle (|z| > 1)unstable — response blows up不穩定——響應發散
just inside (e.g. 1023/1024)lightly damped → high-Q resonator輕阻尼 → 高 Q 諧振器

The closer a pole sits to the unit circle, the less it decays per sample → higher Q, narrower bandwidth. That is exactly the role of the 1/1024 leak (pole at 1023/1024).

極點越靠近單位圓,每個取樣衰減得越少 → Q 越高、頻寬越窄。這正是 1/1024 洩漏(極點在 1023/1024)扮演的角色。

5. From z back to real frequency: z = e^{sT}5. 從 z 回到真實頻率:z = e^{sT}

The bridge between digital (z) and analog (s) is z = e^{sT}. Two everyday uses:

數位(z)與類比(s)之間的橋樑是 z = e^{sT}。兩個日常用法:

Worked example — this design's F(z): From the recursion f[n] = f[n-1] − f[n-1]/1024 + e[n] + 2e[n-1] + e[n-2], substitute z⁻ᵏ and collect: F·(1 − (1023/1024)z⁻¹) = E·(1 + z⁻¹)², so F(z) = (1+z⁻¹)² / (1 − (1023/1024)z⁻¹). Read it off: a pole at 1023/1024 (just inside the unit circle ⇒ high-Q resonator, corner ωc = 1/(1024T)) and a double zero at z = −1 (the (1+z⁻¹)² numerator, a gentle low-pass smoothing).
實例——本設計的 F(z): 從遞迴式 f[n] = f[n-1] − f[n-1]/1024 + e[n] + 2e[n-1] + e[n-2] 出發,代入 z⁻ᵏ 並整理:F·(1 − (1023/1024)z⁻¹) = E·(1 + z⁻¹)²,所以 F(z) = (1+z⁻¹)² / (1 − (1023/1024)z⁻¹)。直接讀出:一個 1023/1024 的極點(剛好在單位圓內 ⇒ 高 Q 諧振器,轉角 ωc = 1/(1024T)),以及 z = −1 的二重零點(1+z⁻¹)² 分子,溫和的低通平滑)。

Direct-Form-I block diagram of that F(z) — feed-forward taps 1, 2, 1 (numerator) and the single 1023/1024 feedback (denominator pole):

F(z) 的 Direct-Form-I 方塊圖——前饋抽頭 1、2、1(分子)與單一 1023/1024 回授(分母極點):

Direct Form I: F(z) = (1+z⁻¹)² / (1 − (1023/1024)·z⁻¹) e[n] z⁻¹ z⁻¹ e[n] e[n-1] e[n-2] ×1 ×2 ×1 Σ f[n] z⁻¹ ×1023/1024 + numerator (1+z⁻¹)²: feed-forward taps 1, 2, 1 denominator: feedback pole at 1023/1024

Read straight off the diagram: the three blue feed-forward taps 1, 2, 1 are the numerator (1+z⁻¹)² (double zero at z = −1); the single purple feedback gain 1023/1024 through one z⁻¹ is the denominator pole at 1023/1024 (the 1/1024 leak). Both z⁻¹ blocks are one-clock delays (registers).

直接從圖上讀出:三個藍色前饋抽頭 1、2、1 是分子 (1+z⁻¹)²(z = −1 的二重零點);經過一個 z⁻¹ 的單一紫色回授增益 1023/1024 是 1023/1024 的分母極點(1/1024 洩漏)。兩個 z⁻¹ 方塊都是單時脈延遲(暫存器)。

6. The integrator: 1/s and its z-domain forms6. 積分器:1/s 與它的 z 域形式

The integrator is the single most important building block in this loop — it is both G(z) (the resonator's frequency-setting state) and the integral term of the PI_antiWindup controller. In continuous time, integration is just division by s:

積分器是這個迴路中最重要的單一構件——它既是 G(z)(諧振器設定頻率的狀態),也是 PI_antiWindup 控制器的積分項。在連續時間裡,積分就是除以 s

∫₀ᵗ x(τ) dτ ⟷ (1/s)·X(s) [pole at s = 0]

There is no single z-domain integrator — the continuous 1/s maps to different discrete forms depending on how you approximate the area under the curve between samples. All three substitute for s below (T = sample period = 100 ns here):

z 域積分器並非只有一種——連續的 1/s 依「取樣間的曲線下面積如何近似」而映射到不同的離散形式。下表三種方法都是對 s 的代換(此處 T=取樣週期=100 ns):

Method方法s →Integrator H(z)積分器 H(z)Difference equation差分方程
Forward Euler
(rectangle, left edge)
前向歐拉(矩形取左緣)
(z−1)/T T·z⁻¹ / (1 − z⁻¹) y[n] = y[n−1] + T·x[n−1]
Backward Euler
(rectangle, right edge)
後向歐拉(矩形取右緣)
(z−1)/(Tz) T / (1 − z⁻¹) y[n] = y[n−1] + T·x[n]
Tustin / bilinear
(trapezoid)
Tustin/雙線性(梯形)
(2/T)·(z−1)/(z+1) (T/2)·(1+z⁻¹) / (1 − z⁻¹) y[n] = y[n−1] + (T/2)(x[n]+x[n−1])
This design uses Backward Euler: Both integrators here are the pure 1/(1 − z⁻¹) form (pole exactly at z = 1, no leak). G(z) = 0.5 × (332/1024) / (1 − z⁻¹) is a backward-Euler accumulator whose gain 332/1024 ≈ 0.3242 folds the sample period T and the integrator gain into a single coefficient — that coefficient is what sets the 40 kHz resonant frequency (see Integrator Gain — Frequency Tuning). The PI_antiWindup integral term is the same shape: acc ← acc + Kᵢ·error, with anti-windup clamping the accumulator so the z = 1 pole cannot run away.
本設計採用後向歐拉: 這裡的兩個積分器都是純 1/(1 − z⁻¹) 形式(極點正好在 z = 1,無洩漏)。G(z) = 0.5 × (332/1024) / (1 − z⁻¹) 是一個後向歐拉累加器,其增益 332/1024 ≈ 0.3242 把取樣週期 T 與積分器增益摺進單一係數——這個係數正是設定 40 kHz 諧振頻率的東西(見 積分器增益——頻率調整)。PI_antiWindup 的積分項形狀相同:acc ← acc + Kᵢ·error,並以抗積分飽和(anti-windup)鉗制累加器,讓 z = 1 的極點不會失控。

Transfer Functions轉移函數

The two digital filters implement a second-order resonator in factored form:

這兩個數位濾波器以因式分解的形式實作一個二階諧振器:

F(z) = (z^-0.5 + z^-1.5) / (1 - (1023/1024)·z^-1)
G(z) = 0.5 × (332/1024) / (1 - z^-1) [pure integrator]

The error signal fed into F(z) is:

送進 F(z) 的誤差訊號為:

e[n] = res_in[n] - g1024[27:10][n] // [27:10] = top 18 bits of the 28-bit g1024 ≈ ÷1024 (arith. >>10); [n] = value at sample n
What is e? e is the error signal that drives the F(z) bandpass filter — the input to the resonator recursion. It is the difference between the loop drive res_in (the relay output sign(f1024) plus the phase injection y_in) and the scaled integrator state g1024[27:10] (G(z)). Subtracting the integrator feedback from the drive is the classic second-order-resonator structure; the resulting e is pushed through F(z) via three taps (e[n], e[n-1], e[n-2], stored in registers e_1/e_2), closing the loop that sustains the 40 kHz oscillation. This is the g1024 → e → F(z) path shown in the block diagram above.
e 是什麼? e驅動 F(z) 帶通濾波器的誤差訊號——諧振器遞迴式的輸入。它是迴路驅動 res_in(繼電器輸出 sign(f1024) 加上相位注入 y_in)與縮放後的積分器狀態 g1024[27:10](G(z))之間的差。把積分器回授從驅動中減去,正是經典的二階諧振器結構;得到的 e 經由三個抽頭(e[n]e[n-1]e[n-2],存放在暫存器 e_1e_2)推入 F(z),閉合維持 40 kHz 振盪的迴路。這就是上方方塊圖中的 g1024 → e → F(z) 路徑。
Where res_in comes from: res_in is registered on posedge clk10MHz as the sum of two terms: a relay (comparator) output plus the phase injection y_in.
res_in 從哪裡來: res_inposedge clk10MHz 被鎖存,是兩項之和:繼電器(比較器)輸出加上相位注入 y_in
res_in[n] = { {9{f1024[23]}}, 1'b1, 8'b0 } + y_in // { } = concat, {9{f1024[23]}} = sign bit ×9 (sign-extend); 1'b1 @ bit8 = ±2⁸; 8'b0 = low pad → ±256 = ±256·sign(f1024) + y_in // +256 if f1024≥0, −256 if f1024<0 (relay/comparator output)

How the relay term { {9{f1024[23]}}, 1'b1, 8'b0 } evaluates to ±256:

繼電器項 { {9{f1024[23]}}, 1'b1, 8'b0 } 如何得出 ±256:

f1024 signf1024 符號f1024[23]bits [17:9]位元 [17:9]full 18-bit word完整 18 位元字組value數值
f1024 ≥ 0 0 000000000 0_0000_0000_1_0000_0000 +256
f1024 < 0 1 111111111 1_1111_1111_1_0000_0000 −256 (two's-complement)
TermSource來源Meaning意義
±256·sign(f1024) { {9{f1024[23]}}, 1'b1, 8'b0 } The relay element: takes the sign bit of the F(z) bandpass state f1024[23] and emits a fixed magnitude — +256 when f1024 ≥ 0, −256 when f1024 < 0 (18-bit signed). This square-wave nonlinearity is what sustains the limit-cycle oscillation.繼電器元件:取 F(z) 帶通狀態的符號位元 f1024[23],輸出固定大小——f1024 ≥ 0+256f1024 < 0−256(18 位元有號)。這個方波非線性正是維持極限環振盪的機制。
y_in {{10{uinj[2]}}, uinj[1:0], 6'b0} The injection from PI_antiWindup: the 3-bit uinj sign-extended to 18 bits and left-shifted 6 (×64). This steers the oscillator phase and closes the phase-lock loop.來自 PI_antiWindup注入:3 位元 uinj 符號延伸到 18 位元並左移 6(×64)。它調控振盪器相位、閉合相位鎖定迴路。

How y_in is builty_in 如何構成 — {{10{uinj[2]}}, uinj[1:0], 6'b0}

Same concat/replication idiom as the relay term (uinj is signed [2:0], y_in is signed [17:0]):

與繼電器項相同的串接/複製慣用寫法(uinjsigned [2:0]y_insigned [17:0]):

Width: 10 + 2 + 6 = 18 bits ✓. Field layout:

位寬:10 + 2 + 6 = 18 位元 ✓。欄位配置如下:

[17:8] sign×10 | [7:6] uinj[1:0] | [5:0] 000000

The full 3-bit uinj lands at bits [8:6] (sign-extended above, zero-padded below), so y_in = sign_extend(uinj) << 6 = uinj × 64 (×2⁶). Shown here for all five symmetric levels delay_line2 emits (see Injection Levels Are Symmetric for where they come from):

完整的 3 位元 uinj 落在位元 [8:6](上方符號延伸、下方補零),所以 y_in = sign_extend(uinj) << 6 = uinj × 64(×2⁶)。下表列出 delay_line2 送出的全部五個對稱位準(其來源見注入位準是對稱的):

uinj (bin)uinj (signed)uinj[2]uinj[1:0]full 18-bit wordy_in (= uinj×64)
3'b010+2010 0000000000 10 000000+128
3'b001+1001 0000000000 01 000000+64
3'b0000000 0000000000 00 0000000
3'b111−1111 1111111111 11 000000−64
3'b110−2110 1111111111 10 000000−128

So this term sign-extends the small 3-bit injection and scales it ×64, mapping the relay-correction levels into the ±256 amplitude range of res_in.

所以這一項把小小的 3 位元注入做符號延伸並放大 64 倍,把繼電器修正位準映射到 res_in 的 ±256 振幅範圍內。

Injection Levels Are Symmetric — 5 Levels, Not 4注入位準是對稱的——五階而非四階

The injection is symmetric with FIVE levels: +2, +1, 0, −1, −2 — easy to mistake for four if the +2 case is overlooked. All five are present and active in the hardware: the +2 comes from delay_line2.v:34 (3'b000: out=3'b010;) and is in fact one of the two dominant output levels.

注入是對稱的,共有五個位準:+2、+1、0、−1、−2——若忽略了 +2 的情況,很容易誤以為只有四個。五個位準在硬體中全部存在且有效:+2 來自 delay_line2.v:343'b000: out=3'b010;),而且事實上是兩個主要輸出位準之一。

From delay_line2.v (lines 27–34): the injection is a smoothed 1-bit signal — it sums four consecutive delayed samples (out_d0..out_d3, each 0 or 1), so sum ∈ {0,1,2,3,4}, then maps:

出自 delay_line2.v(第 27–34 行):注入是一個平滑化的 1 位元訊號——它把四個連續延遲取樣out_d0..out_d3,各為 0 或 1)相加,得到 sum ∈ {0,1,2,3,4},再映射如下:

case(sum)
  3'b100: out=3'b110;   // sum=4 → −2
  3'b011: out=3'b111;   // sum=3 → −1
  3'b010: out=3'b0;     // sum=2 →  0
  3'b001: out=3'b001;   // sum=1 → +1
  3'b000: out=3'b010;   // sum=0 → +2
endcase
sum (of 4 bits)uinjy_in (= uinj×64)
03'b010 = +2+128
13'b001 = +1+64
23'b000 = 00
33'b111 = −1−64
43'b110 = −2−128

So the complete set is symmetric: {+2, +1, 0, −1, −2}y_in {+128, +64, 0, −64, −128}. It is also inverting: an all-low input (sum=0) gives +2, an all-high input (sum=4) gives −2.

所以完整集合是對稱的:{+2, +1, 0, −1, −2}y_in {+128, +64, 0, −64, −128}。它同時是反相的:全低的輸入(sum=0)給 +2,全高的輸入(sum=4)給 −2。

Note the feedback loop: res_in depends on f1024, then e = res_in − g1024[27:10] drives f1024. The relay acting on the filter's own sign is what makes it oscillate. (The §2 port table's "±1 scaled" relay output is this term; the real scale is ±2⁸ = ±256.)

注意回授迴路:res_in 依賴 f1024,而 e = res_in − g1024[27:10] 又驅動 f1024。繼電器作用在濾波器自身的符號上,正是振盪的成因。(§2 埠列表中「±1 縮放」的繼電器輸出就是這一項;實際尺度是 ±2⁸ = ±256。)

The F(z) recursion (negedge update):

F(z) 遞迴式(negedge 更新):

f1024[n] = f1024[n-1] - f1024[n-1]/1024 (pole at 1023/1024 ≈ 1) + e[n] (z^0 term) + 2·e[n-1] (z^-1 term, ×2 = z^-0.5 half-step) + e[n-2] (z^-2 term)

F(z) Update — negedge Block WalkthroughF(z) 更新——negedge 區塊逐行解說

This is the F(z) half of relay_feedback2, running on the falling edge of clk10MHz while g1024/res_in run on the rising edge. That negedge/posedge interleave is what creates the half-sample z⁻⁰·⁵ offset the transfer function needs.

這是 relay_feedback2F(z) 半邊,跑在 clk10MHz下降緣,而 g1024res_in 跑在上升緣。這個 negedge/posedge 交錯正是轉移函數所需的半取樣 z⁻⁰·⁵ 偏移的來源。

always @(negedge clk10MHz) begin
    u_past <= f1024[23:11];                                  // u_past cut in half
    f1024  <= f1024 - {{10{f1024[23]}},f1024[23:10]}          // - f1024/1024  (leak)
                    + {{6{e[17]}},e}                        // + e[n]
                    + {{5{e_1[17]}},e_1,1'b0}                // + 2*e[n-1]
                    + {{6{e_2[17]}},e_2};                    // + e[n-2]
    e_1 <= e;                                                // shift error delay line
    e_2 <= e_1;
end

u_past <= f1024[23:11]

Saves the current oscillator output for next cycle. f1024[23:11] is the top 13 bits = u (line 12: u = f1024[23:11]); "cut in half" because it drops one more bit than v = f1024[23:10] (an extra ÷2). It feeds u_temp = u + u_pastu_0d5 = u_temp[13:1] = (u + u_past)/2, the half-period-averaged output.

把目前的振盪器輸出存起來供下一週期使用。f1024[23:11] 是最高 13 位元=u(第 12 行:u = f1024[23:11]);之所以說「砍半」,是因為它比 v = f1024[23:10] 再少取一個位元(多除以 2)。它餵入 u_temp = u + u_pastu_0d5 = u_temp[13:1] = (u + u_past)/2,即半週期平均輸出。

The f1024 update (F(z) recursion)f1024 更新(F(z) 遞迴)

Every operand is sign-extended to 24 bits (the width of f1024) so the signed addition lines up:

每個運算元都符號延伸到 24 位元f1024 的位寬),讓有號加法對齊:

Concatenation串接Meaning意義Term
{{10{f1024[23]}}, f1024[23:10]} f1024[23:10] = f1024 >> 10 = f1024/1024, sign-extended to 24bf1024[23:10]f1024 >> 10=f1024/1024,符號延伸到 24 位元 − leak (1/1024 → pole at 1023/1024)
{{6{e[17]}}, e} e (18b) sign-extended to 24be(18 位元)符號延伸到 24 位元 + e[n] (z⁰ tap)
{{5{e_1[17]}}, e_1, 1'b0} appending 1'b0 = ×2, then sign-extend to 24b尾接 1'b0=×2,再符號延伸到 24 位元 + 2·e[n-1] (z⁻¹ tap, ×2)
{{6{e_2[17]}}, e_2} e_2 sign-extended to 24be_2 符號延伸到 24 位元 + e[n-2] (z⁻² tap)

e_1 <= e; e_2 <= e_1;

A 2-tap shift register delaying the error: after this edge e_1 = e[n], e_2 = e[n-1]. Because non-blocking <= evaluates all right-hand sides with the old values first, the f1024 update uses the current combinational e = e[n] (= res_in − g1024[27:10]) together with the still-old e_1 = e[n-1] and e_2 = e[n-2].

一個延遲誤差的兩級移位暫存器:此邊緣之後 e_1 = e[n]e_2 = e[n-1]。由於非阻塞指定 <= 會先用值計算所有右式,f1024 的更新使用目前的組合邏輯 e = e[n]= res_in − g1024[27:10]),搭配仍是舊值的 e_1 = e[n-1]e_2 = e[n-2]

Resulting transfer function所得的轉移函數

z-transform of the recursion (output F, input E):

對遞迴式取 z 變換(輸出 F、輸入 E):

F·(1 − z⁻¹ + (1/1024)z⁻¹) = E·(1 + 2z⁻¹ + z⁻²) F/E = (1 + z⁻¹)² / (1 − (1023/1024)z⁻¹) = 1024·(1 + z⁻¹)² / (1024 − 1023·z⁻¹)
Why the widths (header comment): The module notes that after retuning to 40 kHz the oscillation amplitude doubled and f1024/g1024 overflowed, so this version gives "one more bit to f1024, e, e_1." That is why f1024 is 24-bit and e/e_1/e_2 are 18-bit signed — headroom so the +2·e[n-1] term and the accumulation never overflow.
為什麼是這些位寬(檔頭註解): 模組註解記載,改調到 40 kHz 後振盪振幅加倍,f1024/g1024 發生溢位,所以這個版本「給 f1024ee_1 多一個位元」。這就是 f1024 是 24 位元、e/e_1/e_2 是 18 位元有號的原因——保留餘裕,讓 +2·e[n-1] 項與累加永不溢位。
Datapath Width — Why 18-bit Signed?資料路徑位寬——為何 18-bit 有號? (click to expand)

The error bus and the registers feeding it — res_in, y_in, e, e_1, e_2 — are all declared signed [17:0]. Two design reasons:

誤差匯流排與餵入它的暫存器——res_iny_inee_1e_2——全部宣告為 signed [17:0]。有兩個設計理由:

Why signed為什麼要有號

The oscillator signal is bipolar — it swings symmetrically above and below zero, so every term on the bus takes both signs: the relay output is ±256, the injection y_in ranges −128…+128, and the error e and integrator feedback are both bidirectional. The datapath must therefore use two's-complement signed arithmetic; an unsigned bus could not represent the negative half of the waveform.

振盪器訊號是雙極性的——它對稱地在零上下擺動,所以匯流排上的每一項都有正有負:繼電器輸出是 ±256,注入 y_in 的範圍是 −128…+128,誤差 e 與積分器回授也都是雙向的。因此資料路徑必須使用二補數有號運算;無號匯流排無法表示波形的負半邊。

Why 18 bits為什麼是 18 位元

The width is set by the error subtraction (relay_feedback2.v line 21):

位寬由誤差減法決定(relay_feedback2.v 第 21 行):

assign e = res_in - g1024[27:10];

g1024 is the 28-bit integrator state; the slice g1024[27:10] is its top 18 bits (= 27−10+1), the ÷1024-scaled feedback term. Since e = res_in − g1024[27:10], the error bus must be ≥18 bits to hold that subtraction without truncating the feedback or overflowing — so res_in, y_in, e, e_1, e_2 are all sized to one consistent 18-bit bus.

g102428 位元積分器狀態;切片 g1024[27:10] 是其最高 18 位元(= 27−10+1),即除以 1024 後的回授項。因為 e = res_in − g1024[27:10],誤差匯流排必須至少 18 位元,才能在不截斷回授、不溢位的情況下容納這個減法——所以 res_iny_inee_1e_2 全部統一為 18 位元匯流排。

Why 18 is enough (and not wasteful): 18-bit signed spans ±131,071 (≈ ±2¹⁷), far larger than the actual drive terms:
為什麼 18 位元夠用(而且不浪費): 18 位元有號可表示 ±131,071(≈ ±2¹⁷),遠大於實際的驅動項:
TermRange範圍Bits really needed實際所需位元
relay output±256 (±2⁸)~10
injection y_in−128…+128~9
res_in (relay + y_in)≈ −384…+384~10

The extra headroom above ~10 bits is deliberate:

超出約 10 位元的額外餘裕是刻意的

  1. Match the integrator feedbackg1024[27:10] genuinely uses all 18 bits, and it is the dominant term in e.
    對齊積分器回授——g1024[27:10] 確實用滿 18 位元,而且它是 e 中的主導項。
  2. Recursion headroom — the F(z) update sums e[n] + 2·e[n-1] + e[n-2] into the 24-bit f1024; the wide bus prevents intermediate overflow.
    遞迴餘裕——F(z) 更新把 e[n] + 2·e[n-1] + e[n-2] 加進 24 位元的 f1024;寬匯流排防止中間結果溢位。
  3. Preserve low-bit resolution — keeping 18 bits retains the fine bits of the ÷1024 integrator feedback that set the precise 40 kHz frequency and Q; truncating to ~10 bits would coarsen the tuning.
    保留低位元解析度——維持 18 位元可保留除以 1024 的積分器回授中的細位元,這些位元決定精確的 40 kHz 頻率與 Q;若截到約 10 位元,調諧就會變粗。

In short: signed because the waveform is bipolar; 18 bits because e = res_in − g1024[27:10], and the 18-bit integrator-feedback slice sets the bus width — with headroom so the resonator recursion never overflows and the frequency/Q resolution is preserved.

簡而言之:有號是因為波形是雙極性的;18 位元是因為 e = res_in − g1024[27:10],18 位元的積分器回授切片決定了匯流排位寬——並保留餘裕,讓諧振器遞迴永不溢位、頻率/Q 解析度得以保留。

Integrator Gain — Frequency Tuning積分器增益——頻率調整

The coefficient 332/1024 is implemented as a shift-add approximation:

係數 332/1024 以移位相加近似實作:

332/1024 = 2^-3 + 2^-5 + 2^-8 + 2^-9 = 0.125 + 0.03125 + 0.00390625 + 0.001953125 = 0.3242... Verilog: temp = (f1024 >> 3) + (f1024 >> 5) + (f1024 >> 8) + (f1024 >> 9)
Frequency: At 10 MHz clock, this gain yields a closed-loop natural frequency of 40 kHz. The original design used 306/1024 for a lower frequency; the change to 332/1024 was the explicit redesign to reach 40 kHz.
頻率: 在 10 MHz 時脈下,此增益使閉迴路自然頻率為 40 kHz。原始設計使用 306/1024(較低頻率);改為 332/1024 正是為了達到 40 kHz 的明確重新設計。

What the 1/1024 Leak Means1/1024 洩漏的意義

The 1/1024 leak is the − f1024/1024 term in the F(z) recursion:

1/1024 洩漏就是 F(z) 遞迴式中的 − f1024/1024 項:

f1024[n] = f1024[n-1] − f1024[n-1]/1024 + (e terms) = (1023/1024)·f1024[n-1] + (e terms)

It means: every clock sample the filter discards 1/1024 of its current state — it keeps 1023/1024 ≈ 99.9% and loses ≈0.1%. ("Leak" = the leaky-bucket image: the stored value continuously drains a tiny bit. 「漏」即漏水桶的比喻:儲存的值持續地洩漏一點點。)

意思是:每個時脈取樣,濾波器丟棄自身狀態的 1/1024——保留 1023/1024 ≈ 99.9%、失去 ≈0.1%。(「洩漏」即漏水桶的比喻:儲存的值持續地流失一點點。)

What it actually does它實際上做什麼

  1. It is the only loss/damping in the resonator. G(z) is a pure, lossless integrator (pole exactly at z = 1); all damping comes from this one term. Without it the filter is a pure accumulator — it integrates forever, drifts, and overflows.
    它是諧振器中唯一的損耗/阻尼。G(z) 是純粹無損的積分器(極點正好在 z = 1);所有阻尼都來自這一項。少了它,濾波器就是純累加器——永遠積分、漂移、溢位。
  2. It moves the pole just inside the unit circle. No leak → pole at z = 1 (marginally stable, infinite Q). With the leak → pole at z = 1023/1024, a lightly-damped resonator with finite, high Q. The closer the pole to the unit circle, the smaller the leak and the higher the Q.
    它把極點移到單位圓內側一點點。沒有洩漏 → 極點在 z = 1(臨界穩定、無限 Q)。有洩漏 → 極點在 z = 1023/1024,一個輕阻尼、有限高 Q 的諧振器。極點越靠近單位圓,洩漏越小、Q 越高。
  3. It models a real resonator's energy loss. A physical cavity (Yu's ultrasonic tank, Q ≈ 25) dissipates a little energy each cycle; the 1/1024 leak is the digital stand-in that gives this oscillator a finite Q instead of an ideal infinite one.
    它模擬真實諧振器的能量損耗。實體腔體(Yu 的超音波腔,Q ≈ 25)每個週期都會耗散一點能量;1/1024 洩漏就是數位替身,讓這顆振盪器擁有有限的 Q,而非理想的無限 Q。
  4. It sets the bandwidth / corner frequency / Q. This single coefficient defines the resonator's sharpness — bigger leak (1/512) → wider band, lower Q; smaller leak (1/2048 = f1024[23:11]) → narrower band, higher Q.
    它設定頻寬/轉角頻率/Q。這個單一係數定義諧振器的尖銳度——洩漏變大(1/512)→ 頻帶變寬、Q 變低;洩漏變小(1/2048 = f1024[23:11])→ 頻帶變窄、Q 變高。
ωc = 1/(1024·T), Q ≈ ω_n/(2·ωc) ≈ 13
Decay-time picture: Losing 1/1024 per sample is exponential decay — f[n] ∝ (1023/1024)ⁿ ≈ e^(−n/1024) — so the natural ring-down time constant is τ ≈ 1024 samples = 1024·T seconds (a free oscillation decays to 1/e after ~1024 cycles). The relay keeps pumping energy back in, so the net result is a stable limit cycle: relay adds energy each cycle, the leak removes a controlled amount → sustained, fixed-amplitude 40 kHz oscillation.
衰減時間圖像: 每個取樣失去 1/1024 就是指數衰減——f[n] ∝ (1023/1024)ⁿ ≈ e^(−n/1024)——所以自然振鈴衰減時間常數為 τ ≈ 1024 個取樣 = 1024·T 秒(自由振盪約 1024 個週期後衰減到 1/e)。繼電器持續把能量打回去,淨結果是一個穩定的極限環:繼電器每週期補能量、洩漏移走可控的量 → 持續、定振幅的 40 kHz 振盪。
Why exactly 1/1024: 1/1024 = 2⁻¹⁰, so the leak is just an arithmetic right-shift by 10 (f1024 >> 10) — no multiplier needed, and a convenient power-of-two for the frequency/Q targets.
為什麼剛好是 1/1024: 1/1024 = 2⁻¹⁰,所以洩漏就是一個算術右移 10 位f1024 >> 10)——不需要乘法器,而且是符合頻率/Q 目標的方便的 2 的冪次。

In one line: the 1/1024 leak is the resonator's controlled energy loss per sample — it turns a would-be infinite-Q pure integrator into a stable, finite-Q bandpass at 40 kHz, and single-handedly sets the bandwidth, corner frequency, and Q.

一句話:1/1024 洩漏是諧振器每取樣受控的能量損耗——它把原本會是無限 Q 的純積分器,變成穩定、有限 Q、位於 40 kHz 的帶通,並獨力設定頻寬、轉角頻率與 Q。

Deriving ωc = 1/(1024·T)推導 ωc = 1/(1024·T)

The corner frequency in F(s) ≈ (2/T)/(s + ωc) comes directly from the single 1/1024 leak in the F(z) feedback. Two derivations, same result.

F(s) ≈ (2/T)/(s + ωc) 中的轉角頻率直接來自 F(z) 回授中唯一的 1/1024 洩漏。兩種推導,同一個結果。

Method 1 — difference equation → derivative方法一——差分方程 → 微分

The leaky-integrator part of the F(z) recursion is:

F(z) 遞迴式中的洩漏積分器部分為:

f[n] = f[n-1] − (1/1024)·f[n-1] + (input)

Move f[n-1] left and divide by the sample period T:

f[n-1] 移到左邊,再除以取樣週期 T

(f[n] − f[n-1]) / T = −(1/1024T)·f[n-1] + (input)/T

The left side is the backward-difference approximation of df/dt, so in continuous time df/dt + (1/1024T)·f = input. Comparing with the standard first-order low-pass df/dt + ωc·f = input:

左邊是 df/dt 的後向差分近似,所以在連續時間中 df/dt + (1/1024T)·f = input。與標準一階低通 df/dt + ωc·f = input 比較:

ωc = 1 / (1024·T)

Method 2 — map the digital pole to an analog pole方法二——把數位極點映射到類比極點

The denominator 1 − (1023/1024)z⁻¹ puts the pole at:

分母 1 − (1023/1024)z⁻¹ 把極點放在:

z_p = 1023/1024 = 1 − 1/1024

Using z = e^{sT}s = (1/T)·ln(z_p), and ln(1−x) ≈ −x for small x:

利用 z = e^{sT}s = (1/T)·ln(z_p),以及小 x 時 ln(1−x) ≈ −x

s_p = (1/T)·ln(1 − 1/1024) ≈ (1/T)·(−1/1024) = −1/(1024T)

A low-pass 1/(s + ωc) has its pole at s = −ωc, so again ωc = 1/(1024T).

低通 1/(s + ωc) 的極點在 s = −ωc,所以同樣得到 ωc = 1/(1024T)

Intuition: The 1/1024 is the leak (the −f1024/1024 subtraction = pole at 1023/1024); the T converts "per sample" into "per second." Losing 1/1024 of the state each sample ⇒ a time constant of 1024·T seconds ⇒ corner frequency ωc = 1/(1024T).
直覺: 1/1024 是洩漏(−f1024/1024 減法=極點在 1023/1024);T 把「每取樣」換算成「每秒」。每取樣失去狀態的 1/1024 ⇒ 時間常數 1024·T 秒 ⇒ 轉角頻率 ωc = 1/(1024T)
ωc = 1 / (1024 × 100 ns) = 1 / 102.4 µs ≈ 9.77×10³ rad/s ≈ 1.55 kHz (T = 100 ns @ 10 MHz)

Quality Factor (Q) — Resonator Damping品質因數 (Q)——諧振器阻尼

There is no explicit "Q" coefficient — the quality factor is set by the only lossy element in the loop: the 1/1024 leak in the F(z) feedback path, i.e. the pole at 1023/1024 (the −f1024/1024 subtraction). G(z) is a pure, lossless integrator (pole exactly at z=1), so all damping comes from this one term. The closer the pole sits to the unit circle, the higher the Q.

沒有明確的「Q」係數——品質因數由迴路中唯一的損耗元件設定:F(z) 回授路徑中的 1/1024 洩漏,即極點在 1023/1024−f1024/1024 減法)。G(z) 是純粹無損的積分器(極點正好在 z=1),所以所有阻尼都來自這一項。極點越靠近單位圓,Q 越高。

ωc = 1 / (1024·T_s) (T_s = 100 ns @ 10 MHz) = 1 / (1024 × 100 ns) ≈ 9.77 krad/s (≈ 1.55 kHz) Q ≈ ω_n / (2·ωc) = (ω_n · 1024 · T_s) / 2 = 2π·40 kHz / (2 × 9.77 krad/s) ≈ 13

Cross-check: 250 samples per 40 kHz period × (1/1024) loss/sample ≈ 0.24 loss/cycle → Q ≈ π/0.24 ≈ 13 (same result).

交叉驗證:每個 40 kHz 週期 250 個取樣 ×(1/1024)損耗/取樣 ≈ 每週期損耗 0.24 → Q ≈ π/0.24 ≈ 13(相同結果)。

vs Yu's Q = 25: Yu & Horng's Q = 25 is the physical quality factor of the ultrasonic SIL cavity. Here it becomes a design choice realized by the leak coefficient. The value differs because this is a relay-feedback limit cycle — the relay sustains the amplitude regardless of Q, so Q governs spectral selectivity/purity, not whether it oscillates. To raise Q, move the pole nearer the unit circle: e.g. change the leak from f1024[23:10] (1/1024) to f1024[23:11] (1/2048), roughly doubling Q and halving the bandwidth.
vs Yu 的 Q = 25: Yu & Horng 的 Q = 25 是超音波 SIL 腔體的實體品質因數。在這裡它變成由洩漏係數實現的設計選擇。數值不同是因為這是一個繼電回授極限環——繼電器不論 Q 為何都會維持振幅,所以 Q 決定的是頻譜的選擇性/純度,而不是能否振盪。要提高 Q,就把極點移近單位圓:例如把洩漏從 f1024[23:10](1/1024)改為 f1024[23:11](1/2048),Q 大約加倍、頻寬減半。

Clocking Strategy時脈策略

Clock edge時脈邊緣What updates更新內容Why原因
negedge clk10MHz f1024, e_1, e_2, u_past F(z) bandpass filter state. Negedge gives half-period offset from G(z), implementing the z^-0.5 fractional delay in the transfer function.F(z) 帶通濾波器狀態。negedge 與 G(z) 相差半個週期,實現轉移函數中的 z^-0.5 分數延遲。
posedge clk10MHz g1024, res_in G(z) integrator and relay computation. The interleaved clocking between F and G is essential to the z^-0.5 term.G(z) 積分器與繼電器運算。F 與 G 之間的交錯時脈是 z^-0.5 項的關鍵。

Phase Injection (uinj)相位注入 (uinj)

The 3-bit uinj input comes from the PI_antiWindup controller. It is sign-extended into an 18-bit value y_in and added to the relay output each cycle:

3 位元的 uinj 輸入來自 PI_antiWindup 控制器。它被符號延伸成 18 位元的 y_in,並在每個週期加到繼電器輸出上:

Parameter參數Value / Expression數值/表達式Description說明
uinj input widthuinj 輸入位寬3 bits [2:0] Signed 3-bit injection from PI_antiWindup controller來自 PI_antiWindup 控制器的 3 位元有號注入
y_in formationy_in 構成{{10{uinj[2]}}, uinj[1:0], 6'b0} Sign-extend to 18 bits, then left-shift 6 (×64 scale)符號延伸到 18 位元,再左移 6(×64 縮放)
Left-shift factor左移因子×64 (<<6) Maps 3-bit uinj into 18-bit res_in amplitude range把 3 位元 uinj 映射到 18 位元 res_in 振幅範圍
res_in formationres_in 構成sign(f1024) + y_in Relay output (±1 scaled) plus injection — updated on posedge繼電器輸出(±1 縮放)加注入——posedge 更新

This injection steers the oscillation phase, closing the overall phase-lock loop.

這個注入調控振盪相位,閉合整體的相位鎖定迴路。

Why is uinj 3 bits?為什麼 uinj 是 3 位元?

The width is set by how the injection is generated — it is a smoothed 1-bit delayed signal, not a fine-resolution value. In PI_antiWindup the injection is produced by delay_line2, whose input is a single bit (uin) delayed by a tunable amount d through a 64k-RAM circular buffer. That delay line is the tunable delay (TDL) of the phase-cancelling loop; d comes from the PI output (d = PI_out[16:0]).

位寬由注入的產生方式決定——它是一個平滑化的 1 位元延遲訊號,不是高解析度數值。在 PI_antiWindup 中,注入由 delay_line2 產生,其輸入是單一位元uin),經由 64k-RAM 環形緩衝延遲一個可調量 d。這條延遲線就是相位消除迴路的可調延遲線(TDL)d 來自 PI 輸出(d = PI_out[16:0])。

A raw 1-bit injection would be a hard square edge (harmonic-rich, poor for clean injection locking), so delay_line2 combines several consecutive delayed samples into a small signed multi-level value — its own comment reads "Smoother transition of input injection for ILO". It emits five symmetric levels:

未經處理的 1 位元注入會是生硬的方波邊緣(諧波豐富,不利於乾淨的注入鎖定),所以 delay_line2 把數個連續延遲取樣合併成一個小的有號多位準值——它自己的註解寫著 "Smoother transition of input injection for ILO"。它送出五個對稱位準:

3'b010 = +2 3'b001 = +1 3'b000 = 0 3'b111 = −1 3'b110 = −2
uinj: 1-bit delayed signal → 5-level smoothed injection (delay_line2) 1-bit uin (delayed by d) 1 0 hard edge → harmonic-rich 3-bit smoothed out[2:0] +2 +1 0 −1 −2 010 001 000 111 110 stepped 5-level → smoother spectrum clk cycles

On a transition the 1-bit edge (red) would jump in one clock; the delay-line output (green) instead walks through all five levels +2 → +1 → 0 → −1 → −2 (codes 010, 001, 000, 111, 110) over consecutive clocks — softening the edge before it is scaled ×64 and injected. (The mapping is inverting: an all-low input window gives +2, an all-high window gives −2; the steady levels +2 and −2 dominate, the rest appear only during the edge.)

在轉換點上,1 位元邊緣(紅)會在一個時脈內跳變;延遲線輸出(綠)則在連續時脈中走過全部五個位準 +2 → +1 → 0 → −1 → −2(編碼 010, 001, 000, 111, 110)——在被放大 ×64 並注入之前先軟化邊緣。(映射是反相的:全低輸入窗給 +2、全高輸入窗給 −2;穩態位準 +2 與 −2 佔主導,其餘只在邊緣期間出現。)

So 3 bits is the minimum that fits: a sign plus the small range −2…+2. More bits would be useless — the source is only 1-bit, so there is no finer amplitude information to carry, only a few edge-smoothing steps. Fewer bits cannot work — you need sign + magnitude for −2…+2. Keeping it narrow also keeps the delay-line word small (1-bit stored in RAM, widened to 3-bit only at the output smoothing stage).
所以 3 位元是剛好夠用的最小值: 一個符號加上 −2…+2 的小範圍更多位元沒有用——來源只有 1 位元,沒有更精細的振幅資訊可攜帶,只有幾個邊緣平滑階。更少位元不可行——表示 −2…+2 需要符號+大小。保持窄位寬也讓延遲線字組小(RAM 中只存 1 位元,僅在輸出平滑級才展寬成 3 位元)。

The ×64 shift then scales this perturbation to ≈ −128…+128 against the relay's ±256 — i.e. the injection is a fraction of the oscillation amplitude, exactly the role of Yu's injection ratio η = A_inj/A_osc: it nudges the phase, it does not carry precise amplitude.

接著 ×64 移位把這個擾動放大到約 −128…+128,相對於繼電器的 ±256——亦即注入是振盪振幅的一小部分,正是 Yu 的注入比 η = A_inj/A_osc 的角色:它輕推相位,而不攜帶精確振幅。

Output Derivation輸出推導

Output輸出Expression表達式Downstream use下游用途
u f1024[23:11] — upper 13 bits (÷2) Phase error demodulation in demodulator.vdemodulator.v 進行相位誤差解調
u_0d5 (u + u_past) / 2 — average of current & previous Feeds a 12-register delay chain in _1Mhz_beta010.v to produce a reference delayed by 12.5 cycles of 2 MHz (= 6.25 µs = quarter period of 40 kHz)餵入 _1Mhz_beta010.v 的 12 級暫存器延遲鏈,產生延遲 12.5 個 2 MHz 週期的參考訊號(= 6.25 µs = 40 kHz 的四分之一週期)
v f1024[23:10] — upper 14 bits (÷1, full resolution) Tx DAC output (Q channel) after 62.5 ns delay and amplitude correction in FP domain經 62.5 週期延遲與浮點域振幅校正後的 Tx DAC 輸出(Q 通道)

Implementation Parameters實作參數

Parameter參數Expression表達式Description說明
F(z) update clockF(z) 更新時脈negedge clk10MHz f1024, e_1, e_2, u_past updated on falling edgef1024、e_1、e_2、u_past 在下降緣更新
G(z) update clockG(z) 更新時脈posedge clk10MHz g1024, res_in updated on rising edge — half-period offset from F(z)g1024、res_in 在上升緣更新——與 F(z) 相差半個週期
Error signal e誤差訊號 eres_in − g1024[27:10] Difference between relay+injection and integrator output繼電器+注入與積分器輸出之差
f1024 recursionf1024 遞迴f1024 − f1024/1024 + e + 2·e_1 + e_2 F(z) bandpass update — pole at 1023/1024, numerator (1+2z⁻¹+z⁻²)F(z) 帶通更新——極點在 1023/1024,分子 (1+2z⁻¹+z⁻²)
332/1024 shift-add332/1024 移位相加(f1024>>3)+(f1024>>5)+(f1024>>8)+(f1024>>9) Approximates 0.3242 — integrator gain sets 40 kHz resonant frequency近似 0.3242——積分器增益設定 40 kHz 諧振頻率
g1024 recursiong1024 遞迴g1024 + temp (temp = 332/1024 × f1024) G(z) pure integrator, accumulates indefinitelyG(z) 純積分器,無限累加
g1024 initial valueg1024 初始值0x1000000 (4'b0001, 24'b0) Non-zero seed — prevents deadlock at startup非零種子——防止啟動時卡死
res_in formationres_in 構成sign(f1024) + y_in Relay (±256 in 18-bit) plus sign-extended injection繼電器(18 位元中的 ±256)加上符號延伸後的注入
u_0d5 interpolationu_0d5 內插u_temp[13:1] where u_temp = {u[12],u}+{u_past[12],u_past} Sign-extended add then right-shift = (u + u_past)/2 half-sample delay符號延伸相加後右移=(u + u_past)/2 半取樣延遲

Top-level Connections (_1Mhz_beta010.v)頂層連接 (_1Mhz_beta010.v)

Instance U2 at line 34 of _1Mhz_beta010.v:

_1Mhz_beta010.v 第 34 行的實例 U2

PortNet in _1Mhz_beta010.v_1Mhz_beta010.v 中的網線Dir方向Destination / Source去處/來源
clk10MHzclk10MHz clock_generator.v (U1)
uinju_inj PI_antiWindup.v (U4) — phase correction injectionPI_antiWindup.v(U4)——相位修正注入
uu demodulator.v (U3) phase detector inputdemodulator.v(U3)鑑相器輸入
u_0d5u_0d5 12-register delay chain in top level → demodulator.v reference input頂層 12 級暫存器延遲鏈 → demodulator.v 參考輸入
vv tx_gainhilbert_90 (I = y_90, Q = y_0 × 1/1.022) → DAC_DA/DB; also the clutter_cancel Tx referencetx_gainhilbert_90(I = y_90,Q = y_0 × 1/1.022)→ DAC_DA/DB;亦為 clutter_cancel 的 Tx 參考

Key Numeric Constants關鍵數值常數

Parameter參數Value數值Meaning意義
Clock rate時脈速率10 MHz100 ns period週期 100 ns
Target frequency目標頻率40 kHz25 µs period = 250 clock cycles週期 25 µs=250 個時脈週期
Integrator gain積分器增益332/1024 ≈ 0.3242Determines resonant frequency決定諧振頻率
F(z) poleF(z) 極點1023/1024 ≈ 0.9990Near-unity pole for low damping接近 1 的極點,實現低阻尼
g1024 initg1024 初始值0x1000000Non-zero seed to avoid deadlock at startup非零種子,避免啟動時卡死
f1024 widthf1024 位寬24 bits signedWaveform Q2.22 fixed-point; outputs sliced from MSBs波形為 Q2.22 定點;輸出取自最高位元
Injection scale注入縮放<< 6 (×64)Maps 3-bit uinj into 18-bit res_in range把 3 位元 uinj 映射到 18 位元 res_in 範圍

3. PI Phase-Lock ControllerPI 相位鎖定控制器

Theory ref — Yu & Horng §5, eq. (11),(12),(18): Same PI controller C(s) = k_I/s + k_p regulating θ→π against the Doppler-phase disturbance. Yu's design collapses to a single knob ω_BW (the PI zero at ω_c cancels the plant pole → pure integrator −ω_BW/s). The module's anti-windup maps onto Yu's stability region θ∈(0.5π, 1.5π) and max trackable speed v_max = π·ω_BW/(5·ω_n)·c — beyond it the plant gain flips sign. [Yu guide]
理論對照 — Yu & Horng §5,式 (11)、(12)、(18): 同樣的 PI 控制器 C(s) = k_I/s + k_p,對抗都卜勒相位擾動、把 θ→π 穩定住。Yu 的設計收斂成單一旋鈕 ω_BW(PI 零點放在 ω_c 抵消受控體極點 → 純積分器 −ω_BW/s)。本模組的抗積分飽和對應 Yu 的穩定區 θ∈(0.5π, 1.5π) 與最大可追蹤速度 v_max = π·ω_BW/(5·ω_n)·c——超過它受控體增益就會翻號。[Yu 導讀]

The system implements a digital phase-locked loop (PLL) using a PI controller. The oscillator phase is measured by demodulation, corrected by a PI integrator, and fed back as a variable-delay injection into the relay oscillator. A PWM output carries the same control signal to an analog path.

本系統用 PI 控制器實作一個數位鎖相迴路(PLL)。振盪器相位經解調量測、由 PI 積分器修正,再以可變延遲注入回授進繼電振盪器。PWM 輸出把同一個控制訊號帶到類比路徑。

Which .v Files Implement This由哪些 .v 檔案實作

§3 spans three Verilog files, wired together in the top module _1Mhz_beta010.v:

§3 橫跨三個 Verilog 檔案,在頂層模組 _1Mhz_beta010.v 中接線在一起:

File檔案Role角色Instance in _1Mhz_beta010.v在 _1Mhz_beta010.v 中的實例
PI_antiWindup.v the PI controller itself (the core of §3) — outputs u_inj, pwm_in, dataPI 控制器本體(§3 的核心)——輸出 u_injpwm_indata PI_antiWindup U4 (line 90)
demodulator.v produces the phase error x256 that feeds the PI產生餵給 PI 的相位誤差 x256 demodulator U3 (line 49)
delay_line2.v the tunable delay line that forms the injection u_inj構成注入訊號 u_inj 的可調延遲線 instantiated inside PI_antiWindup.v (not in the top module)

The controller proper is PI_antiWindup.v; its input comes from demodulator.v (x256), and its injection output is built with delay_line2.v. That is why delay_line2 does not appear in _1Mhz_beta010.v directly — only demodulator (U3) and PI_antiWindup (U4) are instantiated at the top; delay_line2 is nested one level down inside the PI block.

控制器本體是 PI_antiWindup.v;它的輸入來自 demodulator.vx256),注入輸出則由 delay_line2.v 構成。這就是為什麼 delay_line2 沒有直接出現在 _1Mhz_beta010.v 中——頂層只實例化 demodulator(U3)與 PI_antiWindup(U4);delay_line2 巢狀在 PI 方塊內部、往下一層。

Signal Chain Overview信號鏈總覽

Stage階段Module / signal模組/訊號Clock時脈Description說明
1. Oscillator1. 振盪器 relay_feedback2 → u (13-bit) 10 MHz Generates 40 kHz oscillation. Phase is steered by u_inj[2:0]產生 40 kHz 振盪。相位由 u_inj[2:0] 調控
2. Reference delay2. 參考延遲 u_0d5 → 12-reg chain → u12 2 MHz Delays u_0d5 by 12.5 cycles of 2 MHz = 6.25 µs = 90° at 40 kHzu_0d5 延遲 12.5 個 2 MHz 週期=6.25 µs=40 kHz 的 90°
3. Phase detector3. 鑑相器 demodulator → x256 (22-bit) 1 MHz / 250 kHz v = u × u12 (cross-correlation), then two cascaded lowpass filters → phase error × 256v = u × u12(互相關),再經兩級級聯低通濾波器 → 相位誤差 × 256
4. Feedback select4. 回授選擇 uin3 (1-bit) 10 MHz Mux selects: external uin, self (u_out), Rx_cc_Amped_DB, or Rx_ir_Amped_DB多工器選擇:外部 uin、自身(u_out)、Rx_cc_Amped_DBRx_ir_Amped_DB
5. PI controller5. PI 控制器 PI_antiWindup → PI_out (24-bit) 50 kHz Integrates x256 with kI = 3/256, adds proportional term kP = 1/2¹⁵以 kI = 3/256 積分 x256,加上比例項 kP = 1/2¹⁵
6. Output filter6. 輸出濾波器 xf → data (24-bit) 50 kHz Unity-gain lowpass, cutoff 15.6 Hz — smooths PI_out for UART transmission單位增益低通,截止頻率 15.6 Hz——把 PI_out 平滑後供 UART 傳送
7. Variable delay7. 可變延遲 delay_line2 → u_inj (3-bit) 50 MHz 128K RAM circular buffer delays uin3 by d = PI_out[16:0] samples → phase correction injection128K RAM 環形緩衝把 uin3 延遲 d = PI_out[16:0] 個取樣 → 相位修正注入
8. PWM output8. PWM 輸出 PWM → pwm (1-bit) 50 MHz 10-bit pwm_in from PI_antiWindup drives analog output來自 PI_antiWindup 的 10 位元 pwm_in 驅動類比輸出

PI Controller CoefficientsPI 控制器係數

Parameter參數Expression表達式Value數值Notes備註
Integral gain kI積分增益 kI 1/2⁷ + 1/2⁸ = 3/2⁸ ≈ 0.01172 per sample Applied at 50 kHz — accumulates into 32-bit int256以 50 kHz 施加——累加進 32 位元的 int256
Proportional gain kP比例增益 kP 1/2¹⁵ ≈ 3.05 × 10⁻⁵ Very small — system is predominantly integral action非常小——系統以積分作用為主
Output scale輸出縮放 int256[31:8] ÷ 256 Shifts integrator accumulator down to 24-bit PI_out把積分累加器右移縮成 24 位元的 PI_out
Output filter pole輸出濾波器極點 511/512 ≈ 0.998 Cutoff ≈ 15.6 Hz at 50 kHz. DC gain = 1 (unity). Output = xf[33:10]在 50 kHz 下截止頻率 ≈ 15.6 Hz。直流增益=1(單位增益)。輸出=xf[33:10]
Error gain (shift_bit)誤差增益(shift_bit) ×2^shift_bit, shift_bit ∈ {0,1,2,3} ×1, ×2, ×4, ×8 Incremented by hardware change button; scales x256 − r由硬體 change 按鈕遞增;縮放 x256 − r
Delay range延遲範圍 d = PI_out[16:0] 0 – 131071 samples @ 50 MHz = 0 – 2.62 ms → covers many periods of 40 kHz for phase wrap= 0 – 2.62 ms → 涵蓋 40 kHz 的許多週期,供相位迴繞使用

What "Anti-Windup" Means「抗積分飽和」是什麼意思

The module name is PI_antiWindup — so what is the "windup" it defends against? It is a problem that afflicts any controller with an integrator (the I term).

模組名稱是 PI_antiWindup——那它防範的「windup(積分飽和)」是什麼?這是任何含積分器的控制器(I 項)都會遇到的問題。

The problem: integrator windup問題:積分器飽和(windup)

The integral term here is the pure accumulator int256 ← int256 + kI·error — a pole at z = 1 (see the Z-Domain Primer §6), so it sums error without limit. Now suppose the actuator saturates — the delay command d = PI_out[16:0] hits its 0…131071 range limit, or pwm_in clips at full scale:

這裡的積分項是純累加器 int256 ← int256 + kI·error——極點在 z = 1(見 z 域入門 §6),所以它會無上限地累加誤差。現在假設致動器飽和了——延遲命令 d = PI_out[16:0] 撞到 0…131071 的範圍極限,或 pwm_in 在滿刻度處截波:

  1. A large phase error demands a big output.
    大的相位誤差要求大的輸出。
  2. The output is clamped at its limit — but the error does not go away.
    輸出被鉗制在極限值——但誤差並沒有消失。
  3. The integrator keeps adding that non-zero error every 50 kHz sample, so int256 grows huge — it "winds up."
    積分器每個 50 kHz 取樣都持續累加那個非零誤差,於是 int256 越長越大——它「飽和(winds up)」了。
  4. When the error finally reverses, int256 is enormous, so the controller keeps commanding the limit long after it should have backed off — it must "unwind" first.
    當誤差終於反轉時,int256 已經巨大無比,控制器在早該收手之後仍持續命令極限值——它得先「洩掉(unwind)」才行。

Symptom: large overshoot and sluggish, oscillatory recovery after any saturation event — the loop behaves as if it "remembers" too much. In this PLL that means the 40 kHz phase lock slews past its target and takes many cycles to settle.

症狀:任何飽和事件之後都出現大幅過衝與遲緩、振盪式的恢復——迴路彷彿「記得」太多。在這個 PLL 中,這意味著 40 kHz 相位鎖定會衝過目標,需要許多週期才能穩定下來。

The fix: anti-windup解法:抗積分飽和

Anti-windup stops the integrator from accumulating while the output is saturated, so int256 never runs away and the loop responds immediately once the actuator unsaturates. Common schemes:

抗積分飽和在輸出飽和期間阻止積分器繼續累加,讓 int256 永不失控,致動器一脫離飽和迴路就能立即反應。常見作法:

Scheme方案What it does做什麼
Clamping /
conditional integration
Freeze the int256 ← int256 + kI·error update whenever the output is at a limit and the error would push it further into saturation.每當輸出在極限值誤差會把它推得更深入飽和時,凍結 int256 ← int256 + kI·error 更新。
Back-calculation Feed the difference between the commanded and the actual (clamped) output back into the integrator, subtracting it so int256 tracks what the actuator can deliver.命令值實際(被鉗制)輸出之間的差回饋進積分器並減去,讓 int256 追蹤致動器實際能提供的值。
Integrator-state limit Simply clamp int256 itself to a fixed max/min range.直接把 int256 本身鉗制在固定的最大/最小範圍內。
Why this loop specifically needs it: The integral pole sits exactly on the unit circle (z = 1) — infinite DC gain, with nothing to pull it back. Combined with a saturating delay command (d bounded to 17 bits) and a clipping PWM, the accumulator would otherwise wind up indefinitely. Anti-windup bounds it so the phase-injection output stays controllable and the 40 kHz lock recovers cleanly after limiting. This also maps onto Yu & Horng's stability region θ ∈ (0.5π, 1.5π) noted at the top of this section — staying inside the linear regime where the plant gain keeps its sign.
為什麼這個迴路特別需要它: 積分極點正好落在單位圓上(z = 1)——無限直流增益,沒有任何東西把它拉回來。再加上會飽和的延遲命令(d 被限制在 17 位元)與會截波的 PWM,累加器否則會無限飽和。抗積分飽和把它限制住,讓相位注入輸出保持可控,40 kHz 鎖定在受限後能乾淨恢復。這也對應本節開頭提到的 Yu & Horng 穩定區 θ ∈ (0.5π, 1.5π)——留在受控體增益不翻號的線性區內。

Feedback Input Mux (uin3)回授輸入多工器(uin3)

Injection_Method_Switch (SW9)CC_IR_Switch (SW8)uin3 sourceuin3 來源Mode模式
11uinExternal input — open-loop injection from hardware pin外部輸入——來自硬體腳位的開迴路注入
10u_outSelf-lock — oscillator locks to its own phase (no target)自鎖——振盪器鎖到自己的相位(沒有目標)
01Rx_ir_Amped_DBLock to image-reject receive path (target detection)鎖到影像抑制接收路徑(目標偵測)
00Rx_cc_Amped_DBLock to clutter-cancel receive path (target detection)鎖到雜波消除接收路徑(目標偵測)

The mux is the nested ternary at _1Mhz_beta010.v:99, followed by a second register stage:

多工器就是 _1Mhz_beta010.v:99 的巢狀三元運算子,後面再接一級暫存器:

reg uin2, uin3;
always @(posedge clk10MHz) begin
    uin2 <= (Injection_Method_Switch) ? ((ClutterCancel_OR_ImageReject_Switch) ? uin : u_out)
                                      : ((ClutterCancel_OR_ImageReject_Switch) ? Rx_ir_Amped_DB : Rx_cc_Amped_DB);
    uin3 <= uin2;
end

Injection_Method_Switch picks local (1) vs received-over-the-air (0) feedback; ClutterCancel_OR_ImageReject_Switch picks the variant within each group. Both are board slide switches — SW9 (PIN_AE19) and SW8 (PIN_Y11) respectively, per C5G User Manual Table 3-3 in docs/ (input ports @_1Mhz_beta010.v:662, mirrored on the LEDs via r @:22; see also §6 Board Controls). The uin2 → uin3 chain is a two-flip-flop synchronizer clocked at clk10MHz: uin is an asynchronous external pin and the two _DB signals come from clk10MHz1 (a PLL copy), so double-registering avoids metastability before the bit enters the control loop.

Injection_Method_Switch 選擇本地(1)或空中接收(0)的回授;ClutterCancel_OR_ImageReject_Switch 選擇每組內的變體。兩者都是板上滑動開關——分別是 SW9(PIN_AE19)SW8(PIN_Y11),依 C5G 使用手冊表 3-3(docs/;輸入埠 @_1Mhz_beta010.v:662,經 r 鏡射到 LED @:22;另見§6 板上控制)。uin2 → uin3 鏈是一個在 clk10MHz 上的雙正反器同步器:uin 是非同步外部腳位,兩個 _DB 訊號來自 clk10MHz1(PLL 副本),先打兩拍再進控制迴路,避免亞穩態。

Where each 1-bit source comes from每個 1 位元來源從哪裡來

Source來源Origin chain來源鏈
uin Raw input pin (@_1Mhz_beta010.v:12) — feedback arrives already as a digital square wave from outside the chip.原始輸入腳位(@_1Mhz_beta010.v:12)——回授從晶片外以數位方波形式直接進來。
u_out assign u_out = u[12] (@:35) — sign bit of the 13-bit relay_feedback2 output u, i.e. a 40 kHz square wave. Closes the loop on itself: a bypass/sanity mode with no radio path involved.assign u_out = u[12] (@:35)——13 位元 relay_feedback2 輸出 u 的符號位元,即 40 kHz 方波。迴路對自己閉合:不經無線電路徑的旁通/檢查模式。
Rx_ir_Amped_DB Image-reject chain: Rx_IQ_Sum_B (15-bit) → smittch_trigger with SH=7 (the former ×128 Large_Amplifier_ir) → debounce.影像抑制鏈:Rx_IQ_Sum_B(15 位元)→ SH=7 的 smittch_trigger(原 ×128 Large_Amplifier_ir)→ 去抖。
Rx_cc_Amped_DB Clutter-cancel chain: Rx_cc = Rx_Amped − Tx_Q_Delay (Q24.8) in clutter_cancel.v — subtracting the phase-aligned Tx quadrature replica removes direct leakage — then smittch_trigger with SH=7 (the former ×128 Large_Amplifier_cc) → debounce.雜波消除鏈:clutter_cancel.v 中的 Rx_cc = Rx_Amped − Tx_Q_Delay(Q24.8)——減去相位對齊的 Tx 正交複本以移除直接洩漏——再經 SH=7 的 smittch_trigger(原 ×128 Large_Amplifier_cc)→ 去抖。
Why everything is 1-bit: The receive signals are 15-bit (Rx_IQ_Sum_B) and Q24.8 (Rx_cc) fixed-point words, but the feedback loop only needs the zero-crossing timing (phase), not amplitude. Two stages squash them to one bit:
smittch_trigger (smittch_trigger.v) — Schmitt trigger with thresholds ±200 LSB (parameter TH, scaled by FRAC; SH=7 on the "_Amped" instances stands in for the old ×128 amplifier, so they fire at ±1.56 LSB): output goes high above +TH and only drops below −TH; the hysteresis band rejects noise around zero.
Rx_Debounce (Rx_Debounce.v) — the output toggles only after the input has held the opposite level for 16 consecutive clk10MHz1 cycles (1.6 µs); any bounce resets the counter.
為什麼全都是 1 位元: 接收訊號是 15 位元(Rx_IQ_Sum_B)與 Q24.8(Rx_cc)的定點字,但回授迴路只需要過零時序(相位),不需要振幅。兩級把它們壓成一個位元:
smittch_trigger (smittch_trigger.v)——門檻 ±200 LSB(參數 TH,依 FRAC 縮放;「_Amped」實例的 SH=7 取代舊的 ×128 放大器,故在 ±1.56 LSB 觸發)的施密特觸發器:輸出在超過 +200 時變高、只有低於 −200 才變低;遲滯帶排除零點附近的雜訊。
Rx_Debounce (Rx_Debounce.v)——輸入必須連續 16 個 clk10MHz1 週期(1.6 µs)維持相反準位,輸出才翻轉;任何彈跳都會重置計數器。
Where uin3 goes: uin3 feeds PI_antiWindup port .uin (@_1Mhz_beta010.v:90). Inside, it is not used arithmetically — it enters delay_line2 (PI_antiWindup.v:48), a 1-bit delay line clocked at 50 MHz whose tap index d is set by the PI output. The delayed bit re-emerges as u_inj and drives the uinj port of relay_feedback2 (@:34). In other words, the PI loop controls phase by adjusting how many 20 ns steps the selected 1-bit feedback signal is delayed before re-injection into the relay oscillator.
uin3 去哪裡: uin3 餵入 PI_antiWindup.uin(@_1Mhz_beta010.v:90)。在模組內它不做算術運算——它進入 delay_line2 (PI_antiWindup.v:48),一條跑在 50 MHz 的 1 位元延遲線,其抽頭位置 d 由 PI 輸出設定。延遲後的位元以 u_inj 之姿重新出現,驅動 relay_feedback2uinj(@:34)。換句話說,PI 迴路透過調整被選中的 1 位元回授訊號在重新注入繼電振盪器前延遲多少個 20 ns 步階,來控制相位

The commented lines 97–98 and 100 of _1Mhz_beta010.v record earlier mux iterations: one overrode the selection when Clutter_weak was asserted (the FP compare @:603 checking whether the clutter amplitude-correction factor is below 5.0), one hard-wired the clutter-cancel path, and one was a plain passthrough of uin.

_1Mhz_beta010.v 中被註解掉的第 97–98 與 100 行記錄了較早的多工器版本:一版在 Clutter_weak 有效時覆寫選擇(@:603 的浮點比較,檢查雜波振幅校正因子是否低於 5.0)、一版硬接雜波消除路徑、一版則是 uin 的單純直通。

Relation to Clutter Cancellation — CC Inside the Lock Loop與雜波消除的關係——CC 在鎖定迴路之內

In mode 0/0 the clutter canceller of §6 is not a side branch that merely feeds the peak detector — it sits inside the injection-locking loop. The SIL principle is that the oscillator locks its phase to the zero-crossing timing of whatever bit arrives at relay_feedback2.uinj. The mux decides whose zero crossings those are, and clutter cancellation decides what the receive-path candidate actually carries.

在 0/0 模式下,§6 的雜波消除器不是只餵峰值偵測器的旁支——它位於注入鎖定迴路之內。SIL 原理是:振盪器把相位鎖到抵達 relay_feedback2.uinj 的位元的過零時序上。多工器決定那些過零是誰的,雜波消除則決定接收路徑候選訊號實際攜帶什麼。

The problem: the raw receive signal Rx is dominated by direct Tx→Rx leakage (clutter). Leakage phase is constant and carries no target information — injecting raw Rx would make the loop lock to its own transmission, functionally the same as the self-lock mode (u_out). The CC path subtracts an amplitude-matched, phase-aligned replica of the transmit signal so that the zero crossings of the residual are set by the target echo instead:

問題:原始接收訊號 Rx 被 Tx→Rx 直接洩漏(雜波)主導。洩漏的相位是常數、不帶目標資訊——直接注入原始 Rx 會讓迴路鎖到自己的發射上,功能上等同自鎖模式(u_out)。CC 路徑減去一個振幅匹配、相位對齊的發射訊號複本,讓殘差的過零改由目標回波決定:

// clutter_cancel.v, fixed point since 2026-09-04
// 1. amplitude match — ratio Tx/Rx as unsigned Q8.16 (free-running restoring divider)
div_fx #(.NW(32), .DW(16)) FX_Divider (.clk(~clk10MHz4), .num({1'b0, Tx_Q_Amp, 16'b0}), .den(Rx_Amp), .q(ratio_q), ...);
// 2. scale Rx up to the Tx level (15 x 25 signed product, kept as Q24.8)
wire signed [39:0] prod = Rx_IQ_Sum * $signed({1'b0, Clutter_Amp_Ratio});
always @(posedge clk10MHz2) Rx_Amped <= prod[39:8];
// 3. phase-align the Tx replica (sign bits in, 256 x 14-bit RAM delay line)
phase_detector Phase_Detector (~clk10MHz2, 1'b1, phase_diff_, Tx_Q[13], Rx_IQ_Sum[14]);
delay_line #(.W(14)) Variable_Delay_Line (clk10MHz2, Tx_Q_Delay, Tx_Q, phase_diff);
// 4. subtract — residual ≈ target echo (combinational, Q24.8)
assign Rx_cc = Rx_Amped - {{10{Tx_Q_Delay[13]}}, Tx_Q_Delay, 8'b0};

The residual is small (good cancellation removes most of the energy — see §6 signal-level analysis), so it is boosted ×128 and squashed to the 1-bit phase signal the mux consumes:

殘差很小(良好的消除會移除大部分能量——見 §6 訊號位準分析),所以它被放大 ×128,再壓成多工器使用的 1 位元相位訊號:

// _1Mhz_beta010.v — the x128 is the SH=7 shift inside the trigger (Q24.8 input, FRAC=8)
smittch_trigger #(.W(32), .FRAC(8), .SH(7), .TH(200)) Trig_Rx_cc_Amped (clk10MHz1, Rx_cc_Amped_Trig, Rx_cc);  // ±200 hysteresis on x·128
Rx_Debounce DB_Rx_cc_Amped (clk10MHz1, Rx_cc_Amped_DB, Rx_cc_Amped_Trig);       // 16-cycle hold

The mux (0/0) passes Rx_cc_Amped_DB to uin3, and the loop closes through the PI-controlled delay line back into the oscillator:

多工器(0/0)把 Rx_cc_Amped_DB 傳給 uin3,迴路經由 PI 控制的延遲線閉合、回到振盪器:

// PI_antiWindup.v:46–48 — PI output sets the delay-line tap (20 ns steps @ clk50MHz)
wire [16:0] d;
assign d = PI_out[16:0];
delay_line2 U12(.in(uin), .out(u_inj), .d(d), .clk(clk50MHz));

// relay_feedback2.v:34,45 — the injected bit is summed into the resonator input
assign y_in = {{10{uinj[2]}}, uinj[1:0], 6'b0};
res_in <= {{9{f1024[23]}}, 1'b1, 8'b0} + y_in;
Consequence: the quality of the subtraction at _1Mhz_beta010.v:627 directly determines whose phase the PI controller locks the oscillator to. Good cancellation → the loop locks to the target echo, and target range/motion appears in PI_out/data on the UART. Poor cancellation → the leakage still dominates the zero crossings and the loop degenerates toward self-lock, blind to the target.
結果: _1Mhz_beta010.v:627 那個減法的品質,直接決定 PI 控制器把振盪器鎖到誰的相位上。消除得好 → 迴路鎖到目標回波,目標的距離/運動出現在 UART 上的 PI_outdata。消除得差 → 洩漏仍主導過零,迴路退化成自鎖,對目標視而不見。
Clutter_weak tie-in: Clutter_weak = (Clutter_Amp_Ratio > 24'd327680) in clutter_cancel.v compares the Q8.16 ratio (= Tx_amp/Rx_amp) against 5.0 (was an FP_Comparator against 32'h40A00000). A large ratio means Rx is weak relative to Tx — i.e. little leakage to cancel. The abandoned mux iteration at :97 used this flag to auto-fall-back to the image-reject path when cancellation was unnecessary; the current code leaves the choice to the CC_IR switch and only mirrors Clutter_weak on the LEDs (@:22).
Clutter_weak 的關聯: clutter_cancel.v 中的 Clutter_weak = (Clutter_Amp_Ratio > 24'd327680) 把 Q8.16 的比值(= Tx_amp/Rx_amp)與 5.0 比較(原為 FP_Comparator32'h40A00000 比較)。比值大代表 Rx 相對 Tx 很弱——也就是幾乎沒有洩漏可消。:97 被放棄的多工器版本曾用這個旗標在不需要消除時自動退回影像抑制路徑;目前的程式碼把選擇權留給 CC_IR 開關,只把 Clutter_weak 鏡射到 LED (@:22)

Signal Flow Diagram信號流圖

relay_feedback2 u (13-bit, 40kHz) u_0d5→u12 12-reg @2MHz=90° demodulator v = u×u12 25-bit F1 → w 19-bit F2 → x256 22-bit phase error ×256 x256 22-bit PI_antiWindup kI = 3/256 @50kHz kP = 1/2¹⁵ LPF 15.6Hz → data PI_out (24-bit) pwm_in (10-bit) d=PI_out [16:0] 17-bit pwm_in 10-bit data 24-bit delay_line2 128K RAM, d samples PWM → UART tx u_inj 3-bit ← phase correction feedback (u_inj) ← pwm pin ── Feedback input mux (uin3) ────── uin (ext pin) u_out (osc) Rx_ir_Amped_DB Rx_cc_Amped_DB 1-bit 1-bit 1-bit 1-bit MUX Injection_Method CC_IR switches uin3 → PI uin 1-bit

4. Quadrature Transmit Path — DAC Output正交發射路徑——DAC 輸出

Theory ref — Yu & Horng "Yu vs Wu" table (freq-demod row): Wu-specific. This is the Low-IF I/Q transmit: the DAC emits 40 kHz I/Q, which the external 5.8 GHz LO up-converts to 5.8 GHz + 40 kHz (image sideband suppressed ≈62 dB). Yu's ultrasonic radar has no I/Q transmit — it radiates the SIL oscillation directly and demodulates with the T/4 self-multiply discriminator instead. [Yu guide]
理論對照 — Yu & Horng「Yu vs Wu」表(頻率解調列): Wu 專屬。這是低中頻 I/Q 發射:DAC 送出 40 kHz 的 I/Q,由外部 5.8 GHz 本地振盪升頻到 5.8 GHz + 40 kHz(影像邊帶抑制約 62 dB)。Yu 的超音波雷達沒有 I/Q 發射——它直接輻射 SIL 振盪,改用 T/4 自乘鑑別器解調。[Yu 導讀]

Two 14-bit DAC channels output a quadrature (IQ) 40 kHz transmit waveform. The Q channel is the raw oscillator output; the I channel is the Q channel delayed by exactly 90° (62.5 cycles at 10 MHz), creating the orthogonal component needed for image rejection on the receive side.

兩個 14 位元 DAC 通道輸出正交(IQ)40 kHz 發射波形。Q 通道是原始振盪器輸出;I 通道是 Q 通道剛好延遲 90°(10 MHz 下 62.5 週期)的版本,構成接收端影像抑制所需的正交分量。

DAC Pin SummaryDAC 腳位摘要 (click to expand)
Pin腳位Value數值Description說明
DAC_CLK_Aclk10MHzSampling clock for channel A — 10 MHzA 通道取樣時脈——10 MHz
DAC_CLK_Bclk10MHzSampling clock for channel B — 10 MHzB 通道取樣時脈——10 MHz
DAC_WRT_Aclk10MHzWrite enable for channel AA 通道寫入致能
DAC_WRT_Bclk10MHzWrite enable for channel BB 通道寫入致能
DAC_MODE1Dual-channel mode enabled雙通道模式啟用
DAC_DA[13:0]nv_62d5_TxI channel — Q delayed 90° + ×1.022 amplitude correction, offset-binaryI 通道——Q 延遲 90°+×1.022 振幅校正,偏移二進位
DAC_DB[13:0]nv_reg_d3Q channel — raw oscillator output, pipeline-aligned, offset-binaryQ 通道——原始振盪器輸出,管線對齊,偏移二進位

Signal Derivation訊號推導

DACChannel通道Step步驟Operation操作Result結果Where (.v file)位置(.v 檔)
DAC_DA I 1relay_feedback2 outputrelay_feedback2 輸出v [13:0] signed, 40 kHzrelay_feedback2.v (U2 @:34)
262.5-cycle delay @ 10 MHz (= 90°)10 MHz 下延遲 62.5 週期(= 90°)v_62d5delay_62d5_10MHz.v (@_1Mhz_beta010.v:186)
3Fixed-point → IEEE 754 FP定點 → IEEE 754 浮點v_62d5_FPConvert_b2FP.v (@:220)
4FP multiply × 1.022 (channel balance)浮點乘 ×1.022(通道平衡)v_62d5_Amped_FPMULT.v (@:225)
5FP → fixed-point + MSB flip (offset binary)浮點 → 定點+MSB 翻轉(偏移二進位)nv_62d5_Tx → DAC_DAConvert_FP2b.v + _1Mhz_beta010.v (@:231,236)
DAC_DB Q 1relay_feedback2 output + MSB fliprelay_feedback2 輸出+MSB 翻轉v_Tx = {~v[13], v[12:0]}_1Mhz_beta010.v (@:188)
2Register at negedge clk10MHz在 negedge clk10MHz 鎖存nv_reg_1Mhz_beta010.v (@:237)
33 pipeline delay regs (align latency with I path)3 級管線延遲暫存器(與 I 路徑延遲對齊)nv_reg_d3 → DAC_DB_1Mhz_beta010.v (@:257; DAC_DB @:206)

Fixed point since 2026-09-04. The step table above is the original Tx chain. Today v_rawtx_gain (KEY2) → hilbert_90 gives I = y_90 and Q = y_0 × 1/1.022 (fixed point, see §8); Convert_b2FP/MULT/Convert_FP2b and nv_reg no longer exist (the IEEE-754 IP was deleted from the project on 2026-09-04).

2026-09-04 起改為定點。上表是原始的 Tx 鏈。現在 v_rawtx_gain(KEY2)→ hilbert_90 給出 I = y_90、Q = y_0 × 1/1.022(定點,見 §8);Convert_b2FPMULTConvert_FP2bnv_reg 已不存在(IEEE-754 IP 於 2026-09-04 自專案刪除)。

(Historical) All IP modules above (delay_62d5_10MHz, Convert_b2FP, MULT, Convert_FP2b) were instantiated and wired in _1Mhz_beta010.v; the @:NNN notes give the line in _1Mhz_beta010.v where each instance / assignment lives. The MSB-flip and pipeline-register logic (Q channel, and step 5's nv_62d5_Tx) are plain assign/always blocks in _1Mhz_beta010.v itself.

上述所有 IP 模組(delay_62d5_10MHzConvert_b2FPMULTConvert_FP2b)都_1Mhz_beta010.v 中實例化並接線@:NNN 註記給出每個實例/指定在 _1Mhz_beta010.v 中的行號。MSB 翻轉與管線暫存器邏輯(Q 通道,以及步驟 5 的 nv_62d5_Tx)則是 _1Mhz_beta010.v 本身的普通 assignalways 區塊。

MSB-flip lines (2's-complement → offset-binary, {~x[13], x[12:0]}): the I channel (step 5) is at _1Mhz_beta010.v:236 (assign nv_62d5_Tx = {~nv_62d5[13], nv_62d5[12:0]}); the Q channel (step 1) is at _1Mhz_beta010.v:188 (assign v_Tx = {~v[13], v[12:0]}). The active DAC outputs use these (DAC_DA = nv_62d5_Tx @:205, DAC_DB = nv_reg_d3 @:206); the similar v_62d5_Tx at :189 belongs to a commented-out path.

MSB 翻轉行(二補數 → 偏移二進位,{~x[13], x[12:0]}):I 通道(步驟 5)在 _1Mhz_beta010.v:236assign nv_62d5_Tx = {~nv_62d5[13], nv_62d5[12:0]});Q 通道(步驟 1)在 _1Mhz_beta010.v:188assign v_Tx = {~v[13], v[12:0]})。實際使用的 DAC 輸出用的是這些(DAC_DA = nv_62d5_Tx @:205、DAC_DB = nv_reg_d3 @:206);:189 那個相似的 v_62d5_Tx 屬於被註解掉的路徑。

MSB flip: {~v[13], v[12:0]} converts 2's-complement signed to offset-binary (DAC format). In 2's complement, MSB=1 means negative; in offset binary, MSB=1 means positive (mid-scale = 0x2000). Flipping only the MSB switches between the two representations.
MSB 翻轉: {~v[13], v[12:0]} 把二補數有號數轉成偏移二進位(DAC 格式)。二補數中 MSB=1 代表負數;偏移二進位中 MSB=1 代表正數(中間值=0x2000)。只翻轉 MSB 就能在兩種表示法之間切換。
Why flipping the MSB gives offset-binary — the math為什麼翻轉 MSB 就是偏移二進位——數學推導 (click to expand)

It works because flipping the MSB is arithmetically identical to adding 8192, and 8192 is exactly the offset between the two representations. Three steps:

之所以可行,是因為翻轉 MSB 在算術上等同於加 8192,而 8192 正好是兩種表示法之間的偏移。分三步:

1. The two formats differ by a constant offset of 2¹³ = 81921. 兩種格式只差一個常數偏移 2¹³ = 8192

Both formats use the same lower 13 bits with the same positive weights; only the meaning of the whole code differs. Two's complement gives a signed value T (−8192…+8191); offset binary reads the code as a plain unsigned U and the analog stage treats U − 8192 as the value. To make the DAC output the signed value T:

兩種格式的低 13 位元相同、權重也同為正;不同的只是整個編碼的意義。二補數給出有號值 T(−8192…+8191);偏移二進位把編碼讀成無號的 U,類比級把 U − 8192 當作數值。要讓 DAC 輸出有號值 T

U − 8192 = T ⇒ U = T + 8192 // offset binary = signed value + half-scale

2. Adding 2¹³ to a 14-bit word only ever toggles the MSB2. 對 14 位元字組加 2¹³ 只會翻轉 MSB

8192 lives entirely in bit 13. Adding it (mod 2¹⁴):

8192 完全位於位元 13。加上它(mod 2¹⁴):

Either way only bit 13 flips and bits 0–12 are unchanged — exactly what XOR-ing the MSB does:

無論哪種情況,只有位元 13 翻轉、位元 0–12 不變——正是對 MSB 做 XOR 的效果:

T + 2¹³ (mod 2¹⁴) ≡ T ⊕ 2¹³

3. Therefore {~x[13], x[12:0]} is the +8192 offset3. 所以 {~x[13], x[12:0]} 就是 +8192 偏移

Flipping the MSB and leaving the rest = adding 8192 = two's-complement → offset-binary. No adder, no extra logic — one inverter on one wire. (The plot below is the picture of this: a single straight line out = in + 8192.)

翻轉 MSB、其餘不動=加 8192=二補數 → 偏移二進位。不需要加法器、不需要額外邏輯——一條線上一個反相器。(下方的圖就是這件事的圖像:一條直線 out = in + 8192。)

Bit-weight view — the deepest reason: In two's complement the MSB has weight −2¹³; in the DAC's unsigned/offset reading the same bit has weight +2¹³. The lower 13 bits are identical in both. The only term that changes sign between the encodings is the MSB, so the only bit you must touch to switch representations is the MSB. Flipping it swaps that one term from −2¹³·b₁₃ to +2¹³·b₁₃ — the entire difference between the two formats.
位元權重觀點——最深層的原因: 二補數中 MSB 的權重是 −2¹³;在 DAC 的無號/偏移讀法中,同一個位元權重是 +2¹³。低 13 位元在兩種編碼中完全相同。兩種編碼之間唯一變號的項就是 MSB,所以要切換表示法只需動 MSB 這一個位元。翻轉它就把那一項從 −2¹³·b₁₃ 換成 +2¹³·b₁₃——這正是兩種格式的全部差異。

Mapping of the 14-bit signed value to the DAC output code. Flipping the MSB is exactly adding half-scale (+8192), so the transfer curve is a single straight, monotonic line — no wrap, no discontinuity:

14 位元有號值到 DAC 輸出碼的映射。翻轉 MSB 正是加上半刻度(+8192),所以轉換曲線是一條筆直、單調的直線——沒有迴繞、沒有不連續:

signed v[13:0] → DAC code (out = in + 8192) −8192 0 +8191 signed input v[13:0] (2's complement) 0 8192 16383 DAC code (offset bin) −V_FS 0 V +V_FS analog out 0x2000 → 0x0000 (min, −full) 0x0000 → 0x2000 (mid, 0 V) 0x1FFF → 0x3FFF (max, +full)
signed input有號輸入2's-comp二補數flip MSB →翻轉 MSB →offset-bin偏移二進位DAC codeDAC 碼analog out類比輸出
−81920x20000x00000most negative — −V_FS (≈ −1 V)最負——−V_FS(≈ −1 V)
00x00000x20008192mid-scale — 0 V (common-mode)中間刻度——0 V(共模)
+81910x1FFF0x3FFF16383most positive — +V_FS (≈ +1 V)最正——+V_FS(≈ +1 V)

The line is monotonic and continuous: the most-negative signed code (−8192) lands at DAC code 0 (negative full-scale), signed zero lands at mid-scale (0x2000 = 0 V differential), and the most-positive code (+8191) lands at 0x3FFF (positive full-scale). That is why a bare {~x[13], x[12:0]} is sufficient — no adder needed, since XOR-ing the sign bit is the +8192 offset.

這條線單調且連續:最負的有號碼(−8192)落在 DAC 碼 0(負滿刻度),有號零落在中間刻度(0x2000=差動 0 V),最正的碼(+8191)落在 0x3FFF(正滿刻度)。這就是為什麼一個單純的 {~x[13], x[12:0]} 就夠了——不需要加法器,因為對符號位元做 XOR 就是 +8192 偏移。

Analog voltage range — board-dependent: The DAC pin set (DAC_DA/DB[13:0], DAC_CLK_A/B, DAC_WRT_A/B, DAC_MODE) is the Terasic THDB-ADA AD/DA daughter-card convention, which carries a dual 14-bit AD9767 DAC. The AD9767 is a current-output part; the actual output voltage is set by the card's output stage (full-scale current IOUTFS + load / op-amp), not by anything in _1Mhz_beta010.qsf. Nominal full scale on the THDB-ADA is ≈ 2 Vpp per channel, so ±V_FS ≈ ±1 V with mid-scale ≈ 0 V — but treat these as typical values and confirm against your THDB-ADA configuration / output load. The code → voltage map stays linear and monotonic regardless of the absolute scale: code 0 = −full, 8192 = mid, 16383 = +full.
類比電壓範圍——依板卡而定: DAC 腳位組(DAC_DA/DB[13:0]DAC_CLK_A/BDAC_WRT_A/BDAC_MODE)是 Terasic THDB-ADA AD/DA 子卡的慣例,卡上是雙通道 14 位元 AD9767 DAC。AD9767 是電流輸出元件;實際輸出電壓由卡上的輸出級(滿刻度電流 IOUTFS+負載/運算放大器)決定,_1Mhz_beta010.qsf 無關。THDB-ADA 的標稱滿刻度約為每通道 2 Vpp,即 ±V_FS ≈ ±1 V、中間刻度 ≈ 0 V——但請把這些當成典型值,依你的 THDB-ADA 組態/輸出負載確認。無論絕對刻度為何,碼 → 電壓映射都保持線性且單調:碼 0=負滿、8192=中間、16383=正滿。
Worked example — gain, offset & overflow wraparound (formula chain)實例——增益、偏移與溢位迴繞(公式鏈) (click to expand)

A worked illustration of why a gained/offset signal must respect the bus width before the {~x[13], x[12:0]} conversion. Starting from a full-scale 14-bit sine, applying a float gain and a DC offset, then forcing the result back through a 14-bit signed field, the overflow wraps (folds) instead of clipping. The formulas used to generate the plots:

一個實例,說明為什麼加了增益/偏移的訊號在 {~x[13], x[12:0]} 轉換前必須遵守匯流排位寬。從滿刻度的 14 位元弦波出發,施加浮點增益與直流偏移,再硬塞回 14 位元有號欄位,溢位會迴繞(摺疊)而不是截波。產生這些圖的公式如下:

↘ drag the bottom-right corner to resize — content scales with the box

Plot data generation — formula chain

original 14-bit sine → ×1.5 float → −5000 offset → MSB-flip read as 14-bit signed (wrapped) · sampled at fs = 1 Hz (t = n·1 s)
A = 8191P = 50/1.5 ≈ 33.33G = 1.5 OFF = 5000fs = 1 Hzn = 0 … 200
1
original 14-bit signed sine (quantized samples)
x[n] = round( A · sin(2πn / P) )
2
to float, multiply ×1.5, round back to fixed  (→ 15-bit signed)
y[n] = round( G · x[n] ) = round( 1.5 · x[n] )
3
subtract DC offset 5000
z[n] = y[n] − OFF = round(1.5·x[n]) − 5000
4
{ ~z[13], z[12:0] } read as 14-bit signed = wrap into [−8192, +8191]  (plotted value)
a[n] = wrap( z[n] ) = ( ( z[n] + 8192 ) mod 16384 ) − 8192
nonneg modulo:  x mod m = ((x % m) + m) % m
5
overflow flag (red dots = wrapped samples)
folded[n] = ( | z[n] | > 8191 )
~
reference envelope (green dashed) — no round, no wrap
zideal(t) = 1.5·8191·sin(2πt/P) − 5000
xy
data → SVG pixel mapping
xpx = MX + (t / tmax)·PW
ypx = TOP + ((ymax−v)/(ymax−ymin))·PH
tmax=200,  ymin=−17900,  ymax=7900
representative values (envelope extremes)
sinxy=1.5xz=y−5000a=wrap(z)
000−5000−5000
+1819112287+7287+7287
−1−8191−12286−17286−902 (fold)
one-line composite
a[n] = ( ( round(1.5·round(8191·sin(2πn/33.33))) − 5000 + 8192 ) mod 16384 ) − 8192
↘ drag the bottom-right corner to resize

z = (sine × 1.5, fixed) − 5000  → wrapped to 14-bit signed

x[n]=round(8191·sin) → ×1.5 → round → − 5000 → {~z[13],z[12:0]} read as 14-bit signed  ·  freq ×3 → T ≈ 33.3 s (≈33 samples/period, 6 periods shown)

y[n] = round(1.5·x[n]) (peak 12287) → z[n] = y[n] − 5000 (range −17286 … +7287) → {~z[13], z[12:0]} as signed 14-bit = a[n] = ((z+8192) mod 16384) − 8192
a[n] (14-bit signed, wrapped, continuous) z[n] intended (−17286…+7287) folded sample (|z|>8191) −5000 offset
With −5000 the asymmetry flips: the positive peak now lands at +7287 — inside ±8191, so it does NOT fold (clean half-sine). But the negative lobe drops to −17286, even past the 15-bit floor, so the broad span n ≈ 55…95 wraps: as z crosses −8192 the value jumps to +8191 and descends through the rail down to ≈ −902 at the trough. One clean lobe, one badly folded lobe.

Interactive waveform plots built from these formulas (self-contained HTML in plot/): plot/sine_x1p5_signed14.html (×1.5 full-scale fold) and plot/sine_x1p5_minus5000.html (×1.5 − 5000, freq ×3). The key lesson mirrors the Tx amplitude correction: the hardware keeps the corrected value in range (small ×1.022 trim), so it never overflows the 14-bit DAC field — a large gain like ×1.5 would, and the wrap would turn clean peaks into worst-case discontinuities.

由這些公式建立的互動式波形圖(plot/ 中的自足 HTML):plot/sine_x1p5_signed14.html(×1.5 滿刻度摺疊)與 plot/sine_x1p5_minus5000.html(×1.5 − 5000,頻率 ×3)。關鍵教訓呼應 Tx 振幅校正:硬體讓校正後的值保持在範圍內(小小的 ×1.022 微調),所以永遠不會溢出 14 位元 DAC 欄位——像 ×1.5 這樣的大增益就會,而迴繞會把乾淨的波峰變成最糟糕的不連續。

Pipeline alignment: The I-channel FP pipeline (b→FP → MULT → FP→b at 200 MHz) takes several clock cycles. The Q channel adds 3 × negedge clk10MHz delays (nv_reg_d3) to keep I and Q time-aligned at the DAC outputs.
管線對齊: I 通道的浮點管線(200 MHz 下 b→FP → MULT → FP→b)需要數個時脈週期。Q 通道加上 3 個 negedge clk10MHz 延遲(nv_reg_d3),讓 I 與 Q 在 DAC 輸出端保持時間對齊。
× 1.022 — what "channel balance" means×1.022——「通道平衡」是什麼意思 (click to expand)
This is the I/Q amplitude-balance trim for image rejection. In a quadrature (I/Q) transmitter the I and Q paths must be equal in amplitude and exactly 90° apart; any gain mismatch between the two DAC channels (and their analog filters/buffers) leaks a residual image sideband that caps the achievable rejection. The Q channel (DAC_DB) is left at unity gain, so the I channel (DAC_DA) carries the whole correction — scaled up by ≈2.2% to cancel the hardware mismatch (empirically tuned; the source lists ×1.00, ×1.022, ×1.05 as the trim options tried, supporting the ≈62 dB image suppression cited in §4).

The factor lives in _1Mhz_beta010.v as the multiplier constant 32'h3f82d0e5 — the IEEE-754 single-precision literal for ≈ 1.022 (sign 0, exponent 0x7F = 2⁰, mantissa 0x02D0E5/2²³ ≈ 0.022 → 1.022). It is applied in the floating-point domain via the Convert_b2FP → MULT → Convert_FP2b chain (step 4 above). The Rx side has an analogous balance trim, 32'h3f83d70a1.03.

Note: this describes the original trim (boost I by ×1.022). The current code applies the same balance the other way round (gain < 1) — see §8 I/Q Amplitude Balance & Overflow.
這是影像抑制用的 I/Q 振幅平衡微調。在正交(I/Q)發射機中,I 與 Q 路徑必須振幅相等、相位正好相差 90°;兩個 DAC 通道(及其類比濾波器/緩衝器)之間的任何增益不匹配都會洩漏殘餘的影像邊帶,限制可達到的抑制。Q 通道(DAC_DB)保持單位增益,所以整個校正由 I 通道(DAC_DA)承擔——放大約 2.2%以抵消硬體不匹配(經驗調校;原始碼列出曾嘗試 ×1.00×1.022×1.05 三個微調選項,支撐 §4 所引的約 62 dB 影像抑制)。

這個係數以乘法器常數 32'h3f82d0e5 存在於 _1Mhz_beta010.v 中——≈1.022 的 IEEE-754 單精度字面值(符號 0、指數 0x7F=2⁰、尾數 0x02D0E5/2²³ ≈ 0.022 → 1.022)。它經由 Convert_b2FP → MULT → Convert_FP2b 鏈(上面的步驟 4)在浮點域施加。Rx 端有對應的平衡微調,32'h3f83d70a1.03

注意:這裡描述的是原始微調(把 I 放大 ×1.022)。目前的程式碼把同樣的平衡反過來施加(增益 < 1)——見 §8 I/Q 振幅平衡與溢位

Hilbert Transform & Quadrature希爾伯特變換與正交

The quadrature (Q) channel v_62d5 is a 90° phase-shifted copy of the I channel v. The clean textbook way to make a 90° shift across a signal is the Hilbert transform; this design uses a cheaper fixed T/4 delay that is only exact at 40 kHz. Both aim at the same goal: build the complex analytic signal so the Adder can cancel the image sideband.

正交(Q)通道 v_62d5 是 I 通道 v90° 相移複本。對整個訊號做 90° 相移的教科書正解是希爾伯特變換;本設計採用更便宜的固定 T/4 延遲,只在 40 kHz 精確。兩者目標相同:建立複數解析訊號,讓 Adder 能消除影像邊帶。

What the Hilbert transform is: A linear operator that takes a real signal x(t) and returns another real signal x̂(t) — every frequency component phase-shifted by −90°, amplitude unchanged. Time-domain it is convolution with 1/(πt); frequency-domain it just multiplies the spectrum by a pure imaginary sign function.
希爾伯特變換是什麼: 一個線性運算子:輸入實數訊號 x(t),輸出另一個實數訊號 x̂(t)——每個頻率分量相位偏移 −90°、振幅不變。時域上是與 1/(πt) 的摺積;頻域上只是把頻譜乘上一個純虛數的符號函數。
H(f) = −j·sgn(f) = −j for f > 0, +j for f < 0 // multiply by −j = rotate positive freqs −90° in the complex plane

The output x̂(t) is still real (the ±j on ±f are conjugate-symmetric, so a real input gives a real output). The complex number only appears in the next step — the analytic signal, which uses x̂ as the imaginary part:

輸出 x̂(t) 仍是實數(±f 上的 ±j 共軛對稱,實數輸入得到實數輸出)。複數只出現在下一步——解析訊號,它把 x̂ 當作虛部:

x_a(t) = x(t) + j·x̂(t) // spectrum is one-sided (negative frequencies cancelled) → this is what kills the image
Delay vs. true Hilbert transform: A pure time delay of T/4 gives exactly −90° only at the design frequency (40 kHz) and its odd harmonics; a real Hilbert transform gives −90° at every frequency. Because the radar signal is a narrowband 40 kHz tone, the single-frequency delay is a valid, far cheaper approximation than a broadband Hilbert FIR.
延遲 vs 真正的希爾伯特變換: 純時間延遲 T/4 只在設計頻率(40 kHz)及其奇次諧波上剛好是 −90°;真正的希爾伯特變換在每個頻率都給 −90°。因為雷達訊號是窄帶 40 kHz 單音,單頻延遲是有效且遠比寬帶希爾伯特 FIR 便宜的近似。
True Hilbert transform真正的希爾伯特變換T/4 delay (delay_62d5_10MHz)T/4 延遲(delay_62d5_10MHz
Phase shift−90° at all frequencies (broadband)所有頻率皆 −90°(寬帶)−90° only at 40 kHz (narrowband)只在 40 kHz 為 −90°(窄帶)
Amplitude×1 across the band全頻帶 ×1×1 (a pure delay never changes gain)×1(純延遲不改變增益)
Costlong antisymmetric FIR長的反對稱 FIR≈62.5-tap shift register @ 10 MHz10 MHz 下約 62.5 級移位暫存器
Suitswideband signals寬帶訊號the single 40 kHz tone here本案的 40 kHz 單音
Mapping to this design: v = x(t) is I; v_62d5 ≈ x̂(t) is Q (the 90° copy from the T/4 delay line, still a real 14-bit value). Only when I and Q are treated as I + jQ does the signal become complex — and the Adder-based combine exploits the resulting one-sided spectrum (positive/negative-frequency phase cancellation) to suppress the image, the ≈62 dB rejection cited in §4. See the T/4 = 6.25 µs derivation and the data_0d5 half-sample trim in Signal Derivation.
對應到本設計: v=x(t) 是 I;v_62d5 ≈ x̂(t) 是 Q(來自 T/4 延遲線的 90° 複本,仍是實數的 14 位元值)。只有把 I 與 Q 當作 I + jQ 時訊號才變成複數——而以 Adder 為基礎的合成利用由此產生的單邊頻譜(正/負頻率相位消除)來抑制影像,即 §4 所引的約 62 dB 抑制。T/4=6.25 µs 的推導與 data_0d5 半取樣微調見訊號推導

Signal Flow信號流

relay_feedback2 v [13:0] 40 kHz ── I Channel (DAC_DA) ────── delay_62d5 62.5 cyc = 90° v_62d5 b→FP @200MHz MULT × 1.022 3f82d0e5 FP→b nv_62d5 MSB flip {~[13],[12:0]} nv_62d5_Tx DAC_DA I channel → Tx antenna sin(40kHz) ── Q Channel (DAC_DB) ────── MSB flip {~[13],[12:0]} v_Tx reg nv_reg @neg d1 → d2 → d3 3 × negedge 10MHz align nv_reg_d3 DAC_DB Q channel → Tx antenna cos(40kHz) 90° apart at DAC output CLK: DAC_CLK_A = DAC_CLK_B = DAC_WRT_A = DAC_WRT_B = clk10MHz DAC_MODE = 1

5. Image-Rejection Q-Channel 90° — hilbert_90 (was delay_62d5_10MHz)影像抑制 Q 通道 90° — hilbert_90(原 delay_62d5_10MHz)

Theory ref — Yu & Horng §B-②, eq. (A29)–(A32): The cleanest correspondence: 62.5 cycles @ 10 MHz = 6.25 µs = T/4 of 40 kHz = 90°, identical to Yu's quadrature delay T/4 = π/(2ω_n). Yu uses T/4 in the self-multiply FM discriminator (w = LP{u(t)·u(t−T/4)} ∝ Δω, cos→sin at max slope); Wu reuses the same 90° to form the Hartley image-reject quadrature pair. Yu's guide states it directly: "Yu's T/4 time delay ⟷ Wu's 90° I/Q phase split." [Yu guide]
理論對照 — Yu & Horng §B-②,式 (A29)–(A32): 最乾淨的對應:10 MHz 下 62.5 週期=6.25 µs=40 kHz 的 T/4=90°,與 Yu 的正交延遲 T/4 = π/(2ω_n) 完全相同。Yu 把 T/4 用在自乘 FM 鑑別器(w = LP{u(t)·u(t−T/4)} ∝ Δω,cos→sin 於最大斜率處);Wu 則重用同一個 90° 構成 Hartley 影像抑制正交對。Yu 的導讀直接寫道:「Yu 的 T/4 時間延遲 ⟷ Wu 的 90° I/Q 相位分割」。[Yu 導讀]

Delays the 14-bit signed Q sample by exactly 62.5 cycles of 10 MHz = 6.25 µs = one quarter period of 40 kHz. This is the key building block for image rejection in the Rx path: delaying the Q-channel ADC by 90° so it can be added to the I-channel to cancel the unwanted image frequency.

把 14 位元有號的 Q 取樣精確延遲 62.5 個 10 MHz 週期6.25 µs=40 kHz 的四分之一週期。這是 Rx 路徵影像抑制的關鍵構件:把 Q 通道 ADC 延遲 90°,使其能與 I 通道相加、消除不要的影像頻率。

Fixed point since 2026-09-04. The Rx image reject no longer runs in IEEE-754: the two Convert_b2FP, the ×1.0 MULT, delay_62d5_10MHz_FP (FP Adder + MULT ×0.5 around the 62-register chain) and the FP Adder are gone, with the 200/400 MHz PLL clocks they ran on. Both ADC buses are captured on the falling edge of the encode clock clk10MHz1, I is balanced by ×1/1.03 in fixed point (Rx_I_ * 63627 >>> 16), Q goes through the fixed-point delay_62d5_10MHz (the module the Tx path used before its Hilbert pair), and Rx_IQ_Sum_B = I + Q_delay is a 15-bit register. The old Rx_I_d1/d2/ctrl trim registers only compensated FP pipeline latency and are gone: the Q lag is exactly 62.5 samples by construction. Simulation (tb_rx_path.v): target peak 5909 vs 5910 before, image residual 114 vs 141 before. Next step for this block: the hilbert_90 pair, as on Tx.

2026-09-04 起改為定點。Rx 影像抑制不再以 IEEE-754 運算:兩個 Convert_b2FP、×1.0 的 MULTdelay_62d5_10MHz_FP(62 級暫存器鏈外圍的浮點 Adder+MULT ×0.5)與浮點 Adder 均已移除,連同它們所用的 200/400 MHz PLL 時脈。兩路 ADC 匯流排在編碼時脈 clk10MHz1下降邊取樣,I 以定點 ×1/1.03 平衡(Rx_I_ * 63627 >>> 16),Q 經定點的 delay_62d5_10MHz(Tx 路徑在改用 Hilbert 對之前所用的模組),Rx_IQ_Sum_B = I + Q_delay 是 15 位元暫存器。舊的 Rx_I_d1/d2/ctrl 修整暫存器只是補償浮點管線延遲,已刪除:Q 落後恰為 62.5 取樣,由結構保證。模擬(tb_rx_path.v):目標峰值 5909(原 5910),影像殘差 114(原 141)。此區塊的下一步:與 Tx 相同的 hilbert_90 對。

Hilbert pair since 2026-09-05. The fixed 62.5-sample delay is gone from the Rx path too. Two hilbert_90 instances (L=62, clk10MHz4): Q feeds one and its y_90 is Q shifted by exactly −90° at any frequency; I feeds the other and its y_0 is the matching midpoint tap x[n-1-L], so both legs carry the same 63-sample bulk delay by construction. Rx_IQ_Sum_B = Rx_I_0 + Rx_Q_90. L only sets the quadrature gain sin(2πf·L/fclk) = 0.99994 at 40 kHz. tb_rx_path.v at 40 / 44 kHz: image residual 87 / 57 LSB (the 1/1.03 balance floor) versus 114 / 552 with the 62.5-sample delay, whose 90° held only at exactly fclk/250. The sections below on how 62.5 cycles were achieved, the signal-flow diagram and the port list describe delay_62d5_10MHz, now kept in the tree but unused; the theory (T/4 = 90°, Hartley sum) is unchanged.

2026-09-05 起改用 Hilbert 對。Rx 路徑也不再使用固定 62.5 取樣延遲。兩個 hilbert_90 實例(L=62,clk10MHz4):Q 餵入其一,其 y_90 就是 Q 精確移相 −90°、與頻率無關;I 餵入另一個,其 y_0 是對應的中點抽頭 x[n-1-L],兩路由結構保證相同的 63 取樣整體延遲。Rx_IQ_Sum_B = Rx_I_0 + Rx_Q_90。L 只決定正交路增益 sin(2πf·L/fclk),40 kHz 時為 0.99994。tb_rx_path.v 於 40/44 kHz:影像殘差 87/57 LSB(即 1/1.03 平衡底線),對比 62.5 取樣延遲的 114/552——後者的 90° 只在 fclk/250 成立。下方「62.5 週期如何實現」、信號流圖與埠列表描述的是 delay_62d5_10MHz,該檔保留於樹中但已不再實例化;理論(T/4=90°、Hartley 相加)不變。

Where I/Q Mixer / Low-IF / Hartley Lives in the VerilogI/Q 混頻器/低中頻/Hartley 在 Verilog 的哪裡

The "frequency demodulation = I/Q mixer + Low-IF (40 kHz) + Hartley image rejection" chain splits into an analog half and a digital (FPGA) half — only the second half is in Verilog.

「頻率解調=I/Q 混頻器+低中頻(40 kHz)+Hartley 影像抑制」這條鏈分成類比一半與數位(FPGA)一半——只有後者在 Verilog 裡。

1. I/Q mixer (down-conversion) — NOT in Verilog1. I/Q 混頻器(降頻)——不在 Verilog 裡

The actual mixing (RF × LO) happens in the analog front end with a 5.8 GHz quadrature LO. The FPGA only generates the quadrature Tx through the DACs (§4) and receives the two already-down-converted 40 kHz IF channels through the ADCs (in _1Mhz_beta010.v):

實際的混頻(RF × LO)發生在類比前端,使用 5.8 GHz 正交本地振盪。FPGA 只透過 DAC 產生正交 Tx(§4),並經 ADC 接收兩路已降頻的 40 kHz 中頻通道(在 _1Mhz_beta010.v 中):

321: assign Rx_Q_ = {~ADC_DA[13], ADC_DA[12:0]};  // Q channel for Rx
322: assign Rx_I_ = {~ADC_DB[13], ADC_DB[12:0]};  // I channel for Rx
Note: a digital I/Q mixer module IQ_demod.v exists but is commented out / not instantiated存在一個數位 I/Q 混頻器模組 IQ_demod.v,但已被註解掉/未實例化

IQ_demod.v (MULT Tx_I×Rx, Tx_Q×Rx) is a digital I/Q mixer, but it is commented out in the top module:

IQ_demod.vMULT Tx_I×RxTx_Q×Rx)是一個數位 I/Q 混頻器,但在頂層模組中被註解掉了:

713: //  IQ_demod IQ_1 (clk_400, Tx_I_FP, Tx_Q_FP, Rx_cc, IQ_r, IQ_theta);
714: //  IQ_demod IQ_1 (clk_400, Tx_I_FP, Tx_Q_FP, Rx_cc, IQ_I, IQ_Q);

Why the FPGA sees 40 kHz, not 5.8 GHz為什麼 FPGA 看到的是 40 kHz 而不是 5.8 GHz

A common confusion with cos(ωLO − ωRF)t (eq. 2.16): the 40 kHz is not the LO — it is the difference itself. The echo and the LO both sit at ~5.8 GHz and differ by only ~40 kHz:

關於 cos(ωLO − ωRF)t(式 2.16)的常見混淆:40 kHz 不是本地振盪——它就是差值本身。回波與 LO 都在約 5.8 GHz,只差約 40 kHz:

ωLO − ωRF = ~5.8 GHz − ~5.8 GHz ≈ 40 kHz (the IF / beat frequency)

The subtraction (mixing) happens in the analog front end — the 5.8 GHz quadrature mixer / self-injection-locked oscillator — whose output is the low 40 kHz beat. The FPGA's ADC only ever samples that 40 kHz; it never digitizes 5.8 GHz.

相減(混頻)發生在類比前端——5.8 GHz 正交混頻器/自我注入鎖定振盪器——其輸出就是低頻的 40 kHz 差拍。FPGA 的 ADC 只取樣那個 40 kHz;它從不數位化 5.8 GHz

5.8 GHz echo --+ +--> [ analog mixer / SIL ] --> 40 kHz IF --> ADC --> FPGA 5.8 GHz LO ---+ (does the subtraction) (all digital work @ 40 kHz)

Nyquist for 40 kHz needs only >80 kHz sampling; the ADC/processing clocks (250 kHz, 10 MHz) are far above that, so it is trivial. Digitizing 5.8 GHz directly would need an >11.6 GHz ADC — impossible here, which is the entire reason for the analog down-conversion. It is also why the digital IQ_demod.v mixer is left unused: the mix to 40 kHz is analog, and the ADCs receive the two already-down-converted 40 kHz IF channels (Rx_I_, Rx_Q_, lines 321–322).

40 kHz 的 Nyquist 只需要 >80 kHz 取樣率;ADC/處理時脈(250 kHz、10 MHz)遠高於此,輕而易舉。直接數位化 5.8 GHz 需要 >11.6 GHz 的 ADC——在這裡不可能,這正是採用類比降頻的全部理由。這也是數位 IQ_demod.v 混頻器閒置不用的原因:混到 40 kHz 是類比的,ADC 收到的是兩路已降頻的 40 kHz 中頻通道(Rx_I_Rx_Q_,第 321–322 行)。

2. Low-IF (40 kHz) + Hartley image rejection — this IS the Verilog2. 低中頻(40 kHz)+Hartley 影像抑制——這才是 Verilog

In _1Mhz_beta010.v, the "Image Reject module" (≈ lines 353–400):

_1Mhz_beta010.v 中,「Image Reject 模組」(約第 353–400 行):

  1. I/Q capture (fixed point, Q14.0): Rx_I <= Rx_I_bal; Rx_Q <= Rx_Q_; on negedge clk10MHz1 — the falling edge of the ADC encode clock, mid data-valid window.
    I/Q 取樣(定點,Q14.0):Rx_I <= Rx_I_bal; Rx_Q <= Rx_Q_;negedge clk10MHz1——ADC 編碼時脈的下降邊,位在資料有效窗中央。
  2. 90° phase shift on Q (the Hartley quadrature) — the 62.5-cycle delay:
    對 Q 做 90° 相移(Hartley 正交)——62.5 週期延遲:
    hilbert_90 #(.LW(8)) Rx_IR_hilbert_Q (.clk(clk10MHz4), .L(8'd62), .x_in(Rx_Q), .y_90(Rx_Q_90), .y_0(/*unused*/));
    hilbert_90 #(.LW(8)) Rx_IR_hilbert_I (.clk(clk10MHz4), .L(8'd62), .x_in(Rx_I), .y_90(/*unused*/), .y_0(Rx_I_0));
  3. Add I + delayed-Q → cancels the image, giving Rx_IQ_Sum_B (15 bits, cannot overflow):
    I 加上延遲後的 Q → 消除影像,得到 Rx_IQ_Sum_B(15 位元,不會溢位):
    reg  signed [14:0] Rx_IQ_Sum_B;
    always @(posedge clk10MHz6)
        Rx_IQ_Sum_B <= {Rx_I_0[13], Rx_I_0} + {Rx_Q_90[13], Rx_Q_90};  // image-rejected 40 kHz signal

File : line summary檔案:行號摘要

Step步驟Where位置File : line檔案:行號
I/Q mixer (down-convert to 40 kHz)I/Q 混頻器(降頻到 40 kHz)analog front end (5.8 GHz quad LO)— (not in RTL)
Quadrature Tx out / IF in正交 Tx 輸出/中頻輸入DAC §4 ; ADC_DA=Q, ADC_DB=I_1Mhz_beta010.v : 321–322
Digital I/Q multiply (unused)數位 I/Q 相乘(未使用)IQ_demod.v — commented out_1Mhz_beta010.v : 713–714
Hartley 90° delay on QQ 的 Hartley 90° 延遲hilbert_90 (y_90 of Q, y_0 of I)_1Mhz_beta010.v : Rx_IR_hilbert_Q / _I
Image-reject sum (I + Q_delay) → Rx影像抑制相加(I + Q_delay)→ RxRx_IQ_Sum_B <= I + Q_delay_1Mhz_beta010.v : Image Reject module

In short: the mixer is analog; the Low-IF + Hartley image rejection is the digital hilbert_90 pair (90°) + 15-bit adder (I + Q_90 → Rx_IQ_Sum_B) path in _1Mhz_beta010.v — exactly the §5 building block, reusing Yu's T/4 delay as the 90° I/Q split.

簡而言之:混頻器是類比的;低中頻+Hartley 影像抑制_1Mhz_beta010.v 中數位的 hilbert_90 對(90°)+15 位元加法器(I + Q_90 → Rx_IQ_Sum_B)路徑——正是 §5 的構件,重用 Yu 的 T/4 延遲作為 90° I/Q 分割。

How the Image Cancels — Deriving (2.16) & (2.17)影像如何被消除——推導 (2.16) 與 (2.17)

Theory ref — Yu & Horng eq. (2.13)–(2.17): Equations (2.16)/(2.17) come from adding SB (2.14) and SC (2.15), then separating by frequency component. The paper's line “式(2.15) 與 式(2.15) 相加” is a typo — it should read “式(2.14) 與 式(2.15)”, i.e. SB + SC.
理論對照 — Yu & Horng 式 (2.13)–(2.17): 式 (2.16)/(2.17) 來自 SB(2.14)與 SC(2.15)相加,再按頻率分量分開。論文中「式(2.15) 與 式(2.15) 相加」那句是誤植——應為「式(2.14) 與 式(2.15)」,即 SB + SC

The two signals being summed (B is the cos / in-phase branch; C is point A after the 90° phase shift):

被相加的兩個訊號(B 是 cos/同相分支;C 是 A 點經 90° 相移之後):

SB(t) = ½ ALOARF cos(ωLO − ωRF)t + ½ ALOAIM cos(ωLO − ωIM)t (2.14)
SC(t) = ½ ALOARF cos(ωLO − ωRF)t − ½ ALOAIM cos(ωLO − ωIM)t (2.15)

The only difference is the sign of the image (IM) term: + in SB, in SC. Adding them and grouping by frequency:

唯一的差別是影像(IM)項的符號:SB 中是 +、SC 中是 。把它們相加並按頻率分組:

Wanted (RF) part — same sign → reinforces:

想要的(RF)部分——同號 → 相長:

SIF(t) = ½ ALOARF cos(ωLO−ωRF)t + ½ ALOARF cos(ωLO−ωRF)t = ALOARF cos(ωLO−ωRF)t (2.16)

Image (IM) part — opposite sign → cancels:

影像(IM)部分——異號 → 相消:

SIM(t) = ½ ALOAIM cos(ωLO−ωIM)t − ½ ALOAIM cos(ωLO−ωIM)t = 0 (2.17)

So (2.16) is the RF-frequency component of the sum (wanted signal, doubled) and (2.17) is the image-frequency component (identically zero). The image is cancelled, leaving only the wanted signal.

所以 (2.16) 是相加結果中的 RF 頻率分量(想要的訊號,加倍),(2.17) 是影像頻率分量(恆為零)。影像被消除,只留下想要的訊號。

Interactive — watch the image cancel (2.16 & 2.17) (click to expand)

Top = the wanted (RF) component, bottom = the image (IM) component. Faint grey = the two summed terms (SB and SC); the bold curve = their sum. With a perfect 90° shift the wanted doubles and the image vanishes. Add phase error and the image leaks back — that residual is the image-rejection ratio (IRR).

Why the image term flips sign為什麼影像項會翻號

The wanted RF and its image sit on opposite sides of the LO: if ωRF = ωLO − ωIF then ωIM = ωLO + ωIF, so the two difference frequencies have opposite sign:

想要的 RF 與其影像位於 LO 的兩側:若 ωRF = ωLO − ωIF,則 ωIM = ωLO + ωIF,於是兩個差頻符號相反

ωLO − ωRF = +ωIF ωLO − ωIM = −ωIF

A 90° phase shift (Hilbert transform) shifts positive and negative frequencies in opposite directions. So when SA's sin terms become cos in SC, the RF and IM terms acquire opposite signs — exactly the +/ pattern in (2.15). Because SB keeps both terms + and SC flips only the image, the sum doubles the wanted signal and nulls the image — the Hartley image-reject principle.

90° 相移(希爾伯特變換)把頻率與頻率往相反方向移。所以當 SAsin 項在 SC 中變成 cos 時,RF 與 IM 項獲得相反的符號——正是 (2.15) 中的 +/ 型態。因為 SB 兩項都保持 +,而 SC 只翻轉影像,相加就使想要的訊號加倍、影像歸零——Hartley 影像抑制原理。

90° phase shifter — frequency-domain view Im Re ω −ωc c A/2 +j A/2 A/2 −j A/2 impulse spectrum (cosine): A/2 at ±ωc Im Re ω −ωc c X(ω) j X(ω) X(ω) −j X(ω) general spectrum X(ω)
Redrawn (dark theme) from Wu, M.S. thesis 圖 2.5 — 90° phase-shifter frequency-domain analysis. The 90° shift rotates the −ωc component +90° (→+j) and the c component −90° (→−j) — opposite directions, which is why one sideband adds and the other cancels. Drag the bottom-right corner to resize.
重繪(深色主題)自吳碩士論文圖 2.5——90° 相移器的頻域分析。90° 相移把 −ωc 分量轉 +90°(→+j)、把 c 分量轉 −90°(→−j)——方向相反,這就是一個邊帶相加、另一個相消的原因。拖曳右下角可調整大小。

Connection to this FPGA project與本 FPGA 專案的對應

This maps directly onto the §5 image-rejection path in hardware:

這直接對應到硬體中 §5 的影像抑制路徑:

Equation math公式數學Verilog (_1Mhz_beta010.v)
SA / SB — the I and Q mixer outputsSA/SB——I 與 Q 混頻器輸出ADC channels Rx_I_, Rx_Q_ (lines 321–322)
90° phase shift A→CA→C 的 90° 相移hilbert_90 y_90 — exactly 90° at any f (Rx_IR_hilbert_Q); was 62.5 cyc = T/4
SB + SC additionSB + SC 相加Adder IR_Adder_FP (lines 392–396) → Rx (line 400)
SIF (2.16) survives, SIM (2.17) = 0SIF(2.16)存活,SIM(2.17)=0Rx = image-rejected 40 kHz signal

Caveat for your notes: the source's “(2.15)+(2.15)” is a typo for “(2.14)+(2.15)”. Adding (2.15) to itself would give ALOARF cos(ωLO−ωRF)t − ALOAIM cos(ωLO−ωIM)t, which does not cancel the image.

筆記注意:原文的「(2.15)+(2.15)」是「(2.14)+(2.15)」的誤植。把 (2.15) 與自己相加會得到 ALOARF cos(ωLO−ωRF)t − ALOAIM cos(ωLO−ωIM)t,這樣不會消除影像。

Why You Can't Drop Q — Especially When Ad = Aim為什麼不能丟掉 Q——尤其當 Ad = Aim

A tempting shortcut: if the wanted echo and the image happen to have the same amplitude (Ad = Aim), can you skip the Q channel and just read Rx_I_? No — and that is precisely the case where Q matters most.

一個誘人的捷徑:如果想要的回波與影像剛好振幅相同Ad = Aim),能不能跳過 Q 通道、只讀 Rx_I_不行——而且那正是 Q 重要的情況。

What each ADC channel actually carries每個 ADC 通道實際攜帶什麼

Both the desired (RF) target and the image (IM) are already down-converted to the same 40 kHz IF, and both appear in both channels. With ω0 = 2π·40 kHz / 10 MHz = 2π/250 rad/sample:

想要的(RF)目標與影像(IM)都已被降頻到同一個 40 kHz 中頻,而且兩者都出現在兩個通道中。以 ω0 = 2π·40 kHz / 10 MHz = 2π/250 rad/取樣:

Rx_I_[n] = Ad·cos(ω0n) + Aim·cos(ω0n) Rx_Q_[n] = Ad·sin(ω0n) − Aim·sin(ω0n)

The desired and image are identical in the I channel — both are the same 40 kHz cosine. The only thing that distinguishes them is the sign of the Q term (desired +sin, image −sin) — i.e. which way the phasor rotates. That sign is what the 90° delay + add exploits to cancel the image.

想要的訊號與影像在 I 通道中完全相同——都是同一個 40 kHz 餘弦。唯一區分它們的是 Q 項的符號(想要的 +sin、影像 −sin)——也就是相量旋轉的方向。90° 延遲+相加所利用的,正是這個符號。

Setting Q = 0 destroys the discriminator令 Q = 0 會毀掉鑑別器

Zeroing the Q channel leaves only:

把 Q 通道歸零之後只剩下:

Rx = Rx_I_[n] = (Ad + Aim)·cos(ω0n)

The Hartley cancel (I + 90°-delayed Q) works because Q carries the opposite-sign image term. With no Q there is nothing to cancel against — you simply measure the sum of target and image, and can never separate them again.

Hartley 消除(I + 90° 延遲的 Q)之所以有效,正是因為 Q 攜帶符號相反的影像項。沒有 Q 就沒有可以互相抵消的東西——你量到的只是目標與影像的總和,再也無法分開。

Intuition: a real (I-only) signal has a symmetric spectrum — it cannot tell +40 kHz (desired) from −40 kHz (image). Q is the imaginary part that forms the complex/analytic signal and separates positive from negative frequency. Zero it out and the two frequencies fold on top of each other. So keep both channels flowing through hilbert_90 (Rx_IR_hilbert_Q/_I) + the 15-bit adder (Rx_IQ_Sum_B) — that combine is the entire point of the architecture.

直覺:實數(只有 I)的訊號頻譜對稱——它分不出 +40 kHz(想要的)與 −40 kHz(影像)。Q 是構成複數/解析訊號的虛部,用來分開正負頻率。把它歸零,兩個頻率就疊在一起。所以要讓兩個通道都流經 hilbert_90Rx_IR_hilbert_Q/_I)+15 位元加法器(Rx_IQ_Sum_B)——那個合成正是整個架構的重點。

Why 62.5 Cycles = 90° at 40 kHz為什麼 62.5 週期=40 kHz 的 90°

Parameter參數Value數值Derivation推導
Signal frequency訊號頻率40 kHz
Period週期25 µs1 / 40 kHz
Clock rate時脈速率10 MHz100 ns / cycle
Period in clock cycles以時脈週期計的週期250 cycles25 µs ÷ 100 ns
Quarter period (90°)四分之一週期(90°)62.5 cycles250 ÷ 4
Quarter period in time四分之一週期時間6.25 µs62.5 × 100 ns

How 62.5 Cycles Is Achieved (Non-Integer Delay)62.5 週期如何實現(非整數延遲)

An integer register chain gives only whole-cycle delays. To get the fractional 0.5 cycle, the module first interpolates between the current and previous sample, then chains 62 registers:

整數的暫存器鏈只能給出整數週期的延遲。為了取得 0.5 週期的小數部分,模組先在目前取樣與前一取樣之間內插,再串接 62 個暫存器:

Stage階段Implementation實作Delay added增加的延遲
Half-sample interpolation半取樣內插 data_0d5 = (data_in + data_past) >>> 1 (15-bit sum, arithmetic shift) +0.5 cycle
62-register shift chain62 級暫存器移位鏈 d0 → d1 → … → d61 → data_out (negedge clk10MHz) +62 cycles
Total合計 data_out = data_in delayed by 62.5 cycles 62.5 cycles = 6.25 µs = 90°
Fixed-point average: data_temp = {in[13],in} + {past[13],past} (15 bits) and data_0d5 = data_temp[14:1] — the (a + b) / 2 average as a plain add and arithmetic shift, one negedge register. (The removed FP twin did the same with a 7-cycle Adder and a 5-cycle MULT × 32'h3f000000 on clk_200, ~60 ns that the I path had to compensate.)
定點平均: data_temp = {in[13],in} + {past[13],past}(15 位元)、data_0d5 = data_temp[14:1]——(a + b) / 2 平均就是一個加法與算術右移,一級 negedge 暫存器。(已移除的浮點版本用 clk_200 上 7 週期的 Adder 與 5 週期的 MULT × 32'h3f000000 做同一件事,約 60 ns,得由 I 路補償。)

Role in the Image Rejection Architecture在影像抑制架構中的角色

The two ADC channels carry I and Q components of the received 40 kHz signal, nominally 90° apart. Adding them after phase-aligning cancels the image frequency while reinforcing the wanted frequency:

兩個 ADC 通道攜帶接收到的 40 kHz 訊號的 I 與 Q 分量,名義上相差 90°。相位對齊後相加可消除影像頻率、同時加強想要的頻率:

Rx = Rx_I + delay(Rx_Q, 90°) = Rx_I + Rx_Q_delay

The Q channel also receives a ×1.03 amplitude correction (32'h3f83d70a) before the delay to compensate for ADC channel gain mismatch. The I channel gets two extra pipeline registers (Rx_I_d1, Rx_I_d2) to align its latency with the FP pipeline delays on the Q path.

Q 通道在延遲前還接受 ×1.03 的振幅校正(32'h3f83d70a),補償 ADC 通道增益不匹配。I 通道則多兩級管線暫存器(Rx_I_d1、Rx_I_d2),與 Q 路徑的浮點管線延遲對齊。

Tx I/Q Alignment — the Single nv_reg RegisterTx I/Q 對齊——單一 nv_reg 暫存器

Resolved 2026-09-04. The floating-point ×1.022 pipeline on the I channel is gone: the I/Q balance is now a fixed-point ×(1/1.022) on the Q channel (v_L * 64125 >> 16). Both channels leave the Hilbert registers with identical latency and go straight to DAC_DA/DAC_DB, so nv_reg was deleted exactly as the box below anticipated. The rest of this section is kept as history of why the register existed.

2026-09-04 已解決。I 通道的浮點 ×1.022 管線已移除:I/Q 平衡改為 Q 通道上的定點 ×(1/1.022)(v_L * 64125 >> 16)。兩通道以相同延遲離開 Hilbert 暫存器,直接送到 DAC_DA/DAC_DB,因此 nv_reg 已如下方方框所預期地刪除。本節其餘內容保留為該暫存器存在原因的歷史紀錄。

TEMPORARY — DELETE THIS SECTION AND THE nv_reg REGISTER TOGETHER暫時性——本節與 nv_reg 暫存器要一起刪除

nv_reg exists only to compensate the latency of the floating-point amplitude-correction pipeline on the I channel. When that pipeline (Convert_b2FP → MULT → Convert_FP2b) is replaced by a fixed-point combinational multiplier (like the Q10 multiply inside tx_gain.v), the I channel will have zero extra sample delay — then nv_reg must be removed (Q would otherwise lag I by one sample = 1.44° at 40 kHz), and this whole section becomes obsolete.

nv_reg 的存在只是為了補償 I 通道浮點振幅校正管線的延遲。當該管線(Convert_b2FP → MULT → Convert_FP2b)改成定點組合邏輯乘法器(像 tx_gain.v 裡的 Q10 乘法)之後,I 通道就不再多出取樣延遲——屆時必須刪除 nv_reg(否則 Q 會落後 I 一個取樣 = 40 kHz 時 1.44°),本節也隨之作廢。

Image rejection needs I and Q aligned in time at the transmit output too, not just on receive. Since the hilbert_90 rework the two channels leave the module with identical bulk delay; the only remaining imbalance is the floating-point pipeline that sits on the I channel alone, and it is worth exactly one sample.

影像抑制也需要 I 與 Q 在發射輸出端時間對齊,而不只是接收端。改用 hilbert_90 之後,兩通道離開模組時的整體延遲完全相同;唯一剩下的不平衡是只掛在 I 通道上的浮點管線,而它剛好等於一個取樣。

always @(negedge clk10MHz) begin
    nv_reg <= v_Tx;   // ONE register = the FP pipeline's one-sample slip (line 178)
end

// KEY2 gain (tx_gain) is applied upstream on v, before the Hilbert pair — both channels inherit it
assign DAC_DA = {~nv_62d5[13], nv_62d5[12:0]};   // I: hilbert y_90 -> FP x1.022 -> offset binary
assign DAC_DB = nv_reg;    // Q: hilbert y_0 -> nv_reg (offset binary)
  

Why exactly one register — the measured timing: at negedge T0 the hilbert_90 outputs update. The I sample enters Convert_b2FP → MULT → Convert_FP2b (lines 157–171), whose latency is 17 clk_200 cycles = 85 ns (measured by simulating the real IP cores, tb_fp_latency). The DAC latches on the rising edge at T0+50 ns — too early — so it actually captures the I sample one edge later, at T0+150 ns. Without nv_reg the Q sample would be latched at T0+50 ns, one sample ahead of its I partner; the register re-times it to T0+100 ns so both are captured at T0+150 ns — the same edge, preserving the exact 90° from hilbert_90.

為什麼剛好一個暫存器——實測時序:negedge T0 時 hilbert_90 輸出更新。I 取樣進入 Convert_b2FP → MULT → Convert_FP2b(第 157–171 行),其延遲為 17 個 clk_200 週期 = 85 ns(用真實 IP 核心模擬實測,tb_fp_latency)。DAC 在上升緣 T0+50 ns 鎖存——太早——所以實際上晚一個邊緣、在 T0+150 ns 才擷取到這個 I 取樣。若沒有 nv_reg,Q 取樣會在 T0+50 ns 被鎖存,領先它的 I 夥伴一個取樣;加上暫存器後 Q 改在 T0+100 ns 更新,兩者都在 T0+150 ns 的同一個邊緣被擷取,保住 hilbert_90 給出的精確 90°。

Rule of thumb: one nv_reg is correct as long as the FP chain's total latency stays between 50 ns and 150 ns; each additional 100 ns bracket needs one more Q register. Each register of mismatch shifts I/Q by 1 sample = 1.44° at 40 kHz / 10 MHz.

經驗法則:只要浮點鏈總延遲落在 50–150 ns 之間,一個 nv_reg 就是正確的;每多 100 ns 的區間就需要再加一個 Q 暫存器。每差一個暫存器,I/Q 相位就偏移 1 個取樣 = 40 kHz/10 MHz 時的 1.44°。

Why negedge: the DAC latches on the rising edge of clk10MHz (DAC_CLK_A/B = clk10MHz, lines 149–150). Launching the data on the falling edge gives a clean half-cycle (~50 ns) of setup/hold margin, so the value is already stable when the DAC's rising edge captures it — the "negedge 10 MHz align" idea.

為什麼用 negedgeDAC 在 clk10MHz上升緣鎖存(DAC_CLK_A/B = clk10MHz,第 149–150 行)。在下降緣發送資料可得到乾淨的半週期(約 50 ns)建立/保持餘裕,DAC 上升緣擷取時數值早已穩定——這就是「negedge 10 MHz 對齊」的想法。

History (2026-09-01): this used to be a d1→d2→d3 chain (plus unused taps out to d7) feeding DAC_DB from nv_reg_d3. Against the measured 85 ns FP latency that tap overcompensated by 2 samples, making the shipped I/Q differential 60.5 samples ≈ 87.1° instead of 90°. The dead chains were deleted together with the hilbert_90 rework.

歷史(2026-09-01):這裡原本是 d1→d2→d3 鏈(還宣告到 d7 的未用抽頭),由 nv_reg_d3DAC_DB。以實測的 85 ns 浮點延遲來看,那個抽頭補償過頭了 2 個取樣,使出廠的 I/Q 差為 60.5 個取樣 ≈ 87.1° 而非 90°。死掉的鏈已隨 hilbert_90 改版一併刪除。

Signal Flow Diagram信號流圖

── Q Channel (ADC_DA) ────────────────────────────────────────── ADC_DA sign flip Rx_Q_ capture Rx_Q @negedge Q14.0 no scaling delay_62d5_10MHz ½-sample interp + 62-reg chain delay = 62.5 cyc = 6.25 µs = 90° Rx_Q_delay ── I Channel (ADC_DB) ────────────────────────────────────────── ADC_DB sign flip Rx_I_ ×1/1.03 Rx_I_bal (Q16 mul) capture Rx_I @negedge Rx_I (no trim regs) 15-bit adder I + Q_delay Rx_IQ_Sum_B (image-rejected) → Clutter Cancel → Schmitt / Rx_B ── Inside delay_62d5_10MHz ? ────────────────────────────── data_in data_past (1-cycle reg) add a + b (15 bit) >>> 1 data_temp[14:1] data_0d5 = (in + past)/2 = +0.5 cycle delay d0→d1→…→d61 62 regs @neg → +62 cyc data_out 62.5 cycles late latch at negedge

Port List埠列表

PortDir方向Width位寬Description說明
clkinput110 MHz clock (clk10MHz4) — drives the half-sample average, the 62-register chain and data_past (negedge)10 MHz 時脈(clk10MHz4)——驅動半取樣平均、62 級暫存器鏈與 data_past(negedge)
data_ininput14Signed Q14.0 sample to be delayed (Rx_Q, captured on the falling encode edge)待延遲的有號 Q14.0 取樣(於編碼時脈下降邊取樣的 Rx_Q
data_outoutput14Delayed signal: Rx_Q_delay — 62.5 cycles = 90° at 40 kHz延遲後的訊號:Rx_Q_delay——62.5 週期=40 kHz 的 90°
6. Clutter Cancellation & Amplitude Matching雜波消除與振幅匹配 (click to collapse)
Theory ref — Yu & Horng "Yu vs Wu" table (clutter row): Wu-only. Digital range-correlation clutter cancellation has no ultrasonic-SIL counterpart (the clutter row is blank for Yu) — it exists because Wu's microwave Low-IF receiver needs it. The amplitude matching makes the clutter subtraction clean. [Yu guide]
理論對照 — Yu & Horng「Yu vs Wu」表(雜波列): Wu 專屬。數位距離相關的雜波消除在超音波 SIL 中沒有對應(Yu 的雜波列是空白的)——它存在是因為 Wu 的微波低中頻接收機需要它。振幅匹配讓雜波減法乾淨俐落。[Yu 導讀]

Fixed point since 2026-09-04. clutter_cancel.v is integer arithmetic on the 10 MHz clocks: peak-to-peak amplitudes from amplitude_calc, the ratio Tx_Q_Amp / Rx_Amp as unsigned Q8.16 from div_fx (a free-running 32/16 restoring divider that only publishes complete results — the DIV_OPT_VALID guard is gone), the KEY3 latch synchronised to clk10MHz4 instead of always @(negedge CC), phase_detector on the two sign bits, a 256 × 14-bit RAM delay_line, one 15 × 25 multiply and a subtraction giving Rx_cc as signed Q24.8. The ×128 Amplifier_For_CC_Resolve (FP MULT × 32'h43000000) no longer exists as a block: the same gain is the SH=7 shift parameter of the fixed-point smittch_trigger instances (fire at |x| > 200/128 LSB) and a >>> 1 of the Q24.8 value for Rx_cc_Amped_B. Convert_FP2b_16bit became round-half-up + saturate to 16 bits. The DIV/MULT/Subtractor latencies (14–21 / 5 / 7 cycles at 400 MHz) are replaced by one register on each leg of the subtraction. Closed-loop check tb_clutter_cancel.v: ratio 2.5009 for 2.5, phase estimate exact, leakage residual 0.04%, a target added after calibration survives at 753 of an expected 750 LSB p-p. The port list, gain constant and instantiation table below describe the removed amplifier and are kept for reference.

2026-09-04 起改為定點。clutter_cancel.v 全為 10 MHz 時脈上的整數運算:amplitude_calc 給峰對峰振幅;div_fx(自由運轉的 32/16 還原除法器,只發布完整結果——DIV_OPT_VALID 守衛已不需要)算出無號 Q8.16 的比值 Tx_Q_Amp / Rx_Amp;KEY3 鎖存同步到 clk10MHz4,取代 always @(negedge CC)phase_detector 直接吃兩個符號位元;delay_line 是 256 × 14 位元 RAM;一個 15 × 25 乘法與一個減法得到有號 Q24.8Rx_cc。×128 的 Amplifier_For_CC_Resolve(浮點 MULT × 32'h43000000)不再是一個方塊:同樣的增益成為定點 smittch_triggerSH=7 移位參數(|x| > 200/128 LSB 觸發),以及 Rx_cc_Amped_B 對 Q24.8 值的 >>> 1Convert_FP2b_16bit 變成四捨五入+16 位元飽和。DIV/MULT/Subtractor 的延遲(400 MHz 下 14–21/5/7 週期)換成減法兩側各一級暫存器。閉迴路檢查 tb_clutter_cancel.v:比值 2.5009(真值 2.5)、相位估計精確、洩漏殘差 0.04%,校正後加入的目標以 753 LSB p-p 存留(預期 750)。下方的埠列表、增益常數與實例表描述的是已移除的放大器,保留供參考。

(Historical) A small wrapper module that applied a fixed gain of 128× to a 32-bit IEEE 754 floating-point signal using the MULT IP core, in the receive path after the clutter-cancel / image-reject processing, before peak detection and amplitude measurement.

(歷史)一個小型包裝模組,使用 MULT IP 核對 32 位元 IEEE 754 浮點訊號施加固定 128× 增益,位於接收路徑雜波消除/影像抑制處理之後、峰值偵測與振幅量測之前。

Port List埠列表

PortDir方向Width位寬Description說明
clkinput1 Clock for the MULT FP multiplier pipeline (driven by ~clk_400 at top level).MULT 浮點乘法器管線的時脈(頂層由 ~clk_400 驅動)。
data_ininput32 IEEE 754 single-precision floating-point input signal.IEEE 754 單精度浮點輸入訊號。
data_outoutput32 Amplified result: data_in × 128.0.放大後的結果:data_in × 128.0

Gain Constant增益常數

was: Amp_Multiplier_AFCC = 32'h43000000 → 128.0 (IEEE 754) now: smittch_trigger #(.SH(7)) → (x <<< 7) > 200·2^FRAC ; Rx_cc_Amped_B = sat16(round(Rx_cc_Q24.8 >>> 1))
Why 128×? Signals coming out of the CC/IR mixing and subtraction paths are small relative to the ADC full-scale. The ×128 boost brings them into a range suitable for the peak_detector amplitude measurement without saturating.
為什麼是 128×? 從 CC/IR 混頻與減法路徑出來的訊號相對於 ADC 滿刻度很小。×128 的提升把它們帶到適合 peak_detector 振幅量測的範圍,又不會飽和。

Instantiations in _1Mhz_beta010.v在 _1Mhz_beta010.v 中的實例

Since 2026-09-04 the canceller is its own module, clutter_cancel.v, instantiated once in _1Mhz_beta010.v as U_CC. The instantiations listed below now live inside that module (a pure extraction: same instances, same names, same clocks); the core keeps only the shared detection path (Schmitt triggers with the ×128 as SH=7, debounce, CC_Saver, peak extractors). The two Large_Amplifier_* instances below were removed with the fixed-point conversion.

自 2026-09-04 起,消除器已獨立為模組 clutter_cancel.v,在 _1Mhz_beta010.v 中以 U_CC 實例化一次。下列實例化現在位於該模組內(純粹搬移:相同的實例、名稱與時脈);核心只保留共用的偵測路徑 (以 SH=7 內含 ×128 的施密特觸發器、去抖、CC_Saver、峰值擷取)。下表的兩個 Large_Amplifier_* 實例已隨定點化移除。

Instance實例Input輸入Output輸出Path路徑
Large_Amplifier_ccTrig_Rx_cc_Amped (SH=7) Rx_cc (Q24.8) Rx_cc_Amped_Trig Clutter-cancel receive path雜波消除接收路徑
Large_Amplifier_irTrig_Rx_ir_Amped (SH=7) Rx_IQ_Sum_B Rx_ir_Amped_Trig Image-reject receive path影像抑制接收路徑
Feedback role — this path also closes the SIL lock loop: besides feeding the peak detector, Rx_cc is squashed to 1 bit (smittch_trigger with SH=7 → Rx_Debounce @_1Mhz_beta010.v:652–653) and, when both board switches are 0, the resulting Rx_cc_Amped_DB is selected by the uin3 feedback mux as the injection source for the PI phase-lock loop. The clutter subtraction therefore determines whose phase the oscillator locks to — leakage removed → lock to target echo. See §3 Relation to Clutter Cancellation.
回授角色——這條路徑同時閉合 SIL 鎖定迴路: 除了餵峰值偵測器之外,Rx_cc 還被壓成 1 位元(SH=7 的 smittch_triggerRx_Debounce@_1Mhz_beta010.v:652–653),而當兩個板上開關都為 0 時,得到的 Rx_cc_Amped_DB 會被 uin3 回授多工器選為 PI 相位鎖定迴路的注入來源。因此雜波減法決定振盪器鎖到誰的相位——洩漏移除 → 鎖到目標回波。見 §3 與雜波消除的關係

Why ×128 is Needed — Signal Level Analysis為什麼需要 ×128——訊號位準分析

Both downstream consumers — smittch_trigger (Schmitt trigger) and peak_extractor — need a signal with enough swing to cross their detection thresholds reliably. The two paths produce inherently small residuals for different physical reasons:

兩個下游使用者——smittch_trigger(施密特觸發器)與 peak_extractor——都需要擺幅足以可靠跨越其偵測門檻的訊號。這兩條路徑基於不同的物理原因,產生的都是先天很小的殘差

Path路徑Input to amplifier放大器輸入Why it is small為什麼很小
CC Rx_cc = Rx_IQ_Sum_B·ratio − Tx_Q_Delay (Q24.8) Clutter cancellation subtracts most of the signal. The large direct-path leakage from the transmitter is cancelled; only the tiny target echo remains. Good cancellation → residual ≪ original Rx level. 雜波消除把大部分訊號減掉了。發射端的大直接路徑洩漏被消除;只留下微小的目標回波。消除得好 → 殘差 ≪ 原始 Rx 位準。
IR Rx (image-reject sum) Image rejection sums two ADC channels 90° apart to cancel the unwanted image frequency. The wanted signal survives but its amplitude is naturally at a low level relative to the ADC full-scale. 影像抑制把相差 90° 的兩個 ADC 通道相加以消除不要的影像頻率。想要的訊號存活下來,但其振幅相對 ADC 滿刻度天生就低。

Without the ×128 boost, neither the Schmitt trigger nor the peak extractor would see enough swing to function correctly.

沒有 ×128 的提升,施密特觸發器與峰值擷取器都看不到足夠的擺幅、無法正常運作。

Where it sits in the Rx path它在 Rx 路徑中的位置

The gain is ×128 (27, ≈ +42 dB, a 7-bit left shift) applied after the cancellation / rejection stage and before the threshold detectors. It used to be a floating-point Amplifier_For_CC_Resolve block (MULT × 0x43000000); since 2026-09-04 it is the SH=7 parameter of the fixed-point smittch_trigger and a >>> 1 of the Q24.8 residual for Rx_cc_Amped_B. The same gain is used in both receive branches:

增益為 ×128(27,≈ +42 dB,即左移 7 位元),施加在消除/抑制級之後、門檻偵測器之前。它原本是浮點的 Amplifier_For_CC_Resolve 方塊(MULT × 0x43000000);自 2026-09-04 起成為定點 smittch_triggerSH=7 參數,以及 Rx_cc_Amped_B 對 Q24.8 殘差的 >>> 1同一個增益用在兩條接收分支:

CC: Rx_cc (Q24.8) → smittch_trigger #(.FRAC(8),.SH(7)) → Rx_cc_Amped_Trig ; sat16(Rx_cc >>> 1) = Rx_cc_Amped_B IR: Rx_IQ_Sum_B (Q15.0) → smittch_trigger #(.FRAC(0),.SH(7)) → Rx_ir_Amped_Trig ; Rx_B = Rx_IQ_Sum_B → peak_extractor

Cancellation and image rejection deliberately remove the large components (the Tx direct-path leakage and the image sideband), leaving only a small difference near the quantization / noise floor. The ×128 restores that residual to a level where smittch_trigger reliably toggles and peak_extractor registers meaningful peaks — so the boost is a receive-path stage that recovers usable signal swing lost to the cancellation math.

消除與影像抑制刻意移除大的分量(Tx 直接路徑洩漏與影像邊帶),只留下接近量化/雜訊底的微小差值。×128 把那個殘差恢復到 smittch_trigger 能可靠翻轉、peak_extractor 能記錄到有意義峰值的位準——所以這個提升是接收路徑的一級,把消除運算損失的可用訊號擺幅找回來。

Signal Flow Diagram信號流圖

── CLUTTER CANCEL PATH ────────────────────────────────────────── Rx_Amped (Q24.8) Tx_Q_Delay subtract − Rx_cc tiny residual ×128 = SH=7 in smittch_trigger Rx_cc <<< 7 smittch_trigger FP2b → peak_extractor Rx_Debounce Rx_cc_out CC_Saver ── IMAGE REJECT PATH ──────────────────────────────────────────── Rx (FP, I+Q image-reject sum) ×128 = SH=7 in smittch_trigger Rx_IQ_Sum_B <<< 7 smittch_trigger FP2b → peak_extractor Rx_Debounce
Summary: Both paths end at the same two consumers: a Schmitt trigger (needs adequate swing to toggle) and a peak extractor (needs amplitude well above noise floor). The ×128 gain is the bridge between the sub-full-scale residual from CC/IR processing and the minimum level these blocks require.
總結: 兩條路徑的終點都是同樣的兩個使用者:施密特觸發器(需要足夠擺幅才能翻轉)與峰值擷取器(需要振幅遠高於雜訊底)。×128 增益就是 CC/IR 處理後低於滿刻度的殘差,與這些方塊所需最低位準之間的橋樑。

Board Controls — Switches on the C5G板上控制——C5G 的開關

Every run-time control of the clutter canceller is a slide switch on the Cyclone V GX Starter Kit. Pin mappings are from Table 3-3 of the board manual (docs/C5G_User_Manual.pdf, extracted text in docs/manual.txt):

雜波消除器的每一個執行期控制都是 Cyclone V GX Starter Kit 上的滑動開關。腳位對應取自板卡手冊表 3-3(docs/C5G_User_Manual.pdf,文字擷取在 docs/manual.txt):

Control控制FPGA pinFPGA 腳位Signal訊號Role in the design在設計中的角色
SW9PIN_AE19Injection_Method_Switch 0 = feed the lock loop from the receive path (required for CC); 1 = local feedback (CC output unused by the loop)0=以接收路徑餵鎖定迴路(CC 所需);1=本地回授(迴路不使用 CC 輸出)
SW8PIN_Y11ClutterCancel_OR_ImageReject_Switch The clutter-cancel selector: with SW9 = 0, 0 = clutter-cancel path (Rx_cc_Amped_DB), 1 = image-reject path (Rx_ir_Amped_DB)雜波消除選擇器:SW9=0 時,0=雜波消除路徑Rx_cc_Amped_DB),1=影像抑制路徑(Rx_ir_Amped_DB
SW7–SW5AC10, V10, AB10 Unused by this design
SW4PIN_W11Mult_or_Add How the manual trim is applied to the amplitude-match ratio: 1 = multiply by Amp_CC_Mult, 0 = add Amp_CC_Add手動微調如何施加到振幅匹配比:1=乘上 Amp_CC_Mult,0=加上 Amp_CC_Add
SW3PIN_AC8Amp_Sel[3] Trim amount (MSB = SW3 … LSB = SW0) — the 4-bit code selects both constants from the lookup tables below (0x0 = no trim … 0xF = max trim)微調量(MSB=SW3 … LSB=SW0)——4 位元碼從下方查找表同時選出兩個常數(0x0=無微調 … 0xF=最大微調)
SW2PIN_AD13Amp_Sel[2]
SW1PIN_AE10Amp_Sel[1]
SW0PIN_AC9Amp_Sel[0]
KEY1PIN_P12change Push-button, PI controller only (not CC): each press steps the 2-bit shift_bit counter, doubling the PI set-point weight (×29…×212, wraps after 4 presses). Active-low, so it counts on release.按鈕,僅供 PI 控制器(非 CC):每按一次使 2 位元 shift_bit 計數器前進一步,把 PI 設定點權重加倍(×29…×212,按四次後迴繞)。低態有效,所以在放開時計數。
KEY0PIN_P11reset Push-button, PI controller only (not CC): clears the PI integrator (.rst of PI_antiWindup) — use it if the loop has wound up after a mode change.按鈕,僅供 PI 控制器(非 CC):清除 PI 積分器(PI_antiWindup.rst)——模式切換後迴路積分飽和時使用。
KEY3PIN_Y16CC Push-button, clutter canceller: on press (falling edge of CC, synchronised to clk10MHz4) it commits the fixed-point divider's amplitude-match result — Clutter_Amp_Ratio <= ratio_sat (Q8.16, saturated at 255.99) — and latches phase_diff (clutter_cancel.v).按鈕,雜波消除器:按下時(negedge CC)提交 FP 除法器算出的振幅匹配結果——Clutter_Amp_Ratio <= Clutter_Amp_Ratio_Extra,由 DIV_OPT_VALID 穩定交握保護——並同時鎖存 phase_diff(@_1Mhz_beta010.v:354–358)。
KEY2Y15 Unused by this design

So of the ten slide switches, 7 are used (SW9, SW8, SW4, SW3–SW0) and SW7–SW5 are free; of the four push-buttons (Table 3-2), 3 are used — and one of them, KEY3 (CC), belongs to the clutter canceller itself: pressing it commits the freshly computed amplitude-match ratio into the working register (see the table row above). Only KEY2 is unconnected. The buttons idle high and go low while pressed, which has a subtle consequence: change is sampled at 1.25 kHz into change_d and the counter fires on posedge change_d (@_1Mhz_beta010.v:68–76), so shift_bit actually steps when KEY1 is released, not when it is pressed. Each step doubles the weight of the set-point word subtracted from the demodulated signal (case @:82–87 shifts it 9…12 bits), wrapping back after four presses. reset (KEY0) fans out to a single place — PI_antiWindup.rst — clearing the integrator. Pin sources: _1Mhz_beta010.qsf:55–56, 146, 157, 165–170.

所以十個滑動開關中用了 7 個(SW9、SW8、SW4、SW3–SW0),SW7–SW5 空著;四個按鈕(表 3-2)中用了 3 個——其中 KEY3(CC正屬於雜波消除器本身:按下即把剛算好的振幅匹配比提交到工作暫存器(見上表該列)。真正未接線的只有 KEY2。按鈕平時為、按下時為低,這有個微妙的後果:change 以 1.25 kHz 取樣進 change_d,計數器在 posedge change_d 觸發(@_1Mhz_beta010.v:68–76),所以 shift_bit 實際上是在 KEY1 放開時前進,不是按下時。每一步都把從解調訊號中減去的設定點字組權重加倍(@:82–87 的 case 左移 9…12 位元),按四次後迴繞。reset(KEY0)只接到一個地方——PI_antiWindup.rst,清除積分器。腳位來源:_1Mhz_beta010.qsf:55–56, 157, 165–170

Operating recipe: to run target detection with clutter cancellation, set SW9 = 0 and SW8 = 0 (mode 0/0 of the uin3 mux table) — the PI loop then locks to Rx_cc_Amped_DB, the Schmitt-triggered, debounced output of the Rx_Amped − Tx_Q_Delay subtraction (Q24.8). If residual clutter remains (imperfect amplitude match), pick a trim style with SW4 and step SW3–SW0 up from 0x0 until the residual nulls — but note: in the current build the trim mux is commented out (_1Mhz_beta010.v:550), so SW4/SW3–SW0 are inert until it is restored; see the RTL chain below. The switch states are mirrored on the red LEDs above them via the r register (@_1Mhz_beta010.v:22), so you can confirm the FPGA actually sees SW9/SW8 (plus Clutter_weak) at a glance.

操作配方:要以雜波消除進行目標偵測,設 SW9=0 且 SW8=0uin3 多工表的 0/0 模式)——PI 迴路便鎖到 Rx_cc_Amped_DB,即 Rx_Amped − Tx_Q_Delay 減法經施密特觸發、去抖後的輸出。若仍有殘餘雜波(振幅匹配不完美),用 SW4 選微調方式、把 SW3–SW0 從 0x0 往上調直到殘差歸零——但注意:目前建置中微調多工器被註解掉了(_1Mhz_beta010.v:550),所以在恢復之前 SW4/SW3–SW0 是無效的;見下方的 RTL 鏈。開關狀態經由 r 暫存器(@_1Mhz_beta010.v:22)鏡射到上方的紅色 LED,一眼就能確認 FPGA 實際看到的 SW9/SW8(加上 Clutter_weak)。

Interactive — flip the virtual switch bank (click to collapse)

This is the C5G slide-switch row as the FPGA sees it. Click a switch to flip it (blue = 1/up, dark = 0/down; greyed switches are unconnected). The round push-buttons are momentary: click KEY1 to step the PI set-point scale (it counts on release, like the real board), KEY0 to reset the PI integrator, and KEY3 (CC) to commit the clutter amplitude-match ratio. The readout below explains what your current setting does — same information as the table above, but live.

From Switch to Residual — the Exact RTL Chain從開關到殘差——精確的 RTL 鏈

Fixed point since 2026-09-04. The listing below is the historical IEEE-754 chain (with the Amp_Sel trim, itself removed the same day). The current chain is the fixed-point one shown in §3 Relation to Clutter Cancellation: div_fx → Q8.16 ratio latched on the synchronised KEY3 edge → 15 × 25 multiply → delay_line → subtract → Rx_cc (Q24.8). The DIV_OPT_VALID stability gate mentioned below is unnecessary because div_fx only updates its result when a division completes.

2026-09-04 起改為定點。下方列出的是歷史上的 IEEE-754 鏈(含同日移除的 Amp_Sel 微調)。現行的定點鏈見 §3 與雜波消除的關係div_fx → Q8.16 比值於同步後的 KEY3 邊緣鎖存 → 15 × 25 乘法 → delay_line → 相減 → Rx_cc(Q24.8)。下文提到的 DIV_OPT_VALID 穩定閘已不需要,因為 div_fx 只在一次除法完成時更新結果。

Here is the complete path from the switch pins to the cancelled signal, condensed from _1Mhz_beta010.v. The auto-computed amplitude ratio comes first, then the two manual-trim candidates (driven by SW3–SW0), then the SW4 mux, then the actual subtraction:

以下是從開關腳位到消除後訊號的完整路徑,濃縮自 _1Mhz_beta010.v。先是自動計算的振幅比,然後是兩個手動微調候選(由 SW3–SW0 驅動),接著是 SW4 多工器,最後是實際的減法:

// 1. Auto amplitude match — FP divider computes ratio = Tx/Rx        @_1Mhz_beta010.v:529
DIV FP_Divider (.dataa(Tx_Q_Amp_FP), .datab(Rx_Amp_FP), .result(Amp_Multiplier_));

// 2. Manual trim candidates — both computed in parallel from the Amp_Sel LUTs (SW3–SW0)
Subtractor CC_Extra_eq_Adder_For_Tx      (.dataa(Amp_Multiplier_), .datab(Amp_CC_Add),  ...);  // @:535 ratio − trim
MULT       CC_Extra_eq_Multiplier_For_Tx (.dataa(Amp_Multiplier_), .datab(Amp_CC_Mult), ...);  // @:542 ratio × trim

// 3. SW4 (Mult_or_Add) picks which trim applies — ⚠ CURRENTLY BYPASSED @:549–551
// 暫且把這個功能關掉 ("temporarily turn this feature off")
// assign Amp_Multiplier_Extra = (Mult_or_Add) ? Amp_Multiplier_Extra_Mult : Amp_Multiplier_Extra_Add;
assign Amp_Multiplier_Extra = Amp_Multiplier_;   // ← trim disabled: raw ratio passes through

// 4. Ratio applied to Rx, replica phase-aligned, then subtracted
MULT Multiplier (.dataa(Rx_FP), .datab(Amp_Multiplier), .result(Rx_Amped_FP));        // @:560
delay_line Variable_Delay_Line (clk10MHz2, 1'b1, Tx_Q_Delay, Tx_Q_FP, phase_diff);      // @:580
Subtractor Subtract_Two_Signal_For_ClutterCancel (.dataa(Rx_Amped_FP), .datab(Tx_Q_Delay), .result(Rx_cc)); // @:582

Reading the chain switch-by-switch:

逐開關解讀這條鏈:

Interactive — switch panel: dial the trim, null the residual (click to collapse)

Set a channel-imbalance error (how far the auto ratio is off), then use the board switches to null it: SW4 picks multiply/add trim, SW3–SW0 set the amount. The clutter is Rx after the ratio is applied, the replica is Tx_Q_Delay, the residual is Rx_cc. SW9/SW8 show whether the PI loop actually listens to the result. The "trim mux enabled" box models un-commenting _1Mhz_beta010.v:550 — leave it off to see today's build, where Amp_Sel does nothing.

Amp_Sel — Amplitude Fine-Trim ControlAmp_Sel——振幅微調控制

Removed 2026-09-04. The Amp_Sel/Mult_or_Add trim tables and their extra FP subtractor/multiplier are gone from clutter_cancel.v; Clutter_Amp_Ratio_Extra is the raw divider output, as it had effectively been since the bypass. This section is kept as history.

2026-09-04 已移除。Amp_SelMult_or_Add 微調表及其額外的浮點減法器/乘法器已從 clutter_cancel.v 移除;Clutter_Amp_Ratio_Extra 就是除法器的原始輸出(自旁路以來實際上即如此)。本節保留為歷史紀錄。

Amp_Sel is a 4-bit hardware input (switch/pin) that simultaneously drives two combinational lookup tables, producing two IEEE 754 floating-point constants used to fine-trim the clutter-cancellation amplitude matching ratio.

Amp_Sel 是一個 4 位元硬體輸入(開關/腳位),同時驅動兩個組合邏輯查找表,產生兩個 IEEE 754 浮點常數,用來微調雜波消除的振幅匹配比。

The Problem It Solves它解決的問題

Clutter cancellation works by computing Rx_Amped_FP − Tx_Q_Delay. For the subtraction to cancel well, Rx_Amped_FP must match Tx_Q_Delay in amplitude. The automatic amplitude ratio is:

雜波消除靠計算 Rx_Amped_FP − Tx_Q_Delay 運作。要讓減法消得乾淨,Rx_Amped_FP 必須在振幅上匹配 Tx_Q_Delay。自動振幅比為:

Amp_Multiplier_ = Tx_Q_Amp_FP ÷ Rx_Amp_FP (FP divider)

In practice, this raw ratio may have a small residual error due to ADC channel imbalance, hardware asymmetry, or rounding. Amp_Sel provides a manual ±trim on top of the auto-computed ratio so the operator can null out the residual clutter.

實務上,這個原始比值可能因 ADC 通道不平衡、硬體不對稱或捨入而有小殘餘誤差。Amp_Sel 在自動比值之上提供手動 ± 微調,讓操作者能把殘餘雜波歸零。

Two Lookup Tables Driven by Amp_Sel由 Amp_Sel 驅動的兩個查找表

Amp_SelAmp_CC_Add (additive offset)Amp_CC_Add(加法偏移)Amp_CC_Mult (scale factor)Amp_CC_Mult(縮放因子)
0x0+0.00×1.00
0x1+0.01×0.99
0x2+0.02×0.98
0x3+0.03×0.97
0x4+0.04×0.96
0x5+0.05×0.95
0x6+0.06×0.94
0x7+0.07×0.93
0x8+0.08×0.92
0x9+0.09×0.91
0xA+0.10×0.90
0xB+0.11×0.89
0xC+0.12×0.88
0xD+0.13×0.87
0xE+0.14×0.86
0xF+0.15×0.85
Note: The two tables move in opposite directions by design. Amp_CC_Add increases from 0→+0.15 (subtract a positive offset from the ratio → reduce Rx gain). Amp_CC_Mult decreases from 1.0→0.85 (multiply ratio down → also reduce Rx gain). Both trims nudge in the same physical direction: reducing the Rx amplitude scale to improve cancellation depth.
注意: 兩個表的走向刻意相反Amp_CC_Add 從 0 遞增到 +0.15(從比值減去正偏移 → 降低 Rx 增益)。Amp_CC_Mult 從 1.0 遞減到 0.85(把比值乘小 → 同樣降低 Rx 增益)。兩種微調推的都是同一個物理方向:降低 Rx 振幅刻度以改善消除深度。

Where the Constants Are Used & Why這些常數用在哪裡、為什麼

Destination目的地Operation操作Role in architecture在架構中的角色Status狀態
CC_Extra_eq_Adder_For_Tx
(Subtractor instance)
Amp_Multiplier_ − Amp_CC_Add → Amp_Multiplier_Extra_Add Subtracts a small offset from the auto ratio — additive fine-trim mode從自動比值減去一個小偏移——加法式微調模式 Bypassed
CC_Extra_eq_Multiplier_For_Tx
(MULT instance)
Amp_Multiplier_ × Amp_CC_Mult → Amp_Multiplier_Extra_Mult Scales the auto ratio by a factor slightly below 1 — multiplicative fine-trim mode把自動比值乘上略小於 1 的因子——乘法式微調模式 Bypassed
Amp_Multiplier
(latched on negedge CC)
Amp_Multiplier_Extra → latched Holds the stable amplitude ratio used downstream; latches only when CC button pressed and value is stable (DIV_OPT_VALID)保存下游使用的穩定振幅比;只在 CC 按下且數值穩定(DIV_OPT_VALID)時鎖存 Active
MULT Multiplier Rx_FP × Amp_Multiplier → Rx_Amped_FP Scales the raw Rx signal to match Tx amplitude before the clutter-cancel subtraction把原始 Rx 訊號縮放到與 Tx 振幅匹配,供雜波消除減法使用 Active
FP_Comparator Amp_Multiplier vs 32'h40A00000 (= 5.0) Detects clutter strength: if ratio > 5 → Rx is much weaker than Tx → clutter is strong偵測雜波強度:比值 > 5 → Rx 遠弱於 Tx → 雜波很強 Active
Currently bypassed: Line 556 forces assign Amp_Multiplier_Extra = Amp_Multiplier_, so the trim logic is disabled and the raw divider output is used directly. The comment says 暫且把這個功能關掉 ("temporarily disable this feature"). The trim hardware remains wired and ready to re-enable.
目前被旁路: 第 556 行強制 assign Amp_Multiplier_Extra = Amp_Multiplier_,所以微調邏輯被停用、直接使用原始除法器輸出。註解寫著暫且把這個功能關掉。微調硬體仍然接線完整、隨時可重新啟用。

Architecture Diagram架構圖

4-bit HW input Amp_Sel [3:0] LUT: Amp_CC_Add 0 → +0.15 (step 0.01) LUT: Amp_CC_Mult 1.0 → 0.85 (step 0.01) FP Divider Tx_Amp ÷ Rx_Amp Subtractor − Amp_CC_Add MULT trim × Amp_CC_Mult BYPASSED CC Latch Amp_Multiplier MULT Rx_FP × Rx_FP ↓ Rx_Amped_FP → CC subtract Comparator vs 5.0 → Clutter_weak → Clutter_equal → Clutter_strong Mult_or_Add switch — bypassed (reserved for future use)

Why Only a Q Replica — Single-Tone Cancellation為什麼只需要 Q 複本——單音消除

The clutter canceller subtracts a single replica, Tx_Q_Delay (a delayed copy of the transmit Q signal) — there is no separate I-axis canceller, and none is needed. The Tx is quadrature (I = v, Q = v_62d5), but both DACs emit the same 40 kHz frequency. Whatever leaks into Rx — from the I path, the Q path, direct coupling, or static reflections — arrives as one 40 kHz sinusoid with some net amplitude and phase.

雜波消除器只減去單一複本 Tx_Q_Delay(發射 Q 訊號的延遲副本)——沒有獨立的 I 軸消除器,也不需要。Tx 是正交的(I=v、Q=v_62d5),但兩個 DAC 發出的是同一個 40 kHz 頻率。無論洩漏進 Rx 的來自 I 路徑、Q 路徑、直接耦合還是靜態反射——到達時都是一個 40 kHz 弦波,帶著某個淨振幅與相位。

A single tone lives in a 2-D space (amplitude + phase), and you can reach any point in it with one reference tone if you can adjust its phase and amplitude: phase via the Variable_Delay_Line (delaying Tx_Q_FP sweeps its phase through the full 0–360°), amplitude via the upstream Rx_Amped (ratio) scaling. So the delayed Q replica reproduces the combined I- and Q-origin leakage, and one subtraction nulls it. Using Tx_I instead of Tx_Q would make no difference — same tone, and the delay supplies any 90° offset.

單一音存在於二維空間(振幅+相位),而只要能調整參考音的相位與振幅,就能到達其中任何一點:相位靠 Variable_Delay_Line(延遲 Tx_Q_FP 可把相位掃過完整 0–360°),振幅靠上游的 Rx_Amped 縮放。所以延遲後的 Q 複本能重現 I 與 Q 來源合併的洩漏,一次減法就把它歸零。改用 Tx_I 而非 Tx_Q 沒有差別——同一個音,90° 偏移由延遲提供。

A full complex (I & Q) canceller — what the unused IQ_demod.v would set up — is only needed to cancel before down-conversion or across many frequencies at once. Here everything is a single 40 kHz IF tone, so scalar delay-and-subtract with just Tx_Q_Delay suffices (which is why IQ_demod is left commented out).

完整的複數(I 與 Q)消除器——閒置的 IQ_demod.v 會建立的那種——只有在降頻之前消除、或同時跨多個頻率消除時才需要。這裡一切都是單一 40 kHz 中頻音,所以只用 Tx_Q_Delay 的純量延遲減法就夠了(這正是 IQ_demod 被註解掉的原因)。

Interactive — one replica nulls any clutter phase (click to expand)

The clutter is the received 40 kHz tone; move its phase to simulate leakage arriving more from the I or Q axis. The replica is Tx_Q delayed (phase) and scaled (amplitude); the residual is their difference. Match the replica's phase & amplitude to the clutter and the residual flattens to zero — for any clutter phase, so no separate I canceller is required.

Detection Path — Schmitt Trigger, Debounce & Peak偵測路徑——施密特觸發、去抖與峰值

The Q24.8 residual fans out into two measurements (the former ×128 amplifier is now the SH=7 parameter of the trigger and a shift on the 16-bit output):

Q24.8 的殘差分岔成兩種量測(原本的 ×128 放大器現在是觸發器的 SH=7 參數,以及 16 位元輸出上的一次移位):

smittch_trigger — Schmitt triggersmittch_trigger——施密特觸發器

A comparator with hysteresis (two thresholds): once it goes high it won't drop until the signal falls well below a lower threshold. This rejects noise in the amplitude domain near a crossing — but not fast chatter, which is where the debounce comes in.

遲滯(兩個門檻)的比較器:一旦變高,就要等訊號跌破更低的門檻才會變低。這在振幅域排除交越點附近的雜訊——但擋不住快速抖動,那就是去抖的工作了。

Rx_Debounce — time-domain glitch filterRx_Debounce——時域突波濾波器

It only accepts a transition after the input has held its new value for 16 consecutive clocks; any flicker resets the counter, so disturbances shorter than 16 clocks are rejected. Core logic (Rx_Debounce.v, HIGH branch — the LOW branch mirrors it):

它只在輸入以值連續維持 16 個時脈後才接受轉換;任何閃爍都會重置計數器,所以短於 16 個時脈的擾動被排除。核心邏輯(Rx_Debounce.v,HIGH 分支——LOW 分支鏡像對稱):

always @(negedge clk)
  if (data_out) begin                          // currently HIGH
    if (~data_in) begin                        //   input disagrees (low)
      data_out <= (count >= 16) ? ~data_out : data_out;  // flip only after 16 stable
      count    <= (count >= 16) ? 0 : count+1;
    end else count <= 0;                       //   flicker -> restart count
  end else begin /* LOW: same, mirrored */ end

At ~10 MHz, 16 clocks ≈ 1.6 µs. A genuine 40 kHz level lasts ~12.5 µs (~125 clocks), so it passes untouched; sub-µs noise chatter (< 16 clocks) is filtered out. Schmitt hysteresis (amplitude) and debounce (time) are complementary; the clean Rx_cc_out is then latched by CC_Saver.

在約 10 MHz 下,16 個時脈 ≈ 1.6 µs。真正的 40 kHz 位準持續約 12.5 µs(約 125 個時脈),所以原封不動地通過;亞微秒的雜訊抖動(< 16 時脈)被濾除。施密特遲滯(振幅)與去抖(時間)互補;乾淨的 Rx_cc_out 隨後由 CC_Saver 鎖存。

Interactive — glitch in → debounce → clean out (click to expand)

Top = input from the Schmitt trigger (a slow real level with injected glitches); bottom = debounced output. A transition is accepted only after the input holds for N clocks, so glitches narrower than N vanish. Raise the glitch width above N to watch one leak through.

How the Canceller Works — and an Assessment消除器如何運作——以及評估

What the code does. Clutter here means the direct Tx-to-Rx leakage, modelled as a scaled and delayed copy of the transmit reference. On a KEY3 press (falling edge of CC, synchronised to clk10MHz4) clutter_cancel.v freezes two calibration values: an amplitude ratio from the fixed-point divider div_fx, Tx_Q_Amp / Rx_Amp in Q8.16 built from the two peak-to-peak detectors (amplitude_calc), and a phase offset from phase_detector, which counts 10 MHz samples between the zero crossings of the transmit reference Tx_Q and the received sum Rx_IQ_Sum_B. The receive signal is then multiplied by that ratio, the reference is pushed through delay_line (Variable_Delay_Line) by that many samples, and Rx_cc (Q24.8) is the subtraction of the two. The small residual is Schmitt-triggered with the former 128× gain folded in (smittch_trigger, SH=7, threshold 200 LSB) and debounced (Rx_Debounce) into the 1-bit edge Rx_cc_Amped_DB that can drive the PI injection instead of the image-reject path. Two guards sit around it: Clutter_weak (ratio > 5.0, i.e. > 327680 in Q8.16) flags a ratio above 5 as “too little clutter to bother”, and CC_Saver watches the duty cycle of the result and falls back to the image-reject path when it looks broken. (Until 2026-09-04 this ran in IEEE-754: DIV/MULT/ Subtractor IP on a 400 MHz PLL clock, an asynchronous always @(negedge CC) latch and the ×128 Amplifier_For_CC_Resolve.)

程式碼在做什麼。這裡的雜波指 Tx 直接漏到 Rx 的成分,被建模為發射參考訊號經縮放與延遲後的副本。按下 KEY3 (CC 的下降邊,同步到 clk10MHz4)時,clutter_cancel.v 會凍結兩個校正值: 一是定點除法器 div_fx 由兩個峰對峰偵測器(amplitude_calc)算出的 Q8.16 振幅比 Tx_Q_Amp / Rx_Amp; 二是 phase_detector 量得的相位偏移,它計數發射參考 Tx_Q 與接收和 Rx_IQ_Sum_B 過零點之間相隔多少個 10 MHz 取樣。接著把接收訊號乘上該比值、把參考訊號送進 delay_lineVariable_Delay_Line)延遲同樣多個取樣,兩者相減即為 Q24.8 的 Rx_cc。很小的殘差經內含原 128 倍增益的施密特觸發 (smittch_triggerSH=7,門檻 200 LSB)與去抖(Rx_Debounce)變成 1-bit 邊緣 Rx_cc_Amped_DB, 可取代影像消除路徵去驅動 PI 注入。外面還有兩道保護:Clutter_weak(比值 > 5.0,即 Q8.16 的 327680)在比值超過 5 時標記「雜波太弱,不值得消除」;CC_Saver 監看結果的工作週期, 看起來壞掉時就退回影像消除路徑。(2026-09-04 之前這段以 IEEE-754 實作:400 MHz PLL 時脈上的 DIVMULTSubtractor IP、非同步的 always @(negedge CC) 鎖存與 ×128 的 Amplifier_For_CC_Resolve。)

Judgement. It is a minimal single-tap canceller, calibrated once by hand, and that is both its merit and its limit.

評估。這是一個最簡的單抽頭消除器,靠人手一次校正——這既是它的優點,也是它的極限。

As a proof of concept that leakage can be subtracted before the phase-lock loop, it works. For a robust instrument the natural next steps are to scale the reference rather than the receive path, replace the one-shot ratio and delay with an adaptive complex weight such as LMS on I/Q, and synchronise the KEY3 capture.

作為「漏波可以在鎖相迴路之前被減掉」的概念驗證,它是可行的。若要成為穩健的儀器,自然的下一步是:改為縮放參考訊號而非接收路徑、 以自適應的複數權重(例如對 I/Q 做 LMS)取代一次性的比值與延遲,並對 KEY3 的擷取加上同步器。

7. UART Serial Data Output — UART_AUART 序列資料輸出 — UART_A (click to collapse)
Theory ref — Yu & Horng eq. (5): The readout path. Yu's radar output is the delay value, x̂(t) = −½·c·[d(t) − a] — "the delay value itself is the output." UART_A serializes that demodulated quantity (PI output / amplitude) to the host PC. Implementation glue; both designs are FPGA-based. [Yu guide]
理論對照 — Yu & Horng 式 (5): 讀出路徑。Yu 的雷達輸出就是延遲值 x̂(t) = −½·c·[d(t) − a]——「延遲值本身就是輸出」。UART_A 把該解調量(PI 輸出/振幅)序列化送往主機 PC。屬於實作黏合層;兩個設計都以 FPGA 為基礎。[Yu 導讀]

Instance U6_COPY is the only UART in _1Mhz_beta010.v. It is transmit-only (no rx pin). It alternates between two 40-bit packets at 2.5 kHz, sending either PI controller data or peak amplitude, identified by a 1-byte suffix.

實例 U6_COPY_1Mhz_beta010.v 中唯一的 UART。它只發送(沒有 rx 腳位)。它以 2.5 kHz 在兩種 40 位元封包之間輪替,送出 PI 控制器資料或峰值振幅,以 1 位元組尾碼識別。

Port Connections埠連接

UART_A portUART_A 埠Connected to連接到Description說明
clk_50M~clk50MHz50 MHz inverted — baud-rate clock source for speed_select (230400 baud)反相的 50 MHz——speed_select 的鮑率時脈來源(230400 鮑)
clk2d5kHzclk2d5kHz2.5 kHz — triggers one packet transmission per cycle (was port clk_1250)2.5 kHz——每個週期觸發一次封包傳送(原埠名 clk_1250
clk1d25kHzclk1d25kHz1.25 kHz — selects packet type (PI vs amplitude) inside UART_A (was port clk_625)1.25 kHz——在 UART_A 內選擇封包類型(PI vs 振幅)(原埠名 clk_625
data[31:0]data_switch32-bit payload, alternates between data_all and amplitude_info32 位元酬載,在 data_allamplitude_info 之間輪替
txFPGA pinSerial bitstream output — no rx序列位元流輸出——沒有 rx

Two Data Sources — Switched by clk1d25kHz兩個資料來源——由 clk1d25kHz 切換

clk1d25kHzdata_switchSource signal來源訊號Meaning意義Packet ID byte封包識別位元組
1 (high half)(高半週) data_all = {data[23:0], 8'b0} PI_antiWindup.data (24-bit signed) PI controller output — phase error / correction valuePI 控制器輸出——相位誤差/修正值 0xC5
0 (low half)(低半週) amplitude_info = {Rx_peak, 16'b0} Rx_peak_cc or Rx_peak_ir (16-bit, selected by amplitude_select_cc_ir) Peak amplitude of CC or IR receive pathCC 或 IR 接收路徑的峰值振幅 0xBA

40-bit Packet Format40 位元封包格式

Bits位元Field欄位PI / phase packetPI/相位封包Amplitude packet振幅封包
[39:32]start_iden_code (prefix)start_iden_code(前綴)0xD40xEE
[31:8]Payload (24 bits)酬載(24 位元)data_switch[31:8] = PI[23:0]data_switch[31:8] = {Rx_peak[15:0], 8'b0}
[7:0]Type marker (postfix)類型標記(後綴)0xC50xBA
Note: 40 bits = 5 bytes = 1 prefix (start_iden_code) + 3 payload + 1 marker. Only data[31:8] (the top 24 bits) is sent; the low byte data[7:0] is dropped to make room. tx_16bits U3 slices the 40 bits into five 8-bit bytes, which my_uart_tx U1 sends at 230400 baud. test_phase U2 and test_A U4 build the two variants (prefix = start_iden_code, marker = low byte of .in); clk1d25kHz selects between them (high = PI/phase, low = amplitude).
注意: 40 位元=5 位元組=1 前綴(start_iden_code)+3 酬載+1 標記。只送 data[31:8](高 24 位元);低位元組 data[7:0] 被捨棄以騰出空間。tx_16bits U3 把 40 位元切成五個 8 位元位元組,由 my_uart_tx U1230400 鮑送出。test_phase U2test_A U4 建立兩種變體(前綴=start_iden_code、標記=.in 的低位元組);clk1d25kHz 在兩者間選擇(高=PI/相位、低=振幅)。

Bit-Level TX Timing — clken advances one bit, trigger steps bytes位元級 TX 時序——clken 前進一位元、trigger 步進位元組

Beyond its data port, my_uart_tx U1 has two timing ports: .clken(txclk_en) (an input bit clock) and .trigger(trigger) (an output byte handshake).

除了資料埠之外,my_uart_tx U1 還有兩個時序埠:.clken(txclk_en)輸入的位元時脈)與 .trigger(trigger)輸出的位元組交握)。

clken — the baud tick (advances one bit)clken——鮑率節拍(前進一位元)

clken is the 230400-baud enable from speed_select (txclk_en). The state machine runs on the 50 MHz clk_50M, but every step is gated by if (clken), so it only moves forward on a baud tick:

clken 是來自 speed_select230400 鮑致能txclk_en)。狀態機跑在 50 MHz 的 clk_50M 上,但每一步都由 if (clken) 閘控,所以只在鮑率節拍上前進:

STATE_DATA: if (clken) begin tx <= data[bitpos]; // drive the current bit bitpos <= (bitpos==7) ? ... : bitpos+1; // step to the next bit end

Each clken pulse drives the next bit onto tx and increments bitpos — that is what "advances one bit" means. clken is high only 1 of every ~217 clk_50M cycles (50 MHz / 230400 ≈ 217), so each bit is held on the line for exactly one bit period:

每個 clken 脈衝把下一個位元推上 tx 並使 bitpos 遞增——這就是「前進一位元」的意思。clken 每約 217 個 clk_50M 週期只有 1 個為高(50 MHz / 230400 ≈ 217),所以每個位元在線上正好維持一個位元週期:

1 bit = 1 / 230400 s ≈ 4.34 µs ≈ 217 × 20 ns

The frame walks start → D0 → D1 → … → D7 → stop, one bit per clken tick.

訊框依序走 start → D0 → D1 → … → D7 → stop,每個 clken 節拍一個位元。

trigger — the byte-done handshake (steps bytes)trigger——位元組完成交握(步進位元組)

trigger is an output: my_uart_tx pulses it in the STOP state when a byte finishes, then clears it in IDLE. It feeds tx_16bits.trigger_16, telling the serializer to present the next of the five bytes in the 40-bit packet. So clken paces bits within a byte; trigger steps between bytes.

trigger輸出my_uart_tx 在 STOP 狀態於位元組完成時發出脈衝,並在 IDLE 清除。它接到 tx_16bits.trigger_16,通知序列器送出 40 位元封包五個位元組中的下一個。所以 clken 主導位元組之內的位元節奏;trigger 在位元組之間步進。

Timing & Signal Flow時序與信號流

── Frame timing ───────────────────────────────────────────────── clk1d25kHz (1.25 kHz) HIGH → PI data LOW → amplitude data clk2d5kHz (2.5 kHz) ▲ tx frame ▲ tx frame data_switch [0xD4] PI_data[23:0] [0xC5] [0xEE] {Rx_peak[15:0], 8'b0} [0xBA] tx (serial) 5 bytes @230400 5 bytes @230400 [..] = start_iden_code prefix (0xD4 PI / 0xEE amp) & trailing type marker — added inside UART_A → 40-bit frame ── Signal flow ────────────────────────────────────────────────── PI_antiWindup data[23:0] data_all {PI, 8'b0} peak_extractor Rx_peak_cc/ir amp_info {peak, 16'b0} sel: amp_select_cc_ir MUX sel: clk1d25kHz data_switch UART_A 230400 baud 40-bit packet clk2d5kHz (frame) ↓ tx pin → host PC no rx ── One byte on tx, paced by clken (230400 baud), LSB first — e.g. 0xC5 ── clken each divider = one clken tick (1 bit is held ~4.34 µs) tx START D0 D1 D2 D3 D4 D5 D6 D7 STOP trigger byte done → next byte (tx_16bits)

DE25 2×20 GPIO Expansion Header — Where tx Comes OutDE25 2×20 GPIO 擴充排針——tx 從哪裡出來

On the DE25 port (branch de25), the UART tx bitstream leaves the board on the 40-pin GPIO expansion header. The header has 36 user pins connected directly to the Agilex 5 SoC FPGA, plus DC +5 V (VCC5), DC +3.3 V (VCC3P3), and two GND pins (DE25 User Manual §3.8.3, Table 3-15/3-16, Figure 3-23). This design uses four of them (de25_top.v:71–75, _1Mhz_beta010.qsf:232–241):

在 DE25 移植(分支 de25)上,UART tx 位元流從40 腳 GPIO 擴充排針離開板子。排針有 36 個使用者腳位直接連到 Agilex 5 SoC FPGA,外加 DC +5 V(VCC5)、DC +3.3 V(VCC3P3)與兩個 GND 腳(DE25 使用手冊 §3.8.3,表 3-15/3-16,圖 3-23)。本設計用了其中四個de25_top.v:71–75_1Mhz_beta010.qsf:232–241

Header signal排針訊號FPGA pinFPGA 腳位Driven by驅動來源Role角色
GPIO[0]PIN_BK31uart_tx UART tx mirror #1 — 230400 baud serial bitstreamUART tx 鏡射 #1——230400 鮑序列位元流
GPIO[1]PIN_BE43uart_tx UART tx mirror #2 (same signal, second probe point)UART tx 鏡射 #2(同一訊號,第二個探測點)
GPIO[3]PIN_BF401'b0 Driven low as a 0 V probe referencenot a true ground; do not sink return current through it拉低作為 0 V 探測參考——不是真正的接地;不要讓回流電流流經它
GPIO[4]PIN_BK281'b0 Driven low as a 0 V probe referencenot a true ground; do not sink return current through it拉低作為 0 V 探測參考——不是真正的接地;不要讓回流電流流經它

Power Budget for Daughter Cards (Table 3-15)子卡供電預算(表 3-15)

The header can also power a daughter card, but the current is limited:

排針也能為子卡供電,但電流有限:

Supplied voltage供應電壓Max. current limit最大電流限制
5 V1 A
3.3 V1.5 A

Per-Pin Protection Circuit (Figure 3-23)每腳保護電路(圖 3-23)

Each of the 36 data pins passes through a series resistor and two clamp diodes on its way to header JP1. The diodes short any voltage excursion above VCC3P3 + one diode drop, or below GND − one diode drop, protecting the FPGA I/O bank from over/under-voltage. This is why every GPIO_D assignment in the qsf uses 3.3-V LVCMOS — the bank and the clamps are referenced to VCC3P3, so never drive the header from 5 V logic.

36 個資料腳的每一個在通往排針 JP1 的路上都經過一個串聯電阻與兩個箝位二極體。二極體把任何高於 VCC3P3 +一個二極體壓降、或低於 GND −一個二極體壓降的電壓短路,保護 FPGA I/O bank 免受過壓/欠壓。這就是為什麼 qsf 中每個 GPIO_D 指定都用 3.3 V LVCMOS——bank 與箝位都以 VCC3P3 為參考,所以絕不要用 5 V 邏輯驅動排針。

Agilex™ 5 SoC FPGA 3.3-V LVCMOS bank GPIO_0[35..0] 36 series R VCC3P3 (+3.3 V) clamps > 3.3 V + Vₖ clamps < 0 V − Vₖ JP1 2×20 header

Redrawn from DE25 User Manual Figure 3-23 — one series resistor + two clamp diodes per data pin. The protection R adds source impedance: fine at 230400 baud, but it slows edges if you hang a long unterminated cable on the pin.

重繪自 DE25 使用手冊圖 3-23——每個資料腳一個串聯電阻+兩個箝位二極體。保護電阻增加源阻抗:在 230400 鮑下沒問題,但如果在腳位上掛一條長且未端接的線,它會拖慢邊緣。

Pin Assignment of the Expansion Header (Table 3-16)擴充排針腳位配置(表 3-16)

All 36 user pins are 3.3-V I/O. The three rows used by this design are highlighted — they are exactly the qsf's GPIO_D[33..35] assignments.

全部 36 個使用者腳位都是 3.3 V I/O。本設計使用的三列已標亮——正是 qsf 的 GPIO_D[33..35] 指定。

Full Table 3-16 — all 36 pins完整表 3-16——全部 36 腳 (click to expand)
Signal訊號FPGA pinFPGA 腳位I/O stdI/O 標準
GPIO[0]PIN_BK313.3 V — uart_tx
GPIO[1]PIN_BE433.3 V — uart_tx
GPIO[2]PIN_BF293.3 V
GPIO[3]PIN_BF403.3 V — 0 V probe ref
GPIO[4]PIN_BK283.3 V — 0 V probe ref
GPIO[5]PIN_BM313.3 V
GPIO[6]PIN_BM283.3 V
GPIO[7]PIN_BP313.3 V
GPIO[8]PIN_BR313.3 V
GPIO[9]PIN_BU283.3 V
GPIO[10]PIN_BU313.3 V
GPIO[11]PIN_BW283.3 V
GPIO[12]PIN_BR223.3 V
GPIO[13]PIN_BU193.3 V
GPIO[14]PIN_BU223.3 V
GPIO[15]PIN_BW193.3 V
GPIO[16]PIN_BH283.3 V
GPIO[17]PIN_BR283.3 V
Signal訊號FPGA pinFPGA 腳位I/O stdI/O 標準
GPIO[18]PIN_BF363.3 V
GPIO[19]PIN_BE293.3 V
GPIO[20]PIN_BM223.3 V
GPIO[21]PIN_BK223.3 V
GPIO[22]PIN_BR193.3 V
GPIO[23]PIN_BM193.3 V
GPIO[24]PIN_BK193.3 V
GPIO[25]PIN_BH193.3 V
GPIO[26]PIN_BF253.3 V
GPIO[27]PIN_CF93.3 V
GPIO[28]PIN_CH123.3 V
GPIO[29]PIN_CF123.3 V
GPIO[30]PIN_CK23.3 V
GPIO[31]PIN_CJ23.3 V
GPIO[32]PIN_BE253.3 V
GPIO[33]PIN_BF213.3 V
GPIO[34]PIN_BF163.3 V
GPIO[35]PIN_BE213.3 V

8. I/Q Amplitude Balance & Overflow AvoidanceI/Q 振幅平衡與溢位避免

Image rejection needs the I and Q paths equal in amplitude and exactly 90° apart; any gain mismatch between the two DAC channels (and their analog buffers/filters) leaks a residual image sideband that caps the achievable rejection. The correction is a small scalar trim of ≈2.2% on one channel. How that trim is applied — scaling one channel up vs. the other down — is functionally equivalent for balance but not for overflow. (Related: the 90° split itself is covered in §4 Hilbert Transform & Quadrature.)

影像抑制需要 I 與 Q 路徑振幅相等、相位正好相差 90°;兩個 DAC 通道(及其類比緩衝器/濾波器)之間的任何增益不匹配都會洩漏殘餘影像邊帶,限制可達到的抑制。校正是在單一通道上約 2.2% 的小純量微調。這個微調如何施加——把一個通道調還是把另一個調——對平衡而言功能等價,對溢位而言則不然。(相關:90° 分割本身見 §4 希爾伯特變換與正交。)

Two ways to apply the same 1.022 balance施加同一個 1.022 平衡的兩種方式

Resolved 2026-09-04. The design now uses the fixed-point scheme on the Q channel: v_L * 64125 >> 16 (0.978470, −0.0004 % from 1/1.022), rounded. The floating-point ×1.022 on I described below is history; it could wrap the 14-bit DAC code at full scale once the KEY2 gain moved to the source.

2026-09-04 已解決。設計現在採用 Q 通道上的定點方案:v_L * 64125 >> 16(0.978470,與 1/1.022 相差 −0.0004 %),含四捨五入。下文所述 I 通道的浮點 ×1.022 已成歷史;在 KEY2 增益移到訊號源之後,它會在滿刻度時讓 14 位元 DAC 碼繞回。

Current code: balance by ÷1.022 (gain < 1), not ×1.022 The present _1Mhz_beta010.v no longer boosts the I path by ×1.022. Instead the delayed (I/DAC_DA) channel runs at unity (MULT constant 32'h3f800000 = ×1.0), and the other channel (DAC_DB) is scaled down by 1/1.022 ≈ 0.9785. The relative balance is identical, but because the only active multiplier is < 1 no sample can ever exceed the 14-bit full scale — precisely the overflow/fold failure the ×1.5 plots in §4 illustrate.
目前程式碼:以 ÷1.022(增益 < 1)平衡,而非 ×1.022 現行的 _1Mhz_beta010.v 不再把 I 路徑放大 ×1.022。改為讓延遲(I/DAC_DA)通道跑在單位增益MULT 常數 32'h3f800000=×1.0),另一個通道(DAC_DB)被調 1/1.022 ≈ 0.9785。相對平衡完全相同,但因為唯一有效的乘數小於 1,任何取樣都不可能超過 14 位元滿刻度——正是 §4 的 ×1.5 圖所示的溢位/摺疊失效。
Scheme A — original方案 A——原始Scheme B — current code方案 B——目前程式碼
I / delayed ch. (DAC_DA)×1.022 (32'h3f82d0e5)×1.0 (32'h3f800000)
other ch. (DAC_DB)×1.0×0.9785 = 64125/65536 ≈ 1/1.022
relative ratio1.022 : 11.022 : 1 (identical balance)
arithmeticFP MULT (rounded)浮點 MULT(捨入)integer Q16 multiply + >>>16 (truncated)整數 Q16 乘法+>>>16(截斷)
overflow riskpeak ×1.022 can pass ±8191 → folds峰值 ×1.022 可能越過 ±8191 → 摺疊gain < 1 → never overflows增益 < 1 → 永不溢位

Fixed-point implementation定點實作

v_q_scaled = v · 64125 // Q16 fixed-point, 18'sd64125 v_q_bal = v_q_scaled >>> 16 // 64125/65536 = 0.978470 ≈ 1/1.022, gain<1 ⇒ no overflow

K = round(2¹⁶ / 1.022) = 64125, so the realised gain is 64125/65536 = 0.978470 (≈ −0.0004% off the ideal 1/1.022). The signed sample v is multiplied, arithmetic-shifted back to 14 bits, then re-encoded offset-binary for the DAC (v_Tx_bal).

K = round(2¹⁶ / 1.022) = 64125,所以實現的增益是 64125/65536 = 0.978470(與理想的 1/1.022 相差約 −0.0004%)。有號取樣 v 先相乘、算術右移回 14 位元,再重新編碼成偏移二進位送 DAC(v_Tx_bal)。

Trade-off: Scheme B's >>>16 is a floor-truncation (≤ 1 LSB downward bias) versus the FP path's rounding — negligible for image balance, and worth it to guarantee no fold on peaks. Source: _1Mhz_beta010.v:191–196 (the Q16 balance), :235 (I-path unity MULT), :246 (Q feed). The Rx side keeps an analogous balance trim 32'h3f83d70a ≈ ×1.03 in the floating-point domain.
取捨: 方案 B 的 >>>16 是向下截斷(≤ 1 LSB 的向下偏差),相對浮點路徑的捨入——對影像平衡而言可忽略,換得峰值保證不摺疊,值得。出處:_1Mhz_beta010.v:191–196(Q16 平衡)、:235(I 路徑單位增益 MULT)、:246(Q 饋入)。Rx 端在浮點域保留類似的平衡微調 32'h3f83d70a ≈ ×1.03。

9. How the 40 kHz Sine Is Generated40 kHz 弦波如何產生

There is no sine lookup table and no NCO. The transmit sine v is a self-sustained limit cycle produced by a relay-feedback (describing-function) oscillator in relay_feedback2.v: a high-Q digital resonator whose own output is fed back through a relay (sign comparator). This is the signal-level / waveform view; the transfer-function derivation lives in §2 Relay Feedback Oscillator.

這裡沒有弦波查找表、也沒有 NCO。發射弦波 vrelay_feedback2.v繼電回授(描述函數)振盪器產生的自持極限環:一個高 Q 數位諧振器,其自身輸出經繼電器(符號比較器)回授。這是訊號位準/波形的視角;轉移函數推導在 §2 繼電回授振盪器

Relay-Feedback Loop繼電回授迴路

Stage階段Code程式碼Role角色
Relayres_in <= ±256·sign(f1024) + y_in (:45)takes only the sign of the resonator output → a ±256 square wave (the nonlinearity that keeps the loop alive)只取諧振器輸出的符號 → ±256 方波(維持迴路運作的非線性元件)
Resonator F(z)f1024 <= f1024 − f1024/1024 + e + 2·e_1 + e_2 (:27)pole at 1023/1024 (just inside the unit circle) → very high Q, narrow band at 40 kHz; passes the square's fundamental, rejects harmonics極點在 1023/1024(單位圓內側一點點)→ 非常高的 Q、40 kHz 窄帶;讓方波的基頻通過、排除諧波
Integrator G(z) + errorg1024 += temp;   e = res_in − g1024/1024 (:44, :21)second loop state that sets the exact centre frequency and closes the feedback第二個迴路狀態,設定精確的中心頻率並閉合回授
Why it oscillates — and why it's a sine: Frequency: the loop phase returns to 0° only at the resonator's centre, so it locks to 40 kHz. Amplitude: the relay's describing-function gain drops as amplitude grows, so the limit cycle self-limits at a stable level (driving f1024 toward its ±2²³ rail). Shape: the relay emits a square wave, but the high-Q bandpass keeps only the 40 kHz fundamental, so the state f1024 traces a sine.
為什麼會振盪——為什麼是弦波: 頻率:迴路相位只在諧振器中心回到 0°,所以鎖在 40 kHz振幅:繼電器的描述函數增益隨振幅增大而下降,所以極限環自我限制在穩定位準(把 f1024 推向其 ±2²³ 邊界)。形狀:繼電器發出的是方波,但高 Q 帶通只保留 40 kHz 基頻,所以狀態 f1024 描出弦波

Waveform — Relay Drive → Resonant Sine波形——繼電驅動 → 諧振弦波

The small ±256 relay square wave (blue), in phase with the sign of the output, drives the high-Q resonator; the resonator builds it up into a large, clean 40 kHz sine (amber). Square in → sine out is exactly what the narrow bandpass does.

小小的 ±256 繼電方波(藍色),與輸出的符號同相,驅動高 Q 諧振器;諧振器把它建立成又大又乾淨的 40 kHz 弦波(琥珀色)。方波進 → 弦波出,正是窄帶通的本事。

resonator output f1024 (≈40 kHz sine) relay output ±256 = sign(f1024) zero
Kick-start: a zero initial state would stay at zero forever, so g1024 is seeded non-zero ({4'b0001,24'b0}, relay_feedback2.v:41) to perturb the loop and let the oscillation build. Output taps: v = f1024[23:10] (14-bit, to the Tx path), u = f1024[23:11] (13-bit, half amplitude). f1024/e update on negedge clk10MHz and g1024/res_in on posedge — the two half-loops interleave to form the resonator at 10 MHz.
起振: 零初始狀態會永遠停在零,所以 g1024 以非零值播種({4'b0001,24'b0}relay_feedback2.v:41)以擾動迴路、讓振盪建立。輸出抽頭:v = f1024[23:10](14 位元,送 Tx 路徑)、u = f1024[23:11](13 位元,半振幅)。f1024enegedge clk10MHz 更新、g1024res_inposedge——兩個半迴路交錯,構成 10 MHz 下的諧振器。
10. Integrator Gain, F(z), and Clock Scaling積分器增益、F(z) 與時脈縮放 (click to collapse)

These notes capture two related questions about the relay-feedback oscillator in relay_feedback2.v, the block that generates the 40 kHz carrier for the radar IQ pipeline. The first question asks what changes when the integrator gain — the 0.5 × 332/1024 coefficient — is increased. The second asks whether the lowpass filter F(z) inside the loop is a bandpass. The two answers share one core idea: the bandpass/resonator behaviour belongs to the closed loop, not to any single block, and the integrator gain is the knob that sets its center frequency.

本筆記整理關於 relay_feedback2.v 中繼電回授振盪器的兩個相關問題——這個模組負責產生雷達 IQ 流水線的 40 kHz 載波。第一個問題:提高積分器增益(0.5 × 332/1024 那個係數) 會改變什麼?第二個問題:迴路裡的低通濾波器 F(z) 是帶通嗎?兩個答案共享同一個核心概念: 帶通/諧振器的特性屬於整個閉迴路,而不是任何單一方塊;積分器增益就是設定其中心頻率的旋鈕。

10.1  The Integrator Gain and What It Controls積分器增益控制什麼

10.1.1  Where the expression lives in the RTL這個式子在 RTL 的哪裡

The transfer function in question, $G(z) = \dfrac{0.5 \times (332/1024)}{1 - z^{-1}}$, is the integrator accumulated in register g1024 at every 10 MHz clock edge. The gain is not a multiplier — it is a shift-add sum:

問題中的傳遞函數 $G(z) = \dfrac{0.5 \times (332/1024)}{1 - z^{-1}}$,就是暫存器 g1024 在每個 10 MHz 時脈邊緣累加的積分器。這個增益不是乘法器,而是移位相加:

$$\texttt{temp} \;=\; \texttt{f1024}\times\left(2^{-3}+2^{-5}+2^{-8}+2^{-9}\right) \;=\; \texttt{f1024}\times\frac{166}{1024} \;=\; \texttt{f1024}\times\frac{0.5\times 332}{1024}$$ Shift-add gain decomposition增益的移位相加分解

Verbatim from relay_feedback2.v (lines 38–44):

relay_feedback2.v 第 38–44 行原文:

  wire signed [23:0] temp;

  assign  temp = {{3{f1024[23]}},f1024[23:3]}+{{5{f1024[23]}},f1024[23:5]}+{{8{f1024[23]}},f1024[23:8]}+{{9{f1024[23]}},f1024[23:9]};
  always @(posedge clk10MHz) begin
		  g1024 <= g1024+{{4{temp[23]}},temp};        // G(z)=0.5*332/(1024-1024z^-1)
		  res_in <= {{9{f1024[23]}},1'b1,8'b0}+y_in;
  end

The integrator output is subtracted from the relay drive at line 26, and the resulting error feeds the lowpass F(z) at line 30:

積分器輸出在第 26 行從繼電器驅動訊號中被減掉,得到的誤差在第 30 行送進低通 F(z)

  wire signed [17:0] e;
  assign  e = res_in-g1024[27:10];
  ...
  always @(negedge clk10MHz) begin
        u_past <= f1024[23:11]; // u_past is cut in half
        f1024 <= f1024-{{10{f1024[23]}},f1024[23:10]}+{{6{e[17]}},e}+{{5{e_1[17]}},e_1,1'b0}+{{6{e_2[17]}},e_2};   // F(z)=(z^-0.5+z^-1.5)/(1024-1023z^-1)
		  e_1 <= e;
		  e_2 <= e_1;
  end

10.1.2  The loop is a lightly damped resonator迴路是一個低阻尼諧振器

An integrator wrapped in feedback around a first-order lag makes a second-order, lightly damped resonator. In a continuous-time approximation, the closed-loop linear block from the relay output to f1024 is:

積分器對一階遲滯做回授,就構成一個二階、低阻尼的諧振器。用連續時間近似,從繼電器輸出到 f1024 的閉迴路線性方塊為:

$$H(s) \;\approx\; \frac{F_{dc}\,s}{\tau s^{2} + s + 2g}, \qquad \omega_0 \;=\; \sqrt{\frac{2g}{\tau}}$$ Closed-loop resonator & center frequency閉迴路諧振器與中心頻率

where $g$ is the integrator gain per unit time (proportional to the 332 coefficient) and $\tau$ is the time constant of $F(z)$ (pole at $1023/1024$, about $102\ \mu\text{s}$ at 10 MHz). The relay — res_in = ±256·sign(f1024) plus the injection u_inj — locks the oscillation onto this resonance; that is where the loop phase condition is met, and that is what sets the 40 kHz.

其中 $g$ 是積分器每單位時間的增益(正比於係數 332),$\tau$ 是 $F(z)$ 的時間常數(極點在 $1023/1024$,10 MHz 下約 $102\ \mu\text{s}$)。繼電器——res_in = ±256·sign(f1024) 加上注入訊號 u_inj——會把振盪鎖在這個諧振點上:迴路相位條件在此滿足,這就是 40 kHz 的來源。

relay 繼電器 ±256·sign(f1024) u_inj (y_in) 注入 res_in Σ + e F(z) lowpass 低通 pole z = 1023/1024 f1024 u, v out G(z) integrator 積分器 (166/1024)/(1−z⁻¹) g1024[27:10] sign(f1024) — relay closes the loop 繼電器閉合迴路
Loop topology of relay_feedback2.v: the relay drive $\pm 256$ plus injection $u_{inj}$ forms res_in; the integrator output $G(z)\,f_{1024}$ is subtracted at the summing node ($e = \texttt{res\_in} - \texttt{g1024}[27{:}10]$); $F(z)$ filters $e$ into $f_{1024}$, whose sign drives the relay again. The linear part seen by the relay is $H = F/(1+FG)$.
relay_feedback2.v 的迴路拓撲:繼電器驅動 $\pm 256$ 加上注入 $u_{inj}$ 構成 res_in;積分器輸出 $G(z)\,f_{1024}$ 在加總節點被減掉 ($e = \texttt{res\_in} - \texttt{g1024}[27{:}10]$);$F(z)$ 把 $e$ 濾成 $f_{1024}$, 其正負號再驅動繼電器。繼電器看到的線性部分為 $H = F/(1+FG)$。
INTUITION — why integrator feedback makes a resonator直覺——為什麼積分器回授會變成諧振器

Seen alone, $F(z)$ is just a first-order lowpass (an energy-storage element) and $G(z)$ just an integrator. But subtracting the integrator output at $F$'s input adds a restoring force: the longer $f_{1024}$ stays away from zero, the more $g_{1024}$ accumulates and the harder it pulls back — exactly the spring-mass structure, two first-order stages cascading into a second-order oscillation cavity. The damping is set by $F$'s leakage (how far its pole sits from $z=1$) and is very small, so the cavity is a high-Q resonator. The relay acts like a clock's escapement, replenishing energy every half cycle so the oscillation never decays.

單獨看,$F(z)$ 只是個一階低通(儲能元件),$G(z)$ 只是個積分器。但把積分器的輸出從 $F$ 的輸入端減掉,就等於加上一個「回復力」:$f_{1024}$ 偏離零越久,$g_{1024}$ 累積得越多, 把它往回拉的力道就越強——這正是彈簧-質量系統的結構,兩個一階級聯成一個二階振盪腔。 阻尼由 $F$ 的洩漏(極點離 $z=1$ 的距離)決定,非常小,所以是高 Q 諧振器。 繼電器則像鐘擺的擒縱機構,每半週期補一次能量,讓振盪不衰減。

10.1.3  Four effects of increasing the integrator gain提高積分器增益的四個影響

(a)  Oscillation frequency rises as √gain — the tuning knob振盪頻率以 √增益 上升——調頻旋鈕

Since $\omega_0 = \sqrt{2g/\tau}$, doubling the 332 coefficient raises the frequency by $\sqrt{2} \approx 1.41\times$. To retune from 40 kHz to a target $f$, scale the coefficient by $(f/40\,\text{kHz})^2$. This coefficient is the primary frequency-tuning knob of the oscillator.

因為 $\omega_0 = \sqrt{2g/\tau}$,把係數 332 加倍會使頻率升高 $\sqrt{2} \approx 1.41$ 倍。 要從 40 kHz 調到目標頻率 $f$,係數要乘上 $(f/40\,\text{kHz})^2$。 這個係數是這顆振盪器主要的調頻旋鈕。

(b)  Higher Q, lower dampingQ 值更高、阻尼更低

The damping ratio $\zeta = 1/(2\omega_0\tau)$ shrinks as $\omega_0$ rises. Practically: better rejection of harmonics and injection noise, but a slower-settling startup envelope and a more sluggish response to phase steps from u_inj.

阻尼比 $\zeta = 1/(2\omega_0\tau)$ 隨 $\omega_0$ 上升而變小。實務上:對諧波與注入雜訊的 抑制更好,但啟動時包絡收斂更慢,對 u_inj 相位階躍的反應也更遲鈍。

(c)  Fundamental amplitude roughly unchanged基頻振幅大致不變

At resonance the $-\tau\omega_0^2$ and $+2g$ terms cancel, leaving $|H(j\omega_0)| = F$'s passband gain, independent of the integrator coefficient. So the describing-function amplitude of u/v is, to first order, unchanged.

在諧振點上 $-\tau\omega_0^2$ 與 $+2g$ 兩項互相抵消,$|H(j\omega_0)|$ 只剩 $F$ 的通帶增益, 與積分器係數無關。所以由描述函數決定的 u/v 基頻振幅, 一階近似下不會改變。

(d)  g1024 overflow risk growsg1024 溢位風險增加

The integrator's own swing at the oscillation frequency is $|G(j\omega_0)| \propto g/\omega_0 \propto \sqrt{g}$, so a larger coefficient makes g1024 (and temp) swing wider.

積分器本身在振盪頻率的擺幅為 $|G(j\omega_0)| \propto g/\omega_0 \propto \sqrt{g}$, 係數越大,g1024(和 temp)就擺得越寬。

OVERFLOW溢位注意

The header comment of relay_feedback2.v records that overflow already forced a register-widening once: “the overflow happens for f1024 (u) and g1024, so this version … gives one more bit to f1024 and e and e_1.” If you raise the gain much, re-check the 28-bit headroom of g1024 and re-derive the g1024[27:10] slice used for e.

relay_feedback2.v 檔頭註解記載曾因溢位而把暫存器加寬過一次: “the overflow happens for f1024 (u) and g1024, so this version … gives one more bit to f1024 and e and e_1.” 若要大幅提高增益,請重新檢查 g1024 的 28-bit 餘裕, 並重新推導 e 所用的 g1024[27:10] 切片。

Effect of increasing gain提高增益的影響 English英文說明 Chinese中文說明
Frequency頻率 Rises as $\propto\sqrt{g}$; retune factor $(f_{new}/f_{old})^2$ on the coefficient 以 $\sqrt{增益}$ 上升;調到目標頻率需把係數乘 $(f_{new}/f_{old})^2$
Q / damping ratioQ 值/阻尼比 Q up, $\zeta = 1/(2\omega_0\tau)$ down; sharper resonance, slower startup, sluggish $u_{inj}$ response Q 值升高、阻尼比變小;諧振更尖銳,啟動更慢,對 $u_{inj}$ 相位階躍反應更遲鈍
Amplitude振幅 Roughly unchanged — $|H(j\omega_0)|$ equals $F$'s passband gain, independent of $g$ 基頻振幅大致不變—— $|H(j\omega_0)|$ 只剩 $F$ 的通帶增益,與 $g$ 無關
Overflow溢位 Risk grows — $|G(j\omega_0)| \propto \sqrt{g}$; check g1024 28-bit headroom and the [27:10] slice 風險增加—— $g_{1024}$ 擺幅 $\propto\sqrt{g}$;需檢查 28-bit 餘裕與 [27:10] 切片
IMPLEMENTATION實作注意

The gain is realized as the shift-add sum at line 40: $2^{-3}+2^{-5}+2^{-8}+2^{-9} = 166/1024$ — not a real multiplier. Changing the gain means picking a new sum of powers of two — e.g. $0.5\times 512/1024 = 0.25$ is just f1024[23:2]. Values that are not a short sum of powers of two cost extra adder terms.

增益是第 40 行的移位相加:$2^{-3}+2^{-5}+2^{-8}+2^{-9} = 166/1024$——不是真正的乘法器。 改增益意味著重新挑一組 2 的冪次和——例如 $0.5\times 512/1024 = 0.25$ 就只是 f1024[23:2]。無法用少數幾個 2 的冪次表示的數值,會多花加法器項。

10.2  Is F(z) a Bandpass?F(z) 是帶通嗎?

No — F(z) alone is a first-order lowpass; the bandpass character belongs to the closed loop.

不是——$F(z)$ 本身是一階低通;帶通特性屬於整個閉迴路。

10.2.1  F(z) by itself單獨看 F(z)

From the f1024 update (line 30):

f1024 的更新式(第 30 行):

  • The denominator pole sits at $z = 1023/1024$ — a leaky integrator. At 10 MHz sampling this is a cutoff frequency around $\frac{1}{1024}\cdot\frac{10\,\text{MHz}}{2\pi} \approx 1.55\ \text{kHz}$, rolling off at $-20$ dB/dec above it. The 40 kHz oscillation therefore sits outside F's own passband, in its integrator-like region.
    分母極點在 $z = 1023/1024$——一個洩漏積分器。10 MHz 取樣下截止頻率約 $\frac{1}{1024}\cdot\frac{10\,\text{MHz}}{2\pi} \approx 1.55\ \text{kHz}$,之後以 $-20$ dB/dec 滾降。因此 40 kHz 的振盪落在 $F$ 自己的通帶之外, 處於近似積分器的區段。
  • The numerator $e + 2e_1 + e_2 = (1+z^{-1})^2$ places zeros at Nyquist (5 MHz) — it only adds extra high-frequency attenuation and provides the half-sample delay ($z^{-0.5}$ equivalent) noted in the source comment. It does not create a bandpass.
    分子 $e + 2e_1 + e_2 = (1+z^{-1})^2$ 的零點在 Nyquist(5 MHz)——只是多壓一點高頻, 並提供原始碼註解所寫的半取樣延遲($z^{-0.5}$ 等效)。它不會造出帶通。

DC in, DC out: viewed alone, $F(z)$ is a first-order lowpass with no ability to reject low frequencies.

DC 進來、DC 出去:單獨看,$F(z)$ 就是一階低通,沒有抑制低頻的能力。

10.2.2  Where the bandpass comes from帶通從哪裡來

The bandpass appears in the closed loop $H = F/(1+FG)$. The integrator feedback has near-infinite gain at low frequency, which inflates the denominator there — equivalent to a zero at DC in the overall transfer (DC is completely removed). The high-frequency end rolls off through F's pole. Squeezed between the two:

帶通出現在閉迴路 $H = F/(1+FG)$。積分器回授在低頻的增益趨近無限大,把分母在低頻端撐大—— 等效於整體傳遞函數在 DC 多了一個零點(DC 被完全消掉);高頻端則由 $F$ 的極點滾降。 兩者夾出來的就是:

$$H(s) \;\approx\; \frac{F_{dc}\,s}{\tau s^{2} + s + 2g} \qquad\Longrightarrow\qquad \text{2nd-order bandpass, center } \omega_0 = \sqrt{\frac{2g}{\tau}}$$ Closed loop = resonator閉迴路=諧振器

Zero gain at DC, rolloff at high frequency, peak at $\omega_0$ — a second-order bandpass/resonator. This is also why the integrator gain (the 332 coefficient) moves the center frequency: it does not reshape $F$, it moves the resonance of the whole loop.

DC 增益為零、高頻滾降、峰值在 $\omega_0$——一個二階帶通/諧振器。這也是為什麼積分器增益 (係數 332)動的是中心頻率:它不是改 $F$ 的形狀,而是移動整個迴路的諧振點。

F(z) alone — lowpass 單獨的 F(z)——低通 log f |F| ≈1.55 kHz 截止頻率 40 kHz −20 dB/dec Closed loop H = F/(1+FG) — bandpass 閉迴路——帶通諧振器 log f |H| 40 kHz = ω₀/2π zero at DC F pole rolloff
Left: $F(z)$ alone — first-order lowpass, cutoff frequency $\approx 1.55$ kHz, so 40 kHz lies on its $-20$ dB/dec slope. Right: the closed loop $H = F/(1+FG)$ — integrator feedback kills DC, F's pole kills high frequency, leaving a sharp resonance at $\omega_0 = \sqrt{2g/\tau}$ (40 kHz).
左:單獨的 $F(z)$——一階低通,截止頻率約 $1.55$ kHz,40 kHz 落在其 $-20$ dB/dec 斜坡上。右:閉迴路 $H = F/(1+FG)$——積分器回授消掉 DC,$F$ 的極點消掉高頻,留下位於 $\omega_0 = \sqrt{2g/\tau}$(40 kHz)的尖銳諧振。
KEY RESULT關鍵結果

$F(z)$ is a lowpass; $H = F/(1+FG)$ is the bandpass. The relay locks onto the peak of $H$, and the integrator gain slides that peak along the frequency axis.

$F(z)$ 是低通;帶通是閉迴路 $H = F/(1+FG)$。繼電器鎖在 $H$ 的峰值上, 而積分器增益就是沿頻率軸滑動這個峰值位置的旋鈕。

INTUITION — the analogy直覺類比

$F$ is the energy-storage element (a capacitor, or a mass); $G$'s integral feedback plays the restoring force (the spring); the two first-order stages cascade into a second-order oscillation cavity, and the relay replenishes the losses so the cavity keeps ringing at its own center frequency. Asking whether $F(z)$ is a bandpass is like asking whether a spring is an oscillator — a single element is not, the combination is. DC passes straight through $F$; it is the integrator feedback's infinite gain at DC that removes it completely and carves out the bandpass shape.

$F$ 是「儲能元件」(像電容/質量),$G$ 的積分回授扮演「回復力」(像彈簧), 兩個一階級聯成一個二階振盪腔;繼電器則負責補償損耗,讓這個腔在自己的中心頻率上持續振盪。 所以問「$F(z)$ 是不是帶通」就像問「彈簧是不是振盪器」——單一元件不是,組合起來才是。 DC 進來、DC 出去,$F(z)$ 攔不住低頻;是積分器回授在 DC 的無限增益把它完全消掉, 才夾出帶通的形狀。

10.3  Scaling the Clock Period T縮放時脈週期 T

10.3.1  Clock scaling moves frequency linearly時脈縮放使頻率線性移動

Shortening the clock period — i.e. raising clk10MHz — scales the oscillation frequency linearly: $f_{osc} \propto f_{clk}$. Double the clock and the 40 kHz oscillation becomes 80 kHz.

縮短時脈週期——也就是提高 clk10MHz——會使振盪頻率線性移動: $f_{osc} \propto f_{clk}$。時脈加倍,40 kHz 的振盪就變成 80 kHz。

Why linear, rather than the $\sqrt{\cdot}$ law of the gain knob? All loop coefficients are dimensionless per-sample numbers, so a discrete-time design has no absolute notion of Hz — every frequency in it is a fixed fraction of the sample rate (a normalized frequency). In the continuous-time equivalents both parameters carry $T$: the integrator gain is $g = k_i/T$ and the lowpass time constant is $\tau = 1024\,T$, hence:

為什麼是線性,而不是增益旋鈕的 $\sqrt{\cdot}$ 規律?迴路裡所有係數都是無因次的「每取樣」數值, 所以離散時間設計本身沒有絕對的 Hz 概念——裡面的每個頻率都是取樣率的固定比例(正規化頻率)。 換算成連續時間等效時,兩個參數都帶著 $T$:積分器增益 $g = k_i/T$,低通時間常數 $\tau = 1024\,T$,因此:

$$\omega_0=\sqrt{\frac{2g}{\tau}} =\frac{1}{T}\sqrt{\frac{2k_i}{1024}} \;\propto\; f_{clk}$$ Center frequency scales with the clock中心頻率隨時脈線性縮放

The key contrast with raising the gain: clock scaling changes nothing else. In the damping ratio, $T$ cancels out entirely:

與提高增益的關鍵對比:時脈縮放不改變其他任何東西。在阻尼比裡,$T$ 完全消掉:

$$\zeta=\frac{1}{2\omega_0\tau} =\frac{1}{2\cdot 1024}\sqrt{\frac{1024}{2k_i}}$$ Damping ratio is independent of T阻尼比與 T 無關

Q, the startup settling time measured in oscillation cycles, the amplitude, and the fixed-point swings in f1024/g1024 are all exactly preserved — the waveform is the same waveform, played faster. There is no new overflow risk. This is the "clean" retuning knob, whereas the 332 coefficient moves the frequency as $\sqrt{g}$ and drags Q and the overflow margin along with it.

Q 值、以振盪週期數計的啟動收斂時間、振幅、以及 f1024/g1024 的定點擺幅全部原封不動——波形還是同一個波形,只是播放得更快。也沒有新的溢位風險。 這是「乾淨」的調頻旋鈕;相較之下,係數 332 讓頻率以 $\sqrt{g}$ 移動, 連帶拖動 Q 值和溢位餘裕。

KEY RESULT關鍵結果

The clock is the coarse band knob — everything scales together. The integrator coefficient is the fine tuning within the band — with Q and headroom side effects.

時脈是粗調的頻段旋鈕——所有東西一起縮放。積分器係數是頻段內的細調——但帶有 Q 值與 餘裕(headroom)的副作用。

10.3.2  Three cases of "decreasing T only"「只縮小 T」的三種情況

Case 1 — scale the whole clock tree情況一:整棵時脈樹一起縮放

Feed clock_generator.v with, say, 100 MHz instead of 50 MHz. The digital system stays internally consistent: the oscillator runs at 80 kHz, all filter corners double, and the 62.5-cycle delay is still exactly $T/4$ — the samples-per-period count stays 250 because the 10 MHz / 40 kHz ratio is preserved. The demodulator and PI ratios all survive. Breakage happens only at the boundaries: the UART's 230400 baud rate becomes 460800 (the PC side stops decoding); the PWM and ADC/DAC rates double (the THDB-ADA has headroom, so that part is fine); and the analog world — the physical resonance, matching, and filters the 40 kHz was chosen for — does not move, so the retuned digital system now drives a detuned plant.

clock_generator.v 的輸入從 50 MHz 換成例如 100 MHz。 數位系統內部仍然自洽:振盪器跑在 80 kHz,所有濾波器轉折頻率加倍,62.5 週期的延遲 仍然正好是 $T/4$——每週期取樣數維持 250,因為 10 MHz/40 kHz 的比例被保留了。 解調器與 PI 的比例關係全部存活。破壞只發生在邊界上:UART 的 230400 鮑率變成 460800 (PC 端就解不出來了);PWM 與 ADC/DAC 速率加倍(THDB-ADA 有餘裕,這部分沒問題); 而類比世界——當初為了 40 kHz 挑選的實體諧振、匹配與濾波器——並不會跟著動, 所以重新調頻後的數位系統驅動的是一個失諧的受控體。

Case 2 — shrink T of only one domain情況二:只縮小單一時脈域的 T

Speed up only the clk10MHz divider and the system becomes internally inconsistent. The oscillator and the $T/4$ delay move together (both live on clk10MHz, so they stay coherent at 80 kHz), but the demodulator still samples at 250 kHz and the PI controller at 50 kHz — every designed-in cross-clock-domain ratio breaks: the 250 kHz / 40 kHz = 6.25 samples per carrier period, the PI loop gain per second, the 1.25 kHz UART framing. The result is not a cleanly shifted system but silent garbage.

只加快 clk10MHz 的除頻器,系統就內部不一致了。振盪器和 $T/4$ 延遲會一起移動 (兩者都在 clk10MHz 域上,所以在 80 kHz 仍然相干),但解調器仍以 250 kHz 取樣、PI 控制器仍在 50 kHz——每一個設計好的跨時脈域比例都被打破: 250 kHz/40 kHz = 每載波週期 6.25 個取樣、PI 每秒的迴路增益、1.25 kHz 的 UART 訊框。結果不是一個乾淨平移的系統,而是無聲無息的垃圾輸出。

WARNING警告

Case 2 is not a retuning strategy — it is just a bug. Don't do it.

情況二不是調頻策略——它就只是一個 bug。不要這樣做。

Case 3 — shrink T but keep 40 kHz情況三:縮小 T 但保持 40 kHz

If the target frequency is pinned, the coefficients must absorb the change. From $\omega_0 = \sqrt{2k_i/1024}\,/\,T$: keeping $\omega_0$ fixed when $T$ halves requires $k_i \times 1/4$ (332 → 83) — the integrator gain scales as $T^2$. Keeping $\tau$ fixed requires $F$'s pole to move from $1023/1024$ to $2047/2048$ — poles crowd toward $z=1$ as $1 - T/\tau$. This is the classic oversampling problem: the same physical dynamics need tiny coefficients and poles jammed against the unit circle, so coefficient quantization and accumulator rounding bite harder, and wider registers are needed (the 1024 shift-divides become 2048; small signals fall below the LSB of the shift-add terms sooner). Nothing about a faster clock is free once the target frequency is pinned.

如果目標頻率被釘住,改變就必須由係數吸收。由 $\omega_0 = \sqrt{2k_i/1024}\,/\,T$:$T$ 減半而 $\omega_0$ 不變,需要 $k_i \times 1/4$(332 → 83)——積分器增益以 $T^2$ 縮放。要保持 $\tau$ 不變,$F$ 的極點得從 $1023/1024$ 移到 $2047/2048$——極點以 $1 - T/\tau$ 的方式往 $z=1$ 擠。這就是經典的過取樣問題:同樣的物理動態需要更小的係數、 以及緊貼單位圓的極點,於是係數量化與累加器捨入的傷害更大,需要更寬的暫存器 (1024 的移位除法變成 2048;小訊號更早掉到移位相加項的 LSB 以下)。 一旦目標頻率被釘住,更快的時脈沒有一樣是免費的。

Case情況 What scales什麼被縮放 What breaks什麼壞掉 Verdict結論
1 — whole clock tree整棵時脈樹 Everything together: $f_{osc}$, filter corners, delays stay $T/4$全部一起:$f_{osc}$、濾波器轉折、延遲仍為 $T/4$ Boundaries only: UART baud rate, PWM/ADC/DAC rates, detuned analog plant只有邊界:UART 鮑率、PWM/ADC/DAC 速率、類比端失諧 Internally clean; fix the boundaries內部乾淨;邊界要另外處理
2 — one domain only只動單一時脈域 Only clk10MHz blocks (oscillator + $T/4$ delay)只有 clk10MHz 上的方塊(振盪器+$T/4$ 延遲) All cross-clock-domain ratios: demod 6.25 samples/period, PI gain/s, UART framing所有跨時脈域比例:解調每週期 6.25 取樣、PI 每秒增益、UART 訊框 Just a bug就是個 bug
3 — faster clock, same 40 kHz時脈加快、保持 40 kHz Coefficients absorb it: $k_i \propto T^2$ (332→83), poles → $2047/2048$由係數吸收:$k_i \propto T^2$(332→83),極點 → $2047/2048$ Precision: coefficient quantization, rounding, register width精度:係數量化、捨入、暫存器寬度 Works, but oversampling costs precision可行,但過取樣以精度為代價

10.4  Which Clock Sets the Displacement Resolution?哪個時脈決定位移解析度?

The design runs two fast clocks. The ADC, DAC, relay oscillator, Hilbert pair and the Rx Schmitt/debounce chain run at clk10MHz; the tunable injection delay line (delay_line2) and the PWM run at clk50MHz. A natural question is which of the two sets the smallest displacement the radar can resolve, and whether either sets its sensitivity. The short answer: the 10 MHz clock sets the resolution, the 50 MHz clock only sets how finely the loop can respond, and neither sets the sensitivity floor.

這個設計有兩個高速時脈。ADC、DAC、繼電振盪器、Hilbert 對以及 Rx 的施密特/去抖鏈都跑在 clk10MHz;可調注入延遲線(delay_line2)與 PWM 跑在 clk50MHz。自然會問:這兩者中哪一個決定雷達能解析的最小位移?又有沒有哪一個決定 靈敏度?簡答:10 MHz 決定解析度,50 MHz 只決定迴路能多細地 回應,而兩者都不決定靈敏度底限。

10.4.1  Where each clock touches the Rx edge每個時脈在哪裡碰到 Rx 邊緣

Displacement is read out as the PI delay word data, which tracks the arrival phase of the received 40 kHz edge. Follow that edge through _1Mhz_beta010.v: it is timed by three stages before it reaches the 50 MHz delay line, and all three run at 10 MHz.

位移是以 PI 的延遲字 data 讀出,它追蹤接收 40 kHz 邊緣的到達相位。沿著 _1Mhz_beta010.v 追這個邊緣:它在到達 50 MHz 延遲線之前已經過三級定時, 而這三級全都跑在 10 MHz。

  • smittch_trigger (fixed point since 2026-09-04; was smittch_trigger_FP) on clk10MHz1 turns the Rx waveform into a 1-bit edge.smittch_trigger_FPclk10MHz1 上把 ADC 波形變成 1-bit 邊緣(405–417 行)。
  • Rx_Debounce on clk10MHz1 shifts it by whole 10 MHz cycles (lines 406–420).Rx_Debounceclk10MHz1 上把它推移整數個 10 MHz 週期(406–420 行)。
  • uin2/uin3 re-register it on clk10MHz (line 75).uin2/uin3clk10MHz 上再暫存一次(75 行)。
  • delay_line2 on clk50MHz then adds the PI-controlled delay d in 20 ns steps, and its 4-tap smoother turns the edge into a 5-level ramp 80 ns long.接著 delay_line2clk50MHz 上以 20 ns 為步進加上 PI 控制的延遲 d,其 4-tap 平滑器把邊緣變成長 80 ns 的 5 階斜坡。
  • relay_feedback2 samples that ramp at 10 MHz; the sampled level encodes where the edge fell inside the 100 ns bin, so the actuator has 20 ns effective resolution.relay_feedback2 以 10 MHz 取樣這個斜坡;取到的位準包含了邊緣落在 100 ns 格子內的位置,所以致動器具有 20 ns 的有效解析度。
clk10MHz domain · 100 ns bins 10 MHz 時脈域 · 100 ns 格子 clk50MHz · 20 ns clk10MHz 10 MHz ADC 14-bit clk10MHz1 Schmitt smittch_trigger Debounce Rx_Debounce uin2 / uin3 clk10MHz delay_line2 d × 20 ns, 4-tap ramp relay_feedback2 samples the ramp edge time already quantised to 100 ns here 邊緣時間在這裡就已量化到 100 ns adds a 20 ns-step offset only 只能再加上 20 ns 步進的偏移 Rx 40 kHz IF 接收中頻 d ← PI_out (50 kHz) → data (UART)
The Rx edge is quantised to the 10 MHz grid by the Schmitt trigger, debounce and uin2/uin3 registers before the 50 MHz delay line ever sees it. The delay line can only add a finer constant offset; it cannot restore timing information the 10 MHz stages have discarded. Rx 邊緣在 50 MHz 延遲線看到它之前,就已被施密特觸發器、去抖以及 uin2/uin3 暫存器量化到 10 MHz 格點。延遲線只能再加上更細的固定偏移, 無法找回 10 MHz 各級已經丟掉的時序資訊。

10.4.2  From clock bin to phase to displacement從時脈格子到相位、再到位移

The RF carrier is 5.8 GHz, so the speed of light enters through the RF wavelength, not through time of flight of the 40 kHz tone. The Tx path emits an I/Q pair (the Hilbert 90° pair of §4) for single-sideband up-conversion, so the transmitted carrier sits at $f_{RF} + 40$ kHz. After the receiver mixes with the $f_{RF}$ LO, the phase of the 40 kHz IF equals the round-trip RF phase. A clock bin $\Delta t$ on the IF edge therefore maps to a displacement in three steps:

RF 載波是 5.8 GHz,所以光速是透過 RF 波長進來,而不是透過 40 kHz 音的飛行時間。 Tx 路徑送出的 I/Q 對(§4 的 Hilbert 90° 對)用於單邊帶上變頻,因此發射載波位於 $f_{RF} + 40$ kHz。接收端與 $f_{RF}$ 本地振盪器混頻後,40 kHz 中頻的相位就等於 RF 往返相位。IF 邊緣上的一個時脈格子 $\Delta t$ 因此經三步對應到位移:

$$\Delta\varphi_{IF} = 360^\circ \cdot \Delta t \cdot f_{IF}, \qquad \Delta\varphi_{IF} = 360^\circ \cdot f_{RF}\,\frac{2\Delta x}{c} = 360^\circ \cdot \frac{2\Delta x}{\lambda_{RF}}, \qquad \Delta x = \frac{\Delta\varphi_{IF}}{360^\circ}\cdot\frac{\lambda_{RF}}{2}$$ IF edge time → IF phase → RF round-trip displacement中頻邊緣時間 → 中頻相位 → RF 往返位移
$$\lambda_{RF} = \frac{c}{5.8\ \text{GHz}} = 51.7\ \text{mm}, \qquad \frac{\lambda_{RF}}{2} = 25.9\ \text{mm per } 360^\circ \text{ of IF phase}$$ Unambiguous range of the phase readout is one half-wavelength相位讀出的無模糊範圍是半個波長
Clock時脈 Time bin時間格子 IF phase at 40 kHz40 kHz 中頻相位 Displacement at 5.8 GHz (round trip)5.8 GHz 往返位移 What it limits限制什麼
10 MHz 100 ns 1.44° ≈ 103 µm Rx edge timing, ADC/DAC sampling, Hilbert and nv_reg trims, the FP phase_detector counter. Dominant.Rx 邊緣定時、ADC/DAC 取樣、Hilbert 與 nv_reg 微調、浮點 phase_detector 計數器。主導。
50 MHz 20 ns 0.29° ≈ 21 µm (readout LSB only, not realised)(只是讀出 LSB,並未實現) PI actuator step d; dead-zone width of the lock; the LSB of data.PI 致動器步進 d;鎖定的死區寬度;data 的 LSB。
40 kHz envelope ToF40 kHz 包絡飛行時間 100 ns ≈ 15 m Not used. This is why the system relies on carrier phase, not time of flight.未使用。這就是系統依賴載波相位而非飛行時間的原因。

So the finest displacement step the loop can actually respond to is about 103 µm. The 20 ns LSB of data gives a readout granularity of 21 µm, but four out of five of those codes carry no new information from the target.

所以迴路實際能回應的最小位移步進約為 103 µm。data 的 20 ns LSB 讓讀出的粒度看起來是 21 µm,但其中五個碼裡有四個不帶任何來自目標的新資訊。

WHY THE 100 ns BIN IS A DEAD ZONE, NOT NOISE為什麼 100 ns 格子是死區、而不是雜訊

The 40 kHz is exactly $10\ \text{MHz}/250$, and clk10MHz1 is PLL-locked to the same crystal as clk10MHz. A static target's Rx edge therefore always lands at the same position inside its 100 ns bin. The quantisation error is a fixed offset, not a dithered random variable, so the 15.6 Hz low-pass on data cannot average it away. A displacement smaller than one bin produces no change in data unless receiver noise happens to push the edge across a bin boundary.

40 kHz 正好是 $10\ \text{MHz}/250$,而且 clk10MHz1clk10MHz 以 PLL 鎖在同一顆石英上。靜止目標的 Rx 邊緣因此永遠落在其 100 ns 格子內的同一位置。 量化誤差是固定偏移,不是抖動的隨機變數,所以 data 上的 15.6 Hz 低通無法把它平均掉。 小於一個格子的位移不會data 有任何變化,除非接收雜訊剛好把邊緣推過格子邊界。

10.4.3  Resolution is not sensitivity解析度不等於靈敏度

Neither fast clock sets the sensitivity floor, i.e. the smallest phase change that rises above the noise. The oscillator state f1024 is a 24-bit accumulator, so its phase is effectively continuous and not stuck on the 1.44° grid. The detectable change is set instead by the noise budget of the slow signal processing:

兩個高速時脈都不決定靈敏度底限,也就是能高出雜訊的最小相位變化。振盪器狀態 f1024 是 24-bit 累加器,其相位實際上是連續的,不會卡在 1.44° 格點上。 可偵測的變化反而由慢速訊號處理的雜訊預算決定:

Stage Rate / width速率/位寬 Contribution貢獻
Oscillator output u振盪器輸出 u 13-bit Quantisation noise into the demodulator進入解調器的量化雜訊
ADC I/QADC I/Q 14-bit @ 10 MHz Receiver amplitude noise; sets where the Schmitt edge lands接收端振幅雜訊;決定施密特邊緣落點
Demodulator LPFs F1, F2解調器低通 F1、F2 1 MHz, 250 kHz In-loop noise bandwidth迴路內雜訊頻寬
PI updatePI 更新 50 kHz, $k_I = 3/2^8$, $k_P = 2/2^{16}$ Loop bandwidth迴路頻寬
Output LPF on datadata 的輸出低通 15.6 Hz Averages hundreds of 40 kHz edges per output sample; this is what resolves below both clock steps when the input is dithered每個輸出樣本平均數百個 40 kHz 邊緣;當輸入有抖動時,這才是能解析到低於兩種時脈步進的原因
50 MHz crystal50 MHz 石英 All clocks and the 40 kHz itself share its drift and jitter; the long-term limit所有時脈與 40 kHz 本身共享其漂移與抖動;長期極限
KEY RESULT重點結論

Resolution (the smallest step the loop can respond to) is set by the 10 MHz clock: 100 ns, 1.44°, about 103 µm at 5.8 GHz. Actuator fineness is set by the 50 MHz delay line: 20 ns, 0.29°, about 21 µm. Sensitivity is set by word widths and the slow filters, not by either clock. Raising the 50 MHz or 10 MHz clock alone does not improve sensitivity; narrowing the output filter or adding ADC bits does.

解析度(迴路能回應的最小步進)由 10 MHz 時脈決定:100 ns、1.44°、 在 5.8 GHz 約 103 µm。致動器精細度由 50 MHz 延遲線決定: 20 ns、0.29°、約 21 µm。靈敏度由位寬與慢速濾波器決定, 與兩個時脈無關。單獨提高 50 MHz 或 10 MHz 時脈不會改善靈敏度;縮窄輸出濾波器或增加 ADC 位元數才會。

10.4.4  How to move the limit如何推進極限

TWO UPGRADES, IN ORDER OF EFFORT兩個升級,依工作量排序

1. Reach the 21 µm step. Move the Schmitt trigger, debounce and uin2/uin3 registers onto clk50MHz, or feed the comparator output straight into delay_line2. The edge is then quantised at 20 ns, and the existing 4-tap smoother already lets the 10 MHz oscillator resolve that. The debounce count threshold must be rescaled by 5 to keep the same time constant.

1. 達到 21 µm 步進。把施密特觸發器、去抖以及 uin2/uin3 暫存器搬到 clk50MHz,或把比較器輸出直接接進 delay_line2。邊緣就會以 20 ns 量化,而現有的 4-tap 平滑器已經能讓 10 MHz 振盪器解析到這個程度。去抖的計數門檻要乘 5 以維持同樣的時間常數。

2. Go below the clock entirely. Estimate the IF phase from the 14-bit I/Q samples ($\arctan(Q/I)$, or the FP phase_detector replaced by an I/Q product) instead of from edge timing. That estimate is limited by ADC noise and averaging, not by any clock, and it also removes the fixed dead zone described above.

2. 完全擺脫時脈限制。改由 14-bit I/Q 取樣估計中頻相位($\arctan(Q/I)$,或把浮點 phase_detector 換成 I/Q 乘積),而不是靠邊緣定時。這種估計只受 ADC 雜訊與平均量限制, 與任何時脈無關,同時也消除了上述的固定死區。

10.5  Why the PI Delay Line Runs at 50 MHz While the Signal Path Runs at 10 MHz為什麼 PI 延遲線用 50 MHz,而訊號路徑用 10 MHz

A frequent question when reading _1Mhz_beta010.v: the relay oscillator, demodulator, Hilbert pair and both image-reject delay lines run on clk10MHz, yet PI_antiWindup and the delay line inside it take clk50MHz. Is that a workaround for some timing problem, a real advantage, or historical accident? It is deliberate, and the reason is the resolution of the actuator, not a timing fix. Section 10.4 looked at which clock limits the measurement; this section explains why the control side was given the faster clock.

閱讀 _1Mhz_beta010.v 時常見的疑問:繼電振盪器、解調器、Hilbert 對和兩條影像消除延遲線都跑在 clk10MHz,但 PI_antiWindup 和它內部的延遲線卻用 clk50MHz。 這是為了迴避某種時序問題、真的有好處,還是歷史偶然?答案是刻意設計,理由是致動器的解析度, 而不是時序補救。10.4 節討論的是哪個時脈限制了量測;本節說明為什麼控制端拿到了較快的時脈。

10.5.1  The PI controller itself is not a 50 MHz blockPI 控制器本身並不是 50 MHz 的區塊

Look at where clk50MHz is actually consumed. The integrator, the proportional sum and the output low-pass in PI_antiWindup.v are clocked by the 50 kHz taps clk50kHz_d2, clk50kHz_d3 and clk50kHz_d4 (lines 33, 41, 69). The 50 MHz input reaches only one thing: the tunable 1-bit delay line delay_line2 U12 (line 48). In clock_generator.v the 50 MHz has one more job, making those _d2/_d3/_d4 taps (lines 23–40). So the question is really why does the injection delay line run at 50 MHz.

先看 clk50MHz 真正被用在哪裡。PI_antiWindup.v 裡的積分器、比例加總與輸出低通, 都是由 50 kHz 的分接 clk50kHz_d2clk50kHz_d3clk50kHz_d4 打拍(第 33、41、69 行)。50 MHz 只送到一個地方:可調的 1-bit 延遲線 delay_line2 U12 (第 48 行)。在 clock_generator.v 裡 50 MHz 還有一個工作,就是產生那些 _d2/_d3/_d4 分接(第 23–40 行)。所以問題其實是:注入延遲線為什麼跑 50 MHz

10.5.2  The actuator is a time delay, so the clock is its resolution致動器是一段時間延遲,所以時脈就是它的解析度

The PI output is not a voltage or a gain. Its low 17 bits become d, and delay_line2 delays the 1-bit injection signal uin by exactly d clock cycles through a 128K-word RAM circular buffer (write pointer k, read pointer k−d). One clock period is therefore the smallest phase step the loop can command at the 40 kHz carrier:

PI 的輸出不是電壓也不是增益。它的低 17 位元變成 ddelay_line2 透過一個 128K 字的 RAM 環形緩衝(寫指標 k、讀指標 k−d),把 1-bit 注入訊號 uin 精確延遲 d 個時脈週期。因此一個時脈週期就是迴路在 40 kHz 載波上 能下達的最小相位步進:

Delay-line clock延遲線時脈 Step步進 Phase step at 40 kHz40 kHz 下的相位步進 Max delay (17-bit d)最大延遲(17-bit d)
10 MHz100 ns1.44°13.1 ms
50 MHz (as built)(實作)20 ns0.29°2.6 ms (≈105 periods/週期)

At 50 MHz the loop gets five times finer phase control for free: 50 MHz is the board oscillator itself, so it costs no PLL, and a 1-bit RAM at 50 MHz is trivial. The output side gains as well. The four-tap sum over out_d0..out_d3 at 50 MHz turns the hard 1-bit edge into a 5-level ramp (−2…+2) spread over 80 ns — the “smoother transition of input injection” in the source comment. An abrupt injection edge would put a glitch into the relay-feedback oscillator; the ramp does not.

在 50 MHz 下,迴路免費得到五倍細的相位控制:50 MHz 本來就是板上振盪器,不需要 PLL,而 1-bit RAM 跑 50 MHz 毫無壓力。輸出端也受益:在 50 MHz 對 out_d0..out_d3 做四拍加總,把生硬的 1-bit 邊緣變成跨 80 ns 的五階斜坡(−2…+2)——也就是原始碼註解裡的「smoother transition of input injection」。突變的注入邊緣會在繼電回授振盪器裡打出毛刺;斜坡則不會。

10.5.3  The staggered 50 kHz taps: a pipeline built from clock skew錯開的 50 kHz 分接:用時脈偏斜搭出的管線

The second use of 50 MHz is subtler. clk50kHz_d2, _d3 and _d4 are the same 50 kHz clock re-registered on alternating negedge/posedge clk50MHz, so each tap lags the previous one by half a 50 MHz period, 10 ns. The integrator samples on _d2, the PI sum on _d3, the output filter on _d4: every stage clocks after the one before it has settled. It is a hand-built pipeline that uses deliberate clock skew instead of clock-enable logic, and 50 MHz is simply the finest time base available for that stagger.

50 MHz 的第二個用途比較隱晦。clk50kHz_d2_d3_d4 是同一個 50 kHz 時脈,交替在 negedgeposedge clk50MHz 重新打拍,所以每一個分接都比前一個 晚半個 50 MHz 週期,即 10 ns。積分器在 _d2 取樣、PI 加總在 _d3、輸出濾波器在 _d4:每一級都在前一級穩定之後才打拍。這是一條用刻意的時脈偏斜(而非 clock-enable 邏輯)手工搭出的 管線,而 50 MHz 只是能拿來做這種錯開的最細時間基準。

10.5.4  Why everything else stays at 10 MHz, and whether the crossing is safe為什麼其他模組維持 10 MHz,以及跨域是否安全

The rest of the design is the sample-rate domain. The DAC and ADC are clocked at 10 MHz, so the relay oscillator, the demodulator, delay_62d5_10MHz and its hilbert_90 replacement, and the 32-bit delay_line that trims the Tx Q phase (Variable_Delay_Line, line 455) all operate on samples and naturally run at the sample clock. The floating-point arithmetic runs on the 200 MHz PLL clock only so that a multi-cycle IP core finishes inside one 10 MHz sample.

設計的其餘部分屬於取樣率時脈域。DAC 與 ADC 以 10 MHz 打拍,所以繼電振盪器、解調器、 delay_62d5_10MHz 及取代它的 hilbert_90,還有修整 Tx Q 相位的 32-bit delay_lineVariable_Delay_Line,第 455 行),處理的都是取樣,自然跑在取樣時脈。 浮點運算跑在 200 MHz 的 PLL 時脈,只是為了讓多週期的 IP 核能在一個 10 MHz 取樣內完成。

Is mixing the two domains safe? Yes, because clk10MHz is divided from clk50MHz by divide_by_5_narrow in clock_generator.v, so the two clocks are synchronous with a fixed phase relation. The 3-bit u_inj produced on negedge clk50MHz and consumed by relay_feedback2 at 10 MHz is a related-clock multicycle path, not a true asynchronous crossing, and the timing analyzer checks it as such.

兩個時脈域混用安全嗎?安全,因為 clk10MHz 是在 clock_generator.v 裡由 divide_by_5_narrowclk50MHz 除頻而來,兩者同步且相位關係固定。在 negedge clk50MHz 產生、由 10 MHz 的 relay_feedback2 取用的 3-bit u_inj,是相關時脈的多週期路徑,而非真正的非同步跨域,時序分析器也會照此檢查。

KEY RESULT重點結論

50 MHz is used exactly where time resolution is the product: the injection delay line (20 ns = 0.29° steps, plus an 80 ns injection ramp) and the 10 ns stagger of the PI stage clocks. 10 MHz is used where the data is: everything that handles DAC/ADC samples. Neither choice is accidental, and the two domains are synchronous by construction.

50 MHz 只用在「時間解析度就是產品」的地方:注入延遲線(20 ns = 0.29° 步進,加上 80 ns 的注入斜坡)以及 PI 各級時脈 10 ns 的錯開。10 MHz 用在資料所在之處:所有處理 DAC/ADC 取樣的部分。 兩個選擇都不是偶然,而且兩個時脈域在結構上就是同步的。

THE PRICE, AND WHAT A REWRITE WOULD DO代價,以及重寫時會怎麼做

The cost is structural oddity rather than risk: several clocks, some negedge, and phase-skewed copies of one 50 kHz clock that a newcomer must recognise as a pipeline. A modern rewrite would run the whole controller on one clock with clock enables and keep only the 50 MHz delay line, since that is the one place where the extra 5× resolution buys something real.

代價是結構上的怪異而非風險:多個時脈、部分用 negedge、以及同一個 50 kHz 時脈的相位錯開副本, 初學者必須看出那其實是一條管線。若以現代手法重寫,會讓整個控制器跑在單一時脈加 clock-enable 上,只保留 50 MHz 的延遲線——因為那是額外五倍解析度唯一真正換到東西的地方。

11. Verilog Code TraceVerilog 程式碼追蹤 (click to collapse)
Verilog Source Walkthrough · Cyclone V · 5CGXFC5C6F27C7

從 50 MHz 到天線:From 50 MHz to the antenna:
_1Mhz_beta010.v 程式碼追蹤Code Trace

This document traces the Verilog source of the 40 kHz delayed self-injection-locked (DSIL) radar line by line, following the actual signal flow — from clock generation all the way to DAC transmit, ADC receive, image rejection, clutter cancellation, and finally the UART/PWM outputs. Every stage comes with the source excerpt (with line numbers), a trace explanation, and the mapping from hardware to the two reference papers.

本文件依照實際訊號流向逐行追蹤 40 kHz 延遲/自我注入鎖定(DSIL)雷達的 Verilog 原始碼, 從時脈生成一路走到 DAC 發射、ADC 接收、影像消除、雜波消除,直到 UART/PWM 輸出。 每一段都附上原始碼片段(含行號)追蹤說明,並把硬體對應到兩篇參考論文的理論。

Top _1Mhz_beta010.v · 721 lines Clock 50 MHz → 10 M / 2 M / 1 M / 250 k / 50 k / 1.25 k Hz FP IEEE-754 single · 200/400 MHz PLL IF 40 kHz ultrasonic

§0.1如何閱讀本追蹤 · How to read

"Code Trace" is not a file-by-file API document — it follows one signal: you watch data move between registers, IP cores, and clock domains. Each stage uses a fixed three-part layout —

「Code Trace」不是逐檔案的 API 文件,而是跟著一個訊號跑:你會看到資料如何在暫存器、IP 核、時脈域之間流動。 每個階段的版面固定為三件套——

  • Source excerpt: quoted directly from _1Mhz_beta010.v or a submodule; highlighted rows = the key lines being traced, with real line numbers on the left.
    原始碼片段:直接引用 _1Mhz_beta010.v 或子模組,高亮列=當前追蹤的關鍵行,左側為真實行號。
  • Trace explanation (TRACE): what these lines do, where the signal goes, and which clock domain it lives in.
    追蹤說明TRACE):這幾行做了什麼、訊號往哪裡去、時脈域是什麼。
  • Theory link (THEORY): which block/equation of the papers it corresponds to (Adler, Hartley image rejection, phase cancelling…).
    理論連結THEORY):對應到論文中的哪個方塊/公式(Adler、Hartley 影像消除、相位消除…)。
信號路徑 signal
時脈 clock
浮點 / IP 核 FP
控制 / 開關 control
輸出 output
配套文件Companion documents

This trace focuses on "how the code flows." For the theory derivations (Adler equation, IRR, circle-fit ranging) see DSIL_Radar_Study_Guide.html; for module-level specification tables (port lists, coefficients, packet formats) see dsil_studyguide.html. The three documents complement each other.

本追蹤聚焦「程式碼怎麼走」。若需要理論推導(Adler 方程、IRR、圓擬合測距)請看 DSIL_Radar_Study_Guide.html;若需要模組級規格表(埠列、係數、封包格式)請看 dsil_studyguide.html。三份文件互補。

§0.2參考來源 · References

Source來源Type類型How this trace uses it本追蹤如何使用它
pdf/吳易陽_etd-0710123-155935-final.pdfM.S. thesis (NSYSU)碩士論文(中山大學)The parent of the FPGA implementation: Low-IF architecture, I/Q digitization, and how image/clutter cancellation map onto the FPGA. Block naming in this trace follows it.FPGA 實現的母本:低中頻 (Low-IF) 架構、I/Q 數位化、影像消除與雜波消除在 FPGA 上的對應。本追蹤的方塊命名以此為準。
pdf/Highly Linear Phase-Canceling Self-Injection-Locked Ultrasonic Radar…pdfIEEE journal paperIEEE 期刊論文Core theory: how phase-cancelling and delayed self-injection locking (DSIL) linearize the ultrasonic radar's phase response and measure respiration/heartbeat. Maps to the §2 oscillator and §4 injection loop.核心理論:相位消除 (phase-cancelling)延遲+自我注入鎖定 (DSIL) 如何把超音波雷達的相位響應線性化、量測呼吸/心跳。對應 §2 振盪器與 §4 注入迴路。
DSIL_Radar_Study_Guide.htmlTheory guide (existing)理論導讀(既有)Full math derivations of Adler locking, Hartley image rejection, FSK ranging, and circle fitting.Adler 鎖定、Hartley 影像消除、FSK 測距、圓擬合的完整數學推導。
dsil_studyguide.htmlModule guide (existing)模組導讀(既有)Quick reference for each module's port list and numeric constants.各模組埠列與數值常數的速查。
◆ THEORY · 一句話背景One-line background

A DSIL radar injects its own transmitted signal, after a delay, back into the oscillator. The tiny phase change caused by the target (chest displacement) is amplified by the injection-locking loop into a measurable frequency/phase shift; I/Q Low-IF reception plus image rejection then extracts the single sideband, achieving highly linear contactless vital-sign detection. This FPGA is the all-digital realization of that theory.

DSIL 雷達把自身發射訊號經過延遲後注入回振盪器。被測目標(胸腔位移)造成的微小相位變化會被注入鎖定迴路放大成可量測的頻率/相位偏移; 再用 I/Q 低中頻接收 + 影像消除取出單邊帶,達到高線性度的非接觸式生理偵測。本 FPGA 即此理論的全數位實現。

§0.3模組階層 · Instantiation tree

The table below lists the submodules _1Mhz_beta010 instantiates directly (top-level instances), ordered by signal-chain position. Deeper IP cores (MULT/Adder/DIV…) are generated by the Quartus MegaWizard and explained in place at each stage.

下表是 _1Mhz_beta010 直接實例化的子模組(top-level instances),依訊號鏈順序排列。深層的 IP 核(MULT/Adder/DIV…)由 Quartus MegaWizard 產生,於各階段就地說明。

Fixed point since 2026-09-04. The code listings in this trace (with their line numbers) are the original floating-point RTL. On the de25 branch the whole Rx chain is fixed point and the IEEE-754 IP is deleted — see the notes in §5 and §6 and CLAUDE.md for the current structure. The tree and file map below are updated; the per-stage listings are kept as history.

2026-09-04 起改為定點。本追蹤中的程式碼列表(含行號)是原始的浮點 RTL。在 de25 分支上整條 Rx 鏈已為定點,IEEE-754 IP 已刪除——現行結構見 §5§6 的註記與 CLAUDE.md。下方的樹狀圖與檔案對照表已更新;各階段列表保留為歷史。

_1Mhz_beta010.vinstance map
_1Mhz_beta010  (top)
 ├─ clock_generator  U1            // §1 全部衍生時脈
 │    ├─ divide_by_5_narrow / _60duty   // 10M(40%) , 2M(60%)
 │    └─ counters → 1M/250k/50k/10k/2.5k/1.25k/625
 ├─ relay_feedback2  U2           // §2 40 kHz 振盪器,輸出 u, u_0d5, v
 ├─ demodulator  U3               // §3 u·u_delay → 二級 LPF → x256
 │    └─ Data_mult                       // 13×13 乘法器
 ├─ PI_antiWindup  U4             // §4 PI 相位控制 + 注入
 │    └─ delay_line2 → RAM128K           // 可調延遲線 → u_inj
 ├─ PLL200 / PLL400 / clockgen_10MHz×6   // §1b ADC/DAC 時脈相位(PLL 輸出已無使用者)
 ├─ tx_gain + hilbert_90 Tx_IR_hilbert // §6 Tx KEY2 增益 + 90° 正交對(定點)
 ├─ hilbert_90 Rx_IR_hilbert_Q / _I  // §8 Rx Q 通道 90°(y_90)+ I 中點抽頭(y_0),定點
 ├─ Rx_IQ_Sum_B <= I + Q_delay           // §8 15 位元加法 = 影像消除
 ├─ clutter_cancel U_CC              // §9 雜波消除(定點)
 │    ├─ amplitude_calc ×2 + div_fx       // 振幅比 → Clutter_Amp_Ratio (Q8.16)
 │    └─ phase_detector + delay_line      // 相位對齊後相減 = Rx_cc (Q24.8)
 ├─ smittch_trigger ×4 / Rx_Debounce ×4   // §10 取出數位生理信號(SH=7 內含 ×128)
 ├─ peak_extractor ×2                     // §10 振幅峰值
 ├─ CC_Saver                             // §11 自動 cc/ir 路徑切換
 ├─ UART_A  U6_COPY                       // §10 FSK 封包輸出 tx
 └─ PWM  U5                               // §10 PI → PWM

§0.4全系統信號流 · Master signal flow

relay_feedback2 40 kHz · u, v demodulator x256 @250 k PI_antiWindup u_inj, data delay_line2 RAM 可調延遲 u_inj 注入 (DSIL loop) Tx 振幅校正 v, v_62d5 (90°) DAC_DA/DB 14-bit I/Q ≈ 超音波 / 天線 / 目標 ≈ ADC_DA/DB Rx_I_, Rx_Q_ Image Reject I + Q·z⁻⁶²·⁵ = Rx Clutter Cancel Rx·k − Tx_Q_d = Rx_cc peak_extractor Rx_peak_cc/ir UART_A tx · FSK 40-bit PWM pwm pin clock_generator + PLL200/400 所有時脈域的來源 (見 §1)
50 MHz 振盪 u/v 解調 x256 PI u_inj 注入 Tx DAC Rx ADC 影像消除 雜波消除 峰值/UART/PWM

§1主時脈樹 · clock_generator CLOCK

Everything starts from clk50MHz. clock_generator U1 uses no PLL — it slices 50 MHz into the whole clock chain with dividers and counters. Understanding this tree is the prerequisite for every always @(posedge clkXXX) that follows.

一切從 clk50MHz 開始。clock_generator U1 不用 PLL,而是用「除頻器 + 計數器」把 50 MHz 切成整條時脈鏈。 理解這棵樹是看懂後面所有 always @(posedge clkXXX) 的前提。

_1Mhz_beta010.vL24–28
24//---------------- 1. Generate clock signals------------------//
25	wire clk1d25kHz,clk2d5kHz,clk625Hz,clk50kHz_d4,clk50kHz_d3,clk50kHz_d2, clk250kHz_d1, clk1MHz, clk_Mult,clk2MHz, clk10MHz;
26	clock_generator U1(.clk1d25kHz(clk1d25kHz),...,.clk10MHz(clk10MHz),.clk50MHz(clk50MHz));
clock_generator.vL5–52
7	divide_by_5_narrow U1(.Out_narrow(clk10MHz),.Out(mask),.In(clk50MHz)); // 10MHz 40%duty
11	assign clk_Mult=(~mask)&clk50MHz;   // 在 clk10MHz 低準位內塞兩個脈衝 → 解調乘法用
14	divide_by_5_60duty U2(.Out(clk2MHz),.In(clk10MHz));  // 2MHz 60%duty
16	reg [2:0] count;
18	assign clk250kHz = count[2];   assign clk1MHz = count[0];  // 由 2MHz 的 3-bit 計數器分出
26	always@(posedge clk50MHz) clk250kHz_d1 <= clk250kHz; // 對齊 50MHz
30	divide_by_5_60duty U3(.Out(clk50kHz_d1),.In(clk250kHz_d1));
42	divide_by_5_60duty U4(.Out(clk10kHz),.In(clk50kHz_d5));
46	assign clk2d5kHz=count2[1]; clk1d25kHz=count2[2]; clk625Hz=count2[3]; // 由 10kHz 的 4-bit 計數器
▸ TRACE · 時脈鏈Clock chain

50M ÷5 = 10M (divide_by_5_narrow, 40% duty, also emitting mask) → 10M ÷5 = 2M → a 3-bit counter yields 1M (bit0) and 250k (bit2) → 250k is re-aligned as 250k_d1÷5 = 50k, then the _d2/_d3/_d4/_d5 edge-aligned delayed copies → 50k_d5 ÷5 = 10k → a 4-bit counter yields 2.5k / 1.25k / 625 Hz. Each _dN suffix is a same-frequency, one-edge-shifted copy used to establish explicit causal ordering between always blocks.

50M ÷5 = 10Mdivide_by_5_narrow,40% duty,並吐出 mask)→ 10M ÷5 = 2M → 3-bit 計數器分出 1M(bit0) 與 250k(bit2) → 250k 對齊成 250k_d1÷5 = 50k,再衍生 _d2/_d3/_d4/_d5(不同緣對齊的延遲版)→ 50k_d5 ÷5 = 10k → 4-bit 計數器分出 2.5k / 1.25k / 625 Hz。 每個 _dN 後綴就是「同一頻率、相位差一個邊緣」的副本,用來在不同 always 區塊間建立明確的因果順序。

為什麼需要Why we need clk_Mult

Demodulation must compute u(k)·u(k−60). The Data_mult multiplier needs extra pulses squeezed into the low half of clk10MHz to finish its shift-add; clk_Mult=(~mask)&clk50MHz (L11) exists precisely for that.

解調要算 u(k)·u(k−60)。乘法器 Data_mult 需要在 clk10MHz 為低的空檔多塞脈衝完成移位加總,clk_Mult=(~mask)&clk50MHz(L11)正是為此產生。

§1b 浮點與收發專用時脈FP and ADC/DAC clocks PLL

_1Mhz_beta010.vL155–168
160	PLL200 PLL200MHz (clk50MHz2, 1'b0, clk_200);   // 200MHz:FP IP 核時脈
161	PLL400 PLL400MHz (clk50MHz, 1'b0, clk_400);    // 400MHz:FP IP 核時脈
163	clockgen_10MHz ClkGen10MHz1 (clk50MHz, clk10MHz1); // 6 組不同相位的 10MHz
...	// clk10MHz1..6 分別餵給 ADC 取樣、FP 暫存、緣對齊
▸ TRACE

All IEEE-754 IP cores (MULT/Adder/DIV/Convert_*) run at 200/400 MHz, far faster than the data rate (≤10 MHz), so FP results settle well within one 10 MHz sample. clk10MHz1…6 are six slightly phase-shifted 10 MHz copies used to pipeline ADC sampling → FP conversion → registering cleanly and avoid metastability.

所有 IEEE-754 IP 核(MULT/Adder/DIV/Convert_*)跑在 200/400 MHz,遠快於資料率(≤10 MHz),所以一個 10 MHz 取樣週期內 FP 運算早已收斂。 clk10MHz1…6 是六個相位略異的 10 MHz,用來把「ADC 取樣 → FP 轉換 → 暫存」串成乾淨的管線、避免亞穩態。

§2繼電回授振盪器 · relay_feedback2 SIGNAL

This is the "heart" of the whole DSIL loop: a digital 40 kHz resonant oscillator running at 10 MHz, with a 3-bit injection port uinj.

這是整個 DSIL 迴路的「心臟」:一個跑在 10 MHz 的數位40 kHz 共振振盪器,並開了一個 3-bit 注入埠 uinj

_1Mhz_beta010.vL30–35
32	wire signed [12:0] u_0d5, u;  // 振盪訊號 u 及其延遲半週期版 u_0d5
34	relay_feedback2 U2(.u_0d5(u_0d5),.u(u),.uinj(u_inj),.clk10MHz(clk10MHz),.v(v));
relay_feedback2.vL12–52
12	assign u=f1024[23:11];   // 共振器狀態 f1024 取高位 = 振盪輸出 u
15	assign v = f1024[23:10]; // v = 14-bit 版,送往 Tx DAC
22	wire signed [17:0] e;
23	assign e=res_in-g1024[27:10];  // 誤差 = 參考 − 積分器輸出
29	f1024 <= f1024-{...,f1024[23:10]}+{...e}+{...e_1,1'b0}+{...e_2}; // F(z)=(z^-0.5+z^-1.5)/(1024-1023z^-1)
36	assign y_in={{10{uinj[2]}},uinj[1:0],6'b0}; // ★ 注入訊號 uinj 進入共振器
46	assign temp={{...f1024[23:3]}+...}; // 積分器增益 G:306→332/2,決定 40kHz 共振頻率
50	g1024 <= g1024+{{4{temp[23]}},temp};  // G(z)=0.5*332/(1024-1024z^-1)
51	res_in <= {{9{f1024[23]}},1'b1,8'b0}+y_in;  // 把注入 y_in 疊加進共振迴路的輸入
▸ TRACE · 振盪迴路Oscillator loop

State register f1024 (24-bit) and integrator g1024 form a second-order resonator. e (L23) is the "sign-relay reference − integral" error fed back into f1024 (L29) to sustain the constant-amplitude 40 kHz oscillation. The injection port uinj is scaled into y_in at L36 and summed into res_in at L51 — this is the physical entry point of injection locking. Outputs u (13-bit, to the demodulator) and v (14-bit, to Tx) are both slices of f1024.

狀態暫存器 f1024(24-bit)與積分器 g1024 形成二階共振器。e(L23)是「sign-relay 參考 − 積分」的誤差,回授進 f1024(L29)維持 40 kHz 等幅振盪。 注入埠 uinj 在 L36 被縮放成 y_in,於 L51 疊加進 res_in——這就是注入鎖定的物理入口。輸出 u(13-bit,給解調)與 v(14-bit,給 Tx)皆由 f1024 切片而來。

◆ THEORY · Adler / 自我注入鎖定self-injection locking

Adding uinj into the oscillator input is exactly the papers' injection locking: when the injected frequency is near the free-running frequency, the oscillator phase is pulled toward the injection phase, with the locking range described by the Adler equation. In DSIL the injection source is the radar's own transmission, reflected and delayed by the target, so tiny target displacement (breathing/heartbeat) → reflected-phase change → amplified by injection into a measurable phase shift. See DSIL guide §2-1-1 and the IEEE paper's phase-cancelling analysis.

uinj 寫進振盪器輸入,等價於論文中的注入鎖定 (injection locking):當注入訊號頻率接近自由振盪頻率時,振盪器相位被「拉」向注入相位,鎖定範圍由 Adler 方程描述。 在 DSIL 中,注入來源是自身發射經目標反射後的延遲訊號,因此目標的微小位移(呼吸/心跳)→ 反射相位變化 → 經注入放大成可量測的相位偏移。詳見 DSIL 導讀 §2-1-1 與 IEEE 論文的相位消除分析。

§3解調 · demodulator SIGNAL

Demodulates the 40 kHz u into the baseband phase signal x256. The method: "multiply by a delayed copy of itself (phase detection) + two-stage lowpass."

把 40 kHz 的 u 解調成基頻相位訊號 x256。手法是「自乘延遲版(鑑相)+ 兩級低通」。

_1Mhz_beta010.vL44–50
44	reg signed [12:0] u0,u1,...,u12;
45	always@(negedge clk2MHz) begin  // 把 u 延遲 12.5 個 2MHz 週期 ≈ 90°@40kHz
46	   u0<= u_0d5; u1<=u0; ... u12<=u11;
47	end
49	demodulator U3(.x256(x256),.u(u),.u_delay(u12),.clk250kHz_d1(...),.clk1MHz(...),.clk_Mult(...));
demodulator.vL10–36
12	Data_mult U1(.result(v), .data1(u_delay), .data2(u), .clk(clk_Mult)); // v=u(k)·u(k-60) 鑑相
16	assign w218=w256-{...w256[26:3]}-...;  // LPF F1 係數
19	always@(negedge clk1MHz) w256 <= w256+w218-...+{...v}+...; // F1 @1MHz
32	always@(posedge clk250kHz_d1) x256 <= x256+x187-...+{...w}+...; // F2 @250kHz → x256
▸ TRACE · u → x256

L44–47 delay u_0d5 by 12.5 cycles of 2 MHz through 13 registers, producing the quadrature copy u12. Data_mult computes v = u·u_delay (the phase-detecting multiply, on clk_Mult). Two IIR lowpass filters with negative taps — F1@1 MHz and F2@250 kHz — remove the 2× component, leaving x256 (22-bit, 250 kHz), proportional to instantaneous phase. This is the quantity the loop locks.

L44–47 用 13 級暫存器把 u_0d5 延遲 12.5 個 2 MHz 週期,得到正交副本 u12Data_mult 算出 v = u·u_delay(鑑相乘法,跑在 clk_Mult)。兩個含負係數的 IIR 低通——F1@1 MHz、F2@250 kHz——濾掉 2× 倍頻分量,留下正比於瞬時相位的 x256(22-bit, 250 kHz)。這就是迴路要鎖定的受控量。

◆ THEORY · 鑑相器Phase detector

"Signal × its own 90°-delayed copy + lowpass" is classic digital phase detection: the DC term ∝ sin(Δφ). In DSIL this Δφ carries the target displacement; x256 then feeds the PI to form the phase-locked loop, holding the operating point near the paper's high-sensitivity null-point.

「訊號 × 自身 90° 延遲版 + 低通」是經典的數位鑑相 (phase detection):輸出直流項 ∝ sin(Δφ)。 在 DSIL 中,這個 Δφ 攜帶目標位移資訊;x256 之後送進 PI 形成相位鎖定迴路,使工作點維持在論文所述的高靈敏 null-point 附近。

§4PI 相位控制+注入產生 · PI_antiWindup CONTROL

x256 minus the set-point gives the error; after PI integration it (a) drives the tunable delay line that generates the injection u_inj, and (b) is lowpassed into data for UART output.

x256 與設定點相減得誤差,PI 積分後一方面驅動 可調延遲線 產生注入 u_inj,一方面低通成 data 供 UART 輸出。

_1Mhz_beta010.vL80–91
81	always@(posedge clk250kHz_d1) case(shift_bit)
83	   0: err <= {x256[21],x256}-{{4{r[9]}},r,9'b0}; // 誤差=x256 − 設定點(r 經 shift_bit 縮放)
90	PI_antiWindup U4(.uin(uin3),.u_inj(u_inj),.data(data),.pwm_in(pwm_in),.shift_bit(shift_bit),.x256(x256),...,.rst(reset),...);
PI_antiWindup.vL30–82
31	assign int_temp=int256+{{16{x256[22]}},x256[22:7]}+{...x256[22:8]}; // kI=3/2^8
34	always@(posedge clk50kHz_d2) if(rst==0) int256<=0; else int256<=int_temp; // 積分(含 reset)
41	assign PI_temp=int256[31:8]+{{16{x256[22]}},x256[22:15]}; // kp=2/2^16, PI=積分+比例
49	assign d=PI_out[16:0];  // PI 輸出 → 延遲量 d
50	delay_line2 U12(.in(uin), .out(u_inj), .d(d), .clk(clk50MHz)); // ★ PI 控制注入延遲 → u_inj
76	assign xf_temp=xf-{...xf[33:9]}+{...PI_out}+{...PI_out_1}; // 15.6Hz 低通
82	assign data=xf[33:10];  // 平滑後輸出 → UART
▸ TRACE · 控制迴路的閉合Closing the control loop

The error is computed at top-level L81–87 (x256 − set-point, the set-point scaled from r via shift_bit). The PI integrates at 50 kHz (L34, kI=3/2⁸) and adds the proportional term (L41, kp=2/2¹⁶) to get PI_out. The key is L49–50: PI_out is used directly as the delay amount d for delay_line2; the delayed uin becomes the 3-bit injection u_inj returned to the oscillator (§2). Meanwhile PI_out goes through the 15.6 Hz lowpass (L76) to become data for the UART.

誤差由 top 的 L81–87 算(x256 − 設定點,設定點由 rshift_bit 左移縮放)。 PI 在 50 kHz 積分(L34, kI=3/2⁸)並加比例項(L41, kp=2/2¹⁶)得 PI_out。關鍵在 L49–50PI_out 直接當成延遲量 d 餵給 delay_line2, 延遲後的 uin 變成 3-bit 注入 u_inj 回送振盪器(§2)。同時 PI_out 經 15.6 Hz 低通(L76)成 data 走 UART。

delay_line2 — RAM 環形緩衝可調延遲RAM circular-buffer tunable delay

delay_line2.vL11–37
12	always@(negedge clk) k<=k+1;  // 寫指標
14	assign k_d=k-d;             // 讀指標 = 寫指標 − 延遲量 d
16	RAM128K RAM_2_port(.clock(clk),.data(in),.rdaddress(k_d),.wraddress(k),.wren(1'b1),.q(out_temp));
27	case(sum) ... // 4-tap 平滑:讓注入準位轉換更平順 (-2..+2)
◆ THEORY · 延遲+自我注入鎖定Delay + self-injection locking (DSIL)

The "D" of DSIL is here: a 128K RAM ring buffer where read/write pointer difference d realizes an arbitrary tunable time delay, with d controlled live by the PI. This is the digital counterpart of the paper's DSIL architecture — the delay sets the injection phase and the PI loop automatically holds the optimum operating point for "highly linear phase cancellation." See the delay_line2 section of the module guide.

「D」就在這裡:用一塊 128K RAM 當環形緩衝,讀寫指標差 d 即可實現任意可調的時間延遲,而 d 由 PI 即時控制。 這正是論文 DSIL 架構的數位對應——延遲量決定注入相位,PI 迴路自動把系統維持在最佳工作點,達到「高線性相位消除」。詳見 模組導讀 的 delay_line2 章節。

§5注入來源多工 · uin3 CONTROL

The injection-locking "source" is selected by two panel switches: self-feedback (board verification) or the receive-path signal (real measurement).

注入鎖定的「來源」可由兩個面板開關切換:自迴授(板上驗證)或接收端訊號(真實量測)。

_1Mhz_beta010.vL95–102
95	reg uin2, uin3;
96	always@(posedge clk10MHz) begin
99	  uin2 <=(Injection_Method_Switch)?((ClutterCancel_OR_ImageReject_Switch)?uin:u_out)
99	                                  :((ClutterCancel_OR_ImageReject_Switch)?Rx_ir_Amped_DB:Rx_cc_Amped_DB);
101	  uin3 <=uin2;  // 兩級暫存 → 餵 PI.uin
▸ TRACE · 真值表Truth table
Injection_MethodCC_OR_IRuin3 source注入來源 uin3Meaning意義
11uinExternal pin injection (debug)外部腳位注入(除錯)
10u_outSelf-feedback (self-oscillation check)自迴授(自我振盪驗證)
01Rx_ir_Amped_DBInject the image-rejected receive signal影像消除後的接收訊號注入
00Rx_cc_Amped_DBInject the clutter-cancelled receive signal雜波消除後的接收訊號注入

Real measurements use the bottom two rows: the receive signal (processed by §8/§9, digitized by Schmitt + debounce) is injected back into the oscillator, closing the DSIL loop.

真實量測時走下兩列:接收訊號(§8/§9 處理後、經 Schmitt+debounce 數位化)回注振盪器,閉合 DSIL 迴路。

§6發射端 · Tx → DAC OUTPUT

The oscillator output v must become an I/Q pair (90° apart) to drive quadrature transmit. The Q path gets its 62.5-cycle 10 MHz delay, plus floating-point amplitude correction for channel imbalance.

振盪器輸出 v 要做成 I/Q 兩路(相差 90°)才能驅動正交發射。Q 路用 62.5 個 10 MHz 週期延遲取得,並做浮點振幅校正修正通道不平衡。

_1Mhz_beta010.vL182–206
186	delay_62d5_10MHz Tx_IR_delay (clk10MHz, v_62d5, v); // v 延遲 62.5 週期 = 90°@40kHz
188	assign v_Tx     = {~v[13], v[12:0]};     // 有號→偏移二進位 (DAC 格式)
189	assign v_62d5_Tx= {~v_62d5[13], v_62d5[12:0]};
195	assign DAC_WRT_A=clk10MHz; DAC_CLK_A=clk10MHz; ...   // DAC 時序腳
205	assign DAC_DA = nv_62d5_Tx;  // I 通道 (校正後 Q-delay, IRR Fix)
206	assign DAC_DB = nv_reg_d3;    // Q 通道 (對齊延遲後的 v)
_1Mhz_beta010.v · Tx 振幅校正 (FP)L220–234
220	Convert_b2FP Converter_Tx (.clock(clk_200),.dataa(v_62d5),.result(v_62d5_FP));
225	MULT Multiplier_Tx (.clock(~clk_200),.dataa(v_62d5_FP),.datab(32'h3f82d0e5),.result(v_62d5_Amped_FP)); // ×1.022
231	Convert_FP2b Converter_Tx_b (.clock(clk_200),.dataa(v_62d5_Amped_FP),.result(nv_62d5));
▸ TRACE · v → DAC

delay_62d5_10MHz (L186) delays v by 62.5 cycles for the quadrature v_62d5. To fix DAC channel amplitude imbalance, the Q-delay path is converted to FP → multiplied by 1.022 (32'h3f82d0e5) → converted back to integer nv_62d5 (L220–231). Finally L205–206 put corrected I and latency-aligned Q onto DAC_DA/DB. Note {~v[13],v[12:0]} is the standard signed → offset-binary DAC conversion.

delay_62d5_10MHz(L186)把 v 延遲 62.5 週期得正交 v_62d5。 為修正兩個 DAC 通道的幅度不平衡,Q-delay 路徑被轉成浮點 → 乘 1.022(32'h3f82d0e5) → 轉回整數 nv_62d5(L220–231)。 最後 L205–206 把校正後的 I 與對齊延遲的 Q 送上 DAC_DA/DB。注意 {~v[13],v[12:0]} 是「有號→偏移二進位」的標準 DAC 格式轉換。

◆ THEORY · 為何 62.5 週期 = 90°Why 62.5 cycles = 90°

One 40 kHz period = 25 µs; one 10 MHz cycle = 0.1 µs. 90° = ¼ period = 6.25 µs = 62.5 cycles of 10 MHz. The half-integer delay is realized inside delay_62d5 by averaging adjacent samples (×0.5) — see the FP version in §8. I/Q quadrature is the prerequisite of Hartley image rejection.

40 kHz 一個週期 = 25 µs;10 MHz 一個週期 = 0.1 µs。90° = ¼ 週期 = 6.25 µs = 62.5 個 10 MHz 週期。 半整數延遲由 delay_62d5 內部「相鄰兩拍平均 (×0.5)」實現(見 §8 的 FP 版)。I/Q 正交是 Hartley 影像消除的前提。

§7接收端 · Rx → ADC SIGNAL

Dual ADCs sample I and Q simultaneously; both are converted to signed, then to floating point, with a 1.03× channel-balance correction on the Q channel.

雙 ADC 同時取 I/Q 兩路,轉成有號數後進浮點域,並對 Q 通道做 1.03 倍的通道平衡校正。

_1Mhz_beta010.vL313–344
313	assign ADC_CLK_A=clk10MHz1; ADC_CLK_B=clk10MHz1; ADC_OEB_*=0;
321	assign Rx_Q_ = {~ADC_DA[13], ADC_DA[12:0]};  // 偏移二進位→有號, Q 通道
322	assign Rx_I_ = {~ADC_DB[13], ADC_DB[12:0]};  // I 通道
330	Convert_b2FP Converter_Rx_Q_ADC (.clock(clk_200),.dataa(Rx_Q_),.result(Rx_Q__FP));
335	Convert_b2FP Converter_Rx_I_ADC (.clock(clk_200),.dataa(Rx_I_),.result(Rx_I__FP));
340	MULT Multiplier_Rx_ADC (.clock(~clk_200),.dataa(Rx_Q__FP),.datab(32'h3f83d70a),.result(Rx_Q__Amped_FP)); // Q ×1.03 平衡
326	always@(posedge clk10MHz6) begin Rx_I<=Rx_I__FP; Rx_Q<=Rx_Q__Amped_FP; end
▸ TRACE · ADC → Rx_I / Rx_Q (FP)

ADC_DA→Q, ADC_DB→I (note the crossover, L321–322), with the same offset-binary inversion. Both are converted to FP immediately (L330/335); the Q path is multiplied by 1.03 (32'h3f83d70a) to correct receive I/Q gain imbalance (L340), and finally latched into the Rx_I/Rx_Q FP registers on clk10MHz6, handing off to §8.

ADC_DA→QADC_DB→I(注意交叉,L321–322),同樣做偏移二進位反轉。兩路立刻轉浮點(L330/335), Q 路再乘 1.03(32'h3f83d70a) 校正接收端 I/Q 增益不平衡(L340),最後在 clk10MHz6 鎖進 Rx_I/Rx_Q 浮點暫存器,交棒給 §8。

§8影像消除 · Image Reject CLOCK→FP

A Low-IF receiver picks up both the "target" and its "image." Hartley's method: delay Q by 90° and add it to I — the image cancels, the target reinforces.

低中頻接收會同時收到「目標」與「鏡像」訊號。Hartley 法:把 Q 路延遲 90° 後與 I 路相加,鏡像相消、目標相長。

_1Mhz_beta010.vL362–401
362	delay_62d5_10MHz_FP Rx_IR_delay_FP (clk10MHz4, clk_200, Rx_Q_delay, Rx_Q); // Q 延遲 90°
385	Adder IR_Adder_FP (.clock(clk_400),.dataa(Rx_I_ctrl),.datab(Rx_Q_delay_ctrl),.result(Rx_I_ctrl_and_Rx_Q_delay_ctrl_sum_FP));
393	always@(posedge clk10MHz6) begin
395	   Rx_I_ctrl <= Rx_I_d2;        // I 對齊延遲 (補償 Q 路 latency)
396	   Rx_Q_delay_ctrl <= Rx_Q_delay;
400	   Rx <= Rx_I_ctrl_and_Rx_Q_delay_ctrl_sum_FP;  // ★ Rx = I + Q_delay = 影像已消除
delay_62d5_10MHz_FP.v · 半整數延遲L19–34, 96
19	Adder U1 (.clock(clk200MHz),.dataa(data_in),.datab(data_past),.result(data_temp)); // 相鄰兩拍相加
25	MULT U2 (.clock(~clk200MHz),.dataa(data_temp),.datab(32'h3f000000),.result(data_0d5)); // ×0.5 = 半週期內插
34	always@(negedge clk10MHz) begin d0<=data_0d5; d1<=d0; ... d61<=d60; data_out<=d61; end // 62 級 + 0.5 = 62.5
▸ TRACE · 90° 延遲怎麼做出半週期How the 90° delay makes a half cycle

Integer part: a 62-stage register shift (d0…d61→data_out). Half-cycle part: L19/25 add "current + previous" then ×0.5 (32'h3f000000) — linear interpolation of the midpoint — together exactly 62.5 samples. The delayed Rx_Q_delay and aligned Rx_I_ctrl are summed in the 400 MHz Adder (L385), and the result latched into Rx (L400) — the image-rejected single-sideband receive signal.

整數部分:62 級暫存器移位(d0…d61→data_out)。半週期部分:L19/25 把「現值 + 前一拍」相加再 ×0.5(32'h3f000000)——即線性內插出兩拍中間值,合起來剛好 62.5 拍。 延遲後的 Rx_Q_delay 與對齊後的 Rx_I_ctrl 在 400 MHz 的 Adder 相加(L385),結果鎖進 Rx(L400)——即影像消除後的單邊帶接收訊號。

◆ THEORY · Hartley 影像消除image rejection / IRR

In a Low-IF architecture the wanted signal and its image sit at ±f_IF. Phase-shifting Q by 90° and adding to I makes the image anti-phase (cancels) and the target in-phase (reinforces), raising the image-rejection ratio (IRR). Channel gain imbalance (§7's 1.03, §6's 1.022 trims) caps the IRR — which is why the code spends so much effort on FP amplitude fine-tuning. Theory: DSIL guide §2-1-2 (Hartley / IRR).

低中頻架構中,欲收的訊號與其鏡像落在 ±f_IF。對 Q 路做 90° 相移後與 I 路相加,使鏡像兩路反相相消、目標同相相長,提升影像抑制比 (IRR)。 通道增益不平衡(§7 的 1.03、§6 的 1.022 校正)會限制 IRR 上限,這也是程式中花大量篇幅做浮點振幅微調的原因。理論見 DSIL 導讀 §2-1-2 (Hartley / IRR)

§9雜波消除 · Clutter Cancellation FP

The static background (clutter) = reflections of the transmitted signal. Method: measure the amplitude ratio of Rx to Tx_Q, estimate the phase difference, align and scale Tx_Q, then subtract it from Rx — leaving only the moving target.

靜止背景(雜波)= 發射訊號的反射。做法:量出 Rx 與 Tx_Q 的振幅比、估出相位差,把 Tx_Q 對齊縮放後從 Rx 減掉,只留下動態目標。

_1Mhz_beta010.v · 振幅比與相位L439–446, 534–538
439	amplitude_calculation_FP AmpCal_Rx_FP  (~clk10MHz4, clk_400, 1'b1, Rx_Amp_FP_, Rx);
440	amplitude_calculation_FP AmpCal_Tx_Q_FP(~clk10MHz4, clk_400, 1'b1, Tx_Q_Amp_FP_, Tx_Q);
534	DIV FP_Divider (.clock(~clk_400),.dataa(Tx_Q_Amp_FP),.datab(Rx_Amp_FP),.result(Amp_Multiplier_)); // k = |Tx_Q|/|Rx|
446	always@(negedge CC) phase_diff <= phase_diff_; // 鎖存相位差
_1Mhz_beta010.v · 對齊後相減L571–616
571	MULT Multiplier (.clock(~clk_400),.dataa(Rx_FP),.datab(Amp_Multiplier),.result(Rx_Amped_FP)); // Rx × k
609	phase_detector Phase_Detector (~clk10MHz2, 1'b1, phase_diff_, Tx_Q_FP, Rx_FP);
610	delay_line Variable_Delay_Line (clk10MHz2, 1'b1, Tx_Q_Delay, Tx_Q_FP, phase_diff); // Tx_Q 對齊到 Rx 相位
612	Subtractor Subtract_Two_Signal_For_ClutterCancel (.clock(clk_400),.dataa(Rx_Amped_FP),.datab(Tx_Q_Delay),.result(Rx_cc)); // ★ Rx_cc = Rx·k − Tx_Q_delay
▸ TRACE · Rx → Rx_cc

Four steps: ① amplitude_calculation_FP computes |Rx| and |Tx_Q| (L439/440); ② DIV yields the ratio k=|Tx_Q|/|Rx| (L534); ③ phase_detector counts the zero-crossing offset phase_diff between Tx_Q and Rx (L609) and delay_line delays Tx_Q into phase (L610); ④ the Subtractor computes Rx_cc = Rx·k − Tx_Q_delay (L612). The background clutter (= reflected transmission) is precisely cancelled and the moving target emerges.

四步驟:① amplitude_calculation_FP 各算 |Rx|、|Tx_Q|(L439/440);② DIV 得振幅比 k=|Tx_Q|/|Rx|(L534); ③ phase_detector 數出 Tx_Q 與 Rx 過零點之間的計數差 phase_diff(L609),用 delay_line 把 Tx_Q 延到同相(L610); ④ SubtractorRx_cc = Rx·k − Tx_Q_delay(L612)。背景雜波(=發射的反射)被精準抵銷,動態目標浮現。

phase_detector — 過零點計數鑑相zero-crossing counting phase detection

phase_detector.vL26–43
26	if(n2_1&~wave_2[31]) begin phase_diff<=count_n; count<=0; end // wave_2 上升過零 → 鎖存計數
41	count_latch <= (n1_1&~wave_1[31])&(~(n2_1&~wave_2[31])); // wave_1 過零開始計數
已知限制(來自原始碼註解)Known limitations (from source comments)

Top-level comments at L302–303 state plainly: "the clutter-cancel function causes many glitches, and injection can destabilize frequency or amplitude." So §9's subtraction result is used for measurement, while re-injecting it (§5's Rx_cc_Amped_DB path) should be paired with §11's automatic switching for safety. Image rejection (§8) is marked stable and usable.

top 模組 L302–303 註解明言:「Clutter cancel 功能會造成大量 Glitch、注入會導致頻率或振幅不穩定」。 因此 §9 的相減結果用於量測,但回注迴路(§5 的 Rx_cc_Amped_DB 路徑)需搭配 §11 的自動切換以策安全。影像消除(§8)則標註為已穩定可用。

◆ THEORY · 相位消除 (Phase-Cancelling)

This is exactly the "Phase-Cancelling" of the IEEE paper's title: subtract the known transmit component (amplitude and phase aligned) from the receive signal; the residual is the target modulation. Combined with §2's injection locking, the system operates in the most linear part of the phase response, yielding output highly linear in respiration/heartbeat displacement.

這正對應 IEEE 論文標題的 "Phase-Cancelling":把已知的發射分量(振幅、相位都對齊)從接收中減去,殘餘即目標調變。 配合 §2 的注入鎖定,系統工作在相位響應最線性的區段,得到對呼吸/心跳位移高度線性的輸出。

§10輸出:峰值Outputs: Peak / UART / PWM OUTPUT

10a · 數位化+峰值擷取Digitization + peak capture

_1Mhz_beta010.vL637–690
637	smittch_trigger_FP Trig_Rx_cc_Amped (clk10MHz1, Rx_cc_Amped_Trig, Rx_cc_Amped); // FP 史密特觸發 ±200
638	Rx_Debounce DB_Rx_cc_Amped (clk10MHz1, Rx_cc_Amped_DB, Rx_cc_Amped_Trig);      // 去抖 → 乾淨數位
619	Convert_FP2b_16bit Converter_Rx_cc_B (.clock(~clk_400),.dataa(Rx_cc),.result(Rx_cc_B)); // FP→16bit
689	peak_extractor peaker_ir (clk10MHz5, Rx_ir_B_Reg, Rx_peak_ir); // 取每個正半週峰值=振幅
690	peak_extractor peaker_cc (clk10MHz5, Rx_cc_B_Reg, Rx_peak_cc);
▸ TRACE

smittch_trigger_FP (thresholds ±200 = 32'h43480000) binarizes the FP waveform and Rx_Debounce cleans it, producing the digital physiological pulse for re-injection (back to §5). In parallel, peak_extractor detects zero crossings with zero_crossing, tracks temp_max each positive half-cycle, and outputs the amplitudes Rx_peak_cc/ir for the UART.

smittch_trigger_FP(門檻 ±200 = 32'h43480000)把浮點波形二值化、Rx_Debounce 去抖,得到供回注用的數位生理脈衝(接回 §5)。 平行地,peak_extractor 在每個正半週用 zero_crossing 偵測過零、追蹤 temp_max,輸出振幅 Rx_peak_cc/ir 走 UART。

10b · UART FSK 封包UART FSK packets

_1Mhz_beta010.vL135–146
136	data_all <= {data, 8'b0};   // PI 平滑輸出
137	amplitude_info <= (amplitude_select_cc_ir)?{Rx_peak_ir,16'b0}:{Rx_peak_cc,16'b0};
144	UART_A U6_COPY(.clk_50M(clk50MHz_2),.clk_1250(clk2d5kHz),.data(data_switch),.tx(tx),.clk_625(clk1d25kHz));
146	assign data_switch = (clk1d25kHz)?data_all:amplitude_info; // 兩來源輪替
▸ TRACE · 40-bit FSK frame

Inside UART_A, test/test_A/… prepend an identifier to the 32-bit data, then tx_16bits slices it into 5×8-bit bytes sent by my_uart_tx. clk_625 (=clk1d25kHz) alternates between the two identifiers 0xC5/0xBA (UART_A L41/49, L71-72), forming the FSK marker so the host can tell the sources apart. Packet format details: module guide §7.

UART_A 內部由 test/test_A/... 在 32-bit 資料前綴一個識別碼,再經 tx_16bits 拆成 5×8-bit 由 my_uart_tx 送出。 clk_625(=clk1d25kHz)在兩個頻率識別碼 0xC5/0xBA 間切換(UART_A L41/49, L71-72),形成 FSK 標記;上位機可藉此分辨資料來源。封包格式細節見 模組導讀 §7

10c · PWM

PWM.vL8–18
8	assign diff={1'b0,~in[9],in[8:0]}-sawtooth; // 輸入 vs 鋸齒波比較
15	sawtooth <= sawtooth+1;     // 50MHz 累加產生鋸齒
18	pwm <= diff[10];           // 借位位元 = PWM 輸出
▸ TRACE

PI_antiWindup's 10-bit pwm_in is compared against a 50 MHz sawtooth; the borrow bit diff[10] is the PWM signal whose duty is proportional to the control value, framed by clk50kHz_d2/_d3.

PI_antiWinduppwm_in(10-bit)與 50 MHz 鋸齒波比較,借位位元 diff[10] 即占空比正比於控制量的 PWM 訊號,由 clk50kHz_d2/_d3 框出週期。

§11自動路徑切換 · CC_Saver CONTROL

When clutter is too weak, clutter cancellation fails. CC_Saver counts high/low states over the last 250 samples and automatically decides whether to use the cc or ir path.

雜波太弱時雜波消除會失效。CC_Saver 統計近 250 個週期內的高/低態次數,自動決定要用 cc 還是 ir 路徑。

_1Mhz_beta010.v / CC_Saver.vL669 / L9–34
669	CC_Saver CC_Save (clk10MHz1, Saver_out, Rx_cc_out, Rx_ir_out, route_select, count_full);
9	wire high_agb = (count_h >= 130); high_alb = (count_h <= 110);  // 高態次數判斷
13	output count_full = (total_count >= 250);
34	route_select <= (high_agb | high_alb)?1'b1:1'b0;  // 偏離 50% 占空 → 切換來源
▸ TRACE

Every 250 samples form one window (count_full). If the high count falls outside 110–130 (duty deviating from ~50%, meaning that path's signal quality is good), route_select=1 picks in2 (ir), else in1 (cc). route_select also feeds the §0 LEDs and the §5 injection decision (Clutter_weak comes from FP_Comparator L588 vs 40A00000=5.0).

每 250 個樣本為一窗(count_full)。若高態次數落在 110–130 之外(占空偏離 ~50%,代表該路徑訊號品質好),route_select=1in2(ir),否則選 in1(cc)。 route_select 也回饋給 §0 的 LED 與 §5 的注入決策(Clutter_weakFP_Comparator L588 與 40A00000=5.0 比較得出)。

§A訊號字典 · Signal dictionary

Signal訊號Width / type位寬/型別Clock domain時脈域From → to來源 → 去處
u, u_0d5signed [12:0]10 MHzrelay_feedback2 → demodulator
vsigned [13:0]10 MHzrelay_feedback2 → Tx delay / DAC
x256signed [21:0]250 kHzdemodulator → PI 誤差
u_inj[2:0]50 MHzPI delay_line2 → relay_feedback2.uinj
datasigned [23:0]50 kHzPI 低通 → UART
v_62d5signed [13:0]10 MHzTx 90° 延遲 → 振幅校正 → DAC
Rx_I, Rx_Q[31:0] FP10 MHz / 200 MADC → FP → image reject
Rx[31:0] FP10 MHz / 400 MI+Q_delay (影像消除) → clutter / 輸出
Amp_MultiplierClutter_Amp_Ratio[23:0] UQ8.16 (was [31:0] FP)10 M, KEY3 edge sync'd to clk10MHz4|Tx_Q|/|Rx| → Rx 縮放
Rx_cc[31:0] FP400 MRx·k − Tx_Q_delay → 輸出
Rx_peak_cc/irsigned [15:0]10 MHzpeak_extractor → UART amplitude_info
tx, pwm1-bit50 MHzUART_A / PWM → 接腳
浮點常數速查FP constant quick reference

3f800000=1.0 · 3f82d0e5≈1.022 (Tx) · 3f83d70a≈1.03 (Rx Q) · 3f000000=0.5 (half-cycle interpolation) · 43000000=128 (CC boost) · 43480000=200 (Schmitt threshold) · 40A00000=5.0 (clutter-strength threshold). Full Amp_Ctrl/Amp_Sel LUTs: top L275–509.

3f800000=1.0 · 3f82d0e5≈1.022 (Tx) · 3f83d70a≈1.03 (Rx Q) · 3f000000=0.5 (半週期內插) · 43000000=128 (CC 大增益) · 43480000=200 (史密特門檻) · 40A00000=5.0 (雜波強弱門檻)。 完整 Amp_Ctrl/Amp_Sel 查表見 top L275–509。

§B檔案對照表 · File map

Trace section追蹤章節Main files主要檔案Role角色
§1 Clock§1 時脈clock_generator.v, divide_by_5_narrow/60duty.v, clockgen_10MHz.v, PLL200/400.vClock generation時脈生成
§2 Oscillator§2 振盪器relay_feedback2.v40 kHz resonance + injection40 kHz 共振 + 注入
§3 Demod§3 解調demodulator.v, Data_mult.vPhase detection + 2-stage LPF鑑相 + 二級 LPF
§4 PI / injection§4 PI / 注入PI_antiWindup.v, delay_line2.v, RAM128K.v, scale.vControl + tunable delay控制 + 可調延遲
§6 Txtx_gain.v, hilbert_90.v, peak_display.v, gain_display.v, SevSeg.vQuadrature Tx + gain / balance (fixed point)正交發射 + 增益/平衡(定點)
§7–8 Rx / IRhilbert_90.v ×2 (+ 15-bit adder in _1Mhz_beta010.v); delay_62d5_10MHz.v kept, unusedI/Q receive + image rejection (fixed point)I/Q 接收 + 影像消除(定點)
§9 CCclutter_cancel.v, amplitude_calc.v, div_fx.v, phase_detector.v, delay_line.vClutter cancellation (fixed point; tests tb_clutter_cancel.v, tb_div_fx.v, tb_delay_line.v, tb_amplitude_calc.v)雜波消除(定點;測試 tb_clutter_cancel.vtb_div_fx.vtb_delay_line.vtb_amplitude_calc.v
§10 Outputs§10 輸出peak_detector.v, zero_crossing.v, smittch_trigger.v, Rx_Debounce.v, UART_A.v (+test*.v, my_uart_tx.v, tx_16bits.v, speed_select.v), PWM.vDigitization / serial output / PWM數位化 / 序列輸出 / PWM
§11 Auto switch§11 自動切換CC_Saver.vcc/ir path arbitrationcc/ir 路徑仲裁

Appendix A附錄 A

(click to expand)

Testbench & Simulation — Icarus Verilog + GTKWave測試平台與模擬——Icarus Verilog + GTKWave

Theory ref — Yu & Horng §6 (Design Example & Simulation): Same intent — pre-silicon loop validation. Yu validates the PI / ω_BW design against margin targets (PM ≥ 60°); this testbench validates the 40 kHz oscillator's behavior (period, amplitude) at the RTL level in Icarus Verilog. [Yu guide]
理論對照 — Yu & Horng §6(設計範例與模擬): 意圖相同——上矽前的迴路驗證。Yu 用裕度目標(PM ≥ 60°)驗證 PI/ω_BW 設計;這個測試平台在 Icarus Verilog 的 RTL 層級驗證 40 kHz 振盪器的行為(週期、振幅)。[Yu 導讀]

Purpose用途

tb_relay_feedback2.v is a self-checking testbench that drives relay_feedback2 in a PC-side logic simulator so the 40 kHz oscillation can be observed as a waveform without programming the FPGA.

tb_relay_feedback2.v 是一個自我檢查的測試平台,在 PC 端邏輯模擬器中驅動 relay_feedback2,讓 40 kHz 振盪可以在不燒錄 FPGA 的情況下以波形觀察。

Toolchain工具鏈

Tool工具Role角色Notes備註
iverilogCompiles RTL + testbench into a simulation binary把 RTL+測試平台編譯成模擬二進位檔 Behavioral/RTL simulation only — it does not synthesize for the FPGA (Quartus does that).僅供行為級/RTL 模擬——它為 FPGA 合成(那是 Quartus 的事)。
vvpRuns the compiled simulation執行編譯後的模擬 Produces console output and the .vcd waveform dump.產生主控台輸出與 .vcd 波形轉存。
gtkwaveWaveform viewer波形檢視器 Opens the .vcd and plots signals over time.開啟 .vcd,把訊號隨時間畫出來。
theonekevin.icarusextVS Code "Verilog Testbench Runner"VS Code「Verilog Testbench Runner」擴充 One-click button that chains iverilog → vvp → GTKWave.一鍵按鈕,串起 iverilog → vvp → GTKWave。
Scope limit: Icarus can simulate the hand-written RTL modules (relay_feedback2, PI_antiWindup, UART_A, peak_detector, …) but not the full _1Mhz_beta010 top, because ~22 files instantiate Altera megafunction IP (FP cores, PLLs) that Icarus cannot elaborate without Altera simulation libraries.
範圍限制: Icarus 可以模擬手寫的 RTL 模組relay_feedback2PI_antiWindupUART_Apeak_detector…),但不能模擬完整的 _1Mhz_beta010 頂層,因為約 22 個檔案實例化了 Altera megafunction IP(浮點核、PLL),沒有 Altera 模擬程式庫 Icarus 無法展開。

Testbench Structure測試平台結構

`timescale 1ns/1ps
`include "relay_feedback2.v"

module tb_relay_feedback2;
  reg                clk10MHz = 1'b0;
  reg  signed [2:0]  uinj     = 3'sd0;   // 0 = free oscillation
  wire signed [12:0] u, u_0d5;
  wire signed [13:0] v;

  relay_feedback2 dut (.clk10MHz(clk10MHz), .uinj(uinj),
                       .u(u), .u_0d5(u_0d5), .v(v));

  always #50 clk10MHz = ~clk10MHz;       // 10 MHz: 100 ns period

  // Model FPGA power-up-to-0 (DUT has no reset port)
  initial begin
    dut.f1024 = 0; dut.u_past = 0;
    dut.e_1   = 0; dut.e_2   = 0; dut.res_in = 0;
  end

  initial begin
    $dumpfile("relay_feedback2.vcd");   // name ends .vcd -> GTKWave auto-opens
    $dumpvars(0, tb_relay_feedback2);     // dump TB + DUT internals
  end

  initial begin #1_000_000; $finish; end  // 1 ms = ~40 cycles @ 40 kHz
endmodule

Timescale DirectiveTimescale 指令

The first line, `timescale 1ns/1ps, sets the time units for the whole simulation — everything below depends on it:

第一行 `timescale 1ns/1ps 設定整個模擬的時間單位——下面的一切都依賴它:

Field欄位Value數值Meaning意義
Time unit1 ns What a bare delay means. #5050 ns. (If the unit were 1 ps, the same #50 would be 50 ps.)裸延遲的意義。#5050 ns。(若單位是 1 ps,同樣的 #50 就是 50 ps。)
Time precision1 ps Smallest step the simulator tracks; delays round to this. Time is resolved down to 0.001 ns.模擬器追蹤的最小步階;延遲捨入到此。時間解析到 0.001 ns。

The unit must be ≥ the precision. Only 1, 10, or 100 of s/ms/us/ns/ps/fs are allowed (so 1ns/1ps is valid, 2ns is not). Everything in this design is a multiple of 50 ns, so nothing is ever lost to rounding.

單位必須 ≥ 精度。只允許 s/ms/us/ns/ps/fs110100 倍(所以 1ns/1ps 合法、2ns 不合法)。本設計中一切都是 50 ns 的倍數,所以不會有任何因捨入而損失的精度。

Why it matters here: The clock always #50 clk10MHz = ~clk10MHz; only produces 10 MHz because #50 is interpreted as 50 ns. Omitting `timescale makes delays simulator-dependent and the clock timing wrong. The directive also drives %t formatting (it defaults to the precision unit, ps) — which is why the console period needed $timeformat(-9, 0, " ns", 1) to print in nanoseconds. Because the testbench `includes the DUT after this line, relay_feedback2 inherits the same 1ns/1ps scale.
為什麼這裡重要: 時脈 always #50 clk10MHz = ~clk10MHz; 之所以產生 10 MHz,是因為 #50 被解讀為 50 ns。省略 `timescale 會讓延遲依模擬器而異、時脈時序錯誤。這個指令也決定 %t 的格式(預設用精度單位 ps)——這就是主控台週期需要 $timeformat(-9, 0, " ns", 1) 才能以奈秒列印的原因。因為測試平台在這一行之後`include DUT,relay_feedback2 繼承同樣的 1ns/1ps 刻度。

Clock Generation時脈產生

The single line always #50 clk10MHz = ~clk10MHz; is the clock source. With the `timescale 1ns/1ps directive, #50 waits 50 ns, then toggles the clock. Two toggles = one period = 100 ns:

單獨一行 always #50 clk10MHz = ~clk10MHz; 就是時脈源。在 `timescale 1ns/1ps 指令下,#5050 ns 再翻轉時脈。兩次翻轉=一個週期=100 ns

f = 1 / (100 ns) = 1 / (100×10⁻⁹ s) = 10 MHz   (half-period in ns = 500 / fMHz = 500/10 = 50)

clk10MHz is initialised to 1'b0 at declaration — required, because ~x is still x, so an uninitialised clock would never toggle.

clk10MHz 在宣告時初始化為 1'b0——必要,因為 ~x 對未知值仍是未知,未初始化的時脈永遠不會翻轉。

Critical Gotcha — Reset-less Registers Start as X關鍵陷阱——無重置的暫存器以 X 起始

Why the power-up block exists: relay_feedback2 has no reset port, and its state registers (f1024, res_in, e_1, e_2, u_past) have no initialiser. On a real Cyclone V these power up to 0, but in simulation they start as X (unknown) and the feedback loop stays X forever — the output is a flat undefined trace and never oscillates. Seeding them to 0 at t=0 via the DUT hierarchy (dut.f1024 = 0; …) reproduces FPGA power-up behaviour without modifying the DUT. Only g1024 keeps its own initialiser (the non-zero 0x1000000 seed that kick-starts the oscillation).
為什麼需要上電區塊: relay_feedback2 沒有重置埠,其狀態暫存器(f1024res_ine_1e_2u_past)也沒有初始值。真實的 Cyclone V 上電為 0,但在模擬中它們以 X(未知)起始,回授迴路就永遠停在 X——輸出是一條平坦的未定義波形,永不振盪。在 t=0 經 DUT 階層把它們設為 0(dut.f1024 = 0; …)重現 FPGA 上電行為,而不需修改 DUT。只有 g1024 保留自己的初始值(非零的 0x1000000 種子,用來起振)。

Running It執行方式

Use the toolbar button, NOT the Command Palette: The extension's "Compile and run testbench" command is contributed only to the editor title bar (editor/title) and expects a file-URI argument. Launched from Ctrl+Shift+P it receives no argument and silently throws → the "Icarus Output" channel appears empty. Click the run icon at the top-right of the editor instead.
用工具列按鈕,不要用命令面板: 擴充的「Compile and run testbench」命令只註冊在編輯器標題列(editor/title),並期望一個檔案 URI 引數。從 Ctrl+Shift+P 啟動收不到引數、默默拋出例外 → 「Icarus Output」頻道空白。請改按編輯器右上角的執行圖示。
Step步驟What happens發生什麼
1. Open tb_relay_feedback2.v1. 開啟 tb_relay_feedback2.vLanguage mode must read Verilog (bottom-right status bar) for the toolbar button to appear.語言模式必須顯示 Verilog(右下角狀態列),工具列按鈕才會出現。
2. Click the run button (editor top-right)2. 按執行按鈕(編輯器右上角)Compiles: iverilog -o build/….out tb_relay_feedback2.v, then runs vvp from build/.編譯:iverilog -o build/….out tb_relay_feedback2.v,然後從 build/ 執行 vvp
3. Notification: "Output file was created"3. 通知:「Output file was created」Click Open in GTKWave to view the build/relay_feedback2.vcd.Open in GTKWave 檢視 build/relay_feedback2.vcd

The .vcd and compiled binary land in build/ (the extension runs vvp from there). build/ is gitignored. Manual equivalent: iverilog -o sim.out tb_relay_feedback2.v && vvp sim.out && gtkwave relay_feedback2.vcd.

.vcd 與編譯出的二進位檔落在 build/(擴充從那裡執行 vvp)。build/ 已被 gitignore。手動等效指令:iverilog -o sim.out tb_relay_feedback2.v && vvp sim.out && gtkwave relay_feedback2.vcd

Viewing as an Analog Curve in GTKWave在 GTKWave 中以類比曲線檢視

By default multi-bit buses show as rows of numbers. To plot a signal (v, u) as an analog waveform:

多位元匯流排預設顯示為一排數字。要把訊號(vu)畫成類比波形:

Action (right-click the signal)操作(在訊號上按右鍵)Effect效果
Data Format → Signed DecimalData Format → Signed DecimalInterprets the bus as two's-complement so the curve swings negative.把匯流排解讀為二補數,曲線才能擺到負值。
Data Format → Analog → Interpolated (or Step)Data Format → Analog → Interpolated(或 Step)Renders the bus as a continuous curve instead of numbers.把匯流排畫成連續曲線而非數字。
Insert Analog Height Extension ×3–4Insert Analog Height Extension ×3–4Gives the trace vertical room — without this the curve is squashed flat into one row (the most-missed step).給波形垂直空間——沒有這步,曲線被壓扁在單列裡(最常漏掉的步驟)。
Zoom Fit (Ctrl+Shift+F)Shows the full 1 ms run.顯示完整的 1 ms 執行。

Expected Result預期結果

QuantityValue數值Meaning意義
Oscillation period振盪週期25 µs= 250 clock cycles → 40 kHz, confirming the design target.=250 個時脈週期 → 40 kHz,確認設計目標。
v amplitudev 振幅±~3490Steady-state swing of the 14-bit DAC word after the startup transient.啟動暫態後 14 位元 DAC 字組的穩態擺幅。
Settling穩定時間~100 µsStartup ramp before the amplitude stabilises; 1 ms run captures ~40 full cycles.振幅穩定前的啟動爬升;1 ms 的執行捕捉約 40 個完整週期。
Built-in check: The testbench measures the time between successive zero-crossings of v and prints it to the console — e.g. period=25000 ns (~40000 Hz) — so the 40 kHz result is verified numerically, not only by eye.
內建檢查: 測試平台量測 v 相鄰過零之間的時間並印到主控台——例如 period=25000 ns (~40000 Hz)——所以 40 kHz 的結果有數值驗證,不只是目測。

Client Program (LabVIEW)客戶端程式(LabVIEW)

The PC-side program that receives the radar output over the serial link is a LabVIEW VI. It opens the COM port with VISA Configure Serial Port (230400 baud, 8 data bits, no parity, 1 stop bit — matching the frame the FPGA's my_uart_tx emits), reads the byte stream, and de-frames it into the amplitude and phase values for display and logging.

透過序列連線接收雷達輸出的 PC 端程式是一個 LabVIEW VI。它用 VISA Configure Serial Port 開啟 COM 埠(230400 鮑、8 資料位元、無同位、1 停止位元——與 FPGA 的 my_uart_tx 發出的訊框一致),讀取位元組流,並解訊框成振幅與相位值以供顯示與記錄。

Frame Markers — how the VI de-frames the stream訊框標記——VI 如何解訊框

Because two different quantities share one UART, the FPGA wraps each value in delimiter bytes so the VI can tell them apart (and re-align if a byte is dropped). Each field gets a distinct header (prepended) and trailer (appended):

因為兩種不同的量共用一個 UART,FPGA 用分隔位元組包住每個值,讓 VI 能分辨它們(並在位元組遺失後重新對齊)。每個欄位有獨特的檔頭(前綴)與檔尾(後綴):

Field欄位Header (prepend)檔頭(前綴)Payload酬載Trailer (append)檔尾(後綴)
Amplitude (Rx_peak → amplitude_info)0xEEvalue bytes0xBA
PI / phase (data_all)0xD4value bytes0xC5

So the VI scans for 0xEE … 0xBA to capture an amplitude frame and 0xD4 … 0xC5 for a PI / phase frame. These come straight from the RTL: the amplitude value (Rx_peakamplitude_info) is framed by test_A as {8'hEE, …, 8'hBA}, and the PI/phase value (data_all) by test as {8'hD4, …, 8'hC5} — the prefix is start_iden_code in each test*.v, the trailer is the low byte of .in in UART_A.v. (Selection is by clk1d25kHz: high → PI/data_all/test; low → amplitude/amplitude_info/test_A.)

所以 VI 掃描 0xEE … 0xBA 擷取振幅訊框、0xD4 … 0xC5 擷取 PI/相位訊框。這些直接來自 RTL:振幅值(Rx_peakamplitude_info)由 test_A 包成 {8'hEE, …, 8'hBA},PI/相位值(data_all)由 test 包成 {8'hD4, …, 8'hC5}——前綴是各 test*.v 中的 start_iden_code檔尾UART_A.v.in 的低位元組。(選擇由 clk1d25kHz 決定:高 → PI/data_alltest;低 → 振幅/amplitude_infotest_A。)

Practical tips at 230400 baud: match the port settings exactly, key the reads off the header/trailer markers rather than a fixed byte count, and use a large enough input buffer / timeout so bytes are not dropped mid-stream — dropped bytes are the usual cause of “occasionally shifted values”.

230400 鮑的實務建議:埠設定要完全一致、以檔頭/檔尾標記而非固定位元組數觸發讀取、並使用夠大的輸入緩衝區/逾時,避免位元組在串流中被丟——掉位元組是「偶爾數值錯位」最常見的原因。

Flash Programming & Backup (EPCQ256)快閃記憶體燒錄與備份(EPCQ256)

The design can be loaded into the FPGA two ways: volatile (SRAM configuration via .sof, lost on power-off) or non-volatile (written to the board's serial configuration flash, reloaded automatically on every power-up). This project targets device 5CGXFC5C6F27C7 (Cyclone V GX) with USE_CONFIGURATION_DEVICE ON; the config flash is an EPCQ256 (256 Mbit = 32 MB serial NOR). All commands use Quartus 18.1 ($QBIN = /c/intelFPGA/18.1/quartus/bin64) and the USB-Blaster (-c 1).

設計可以用兩種方式載入 FPGA:揮發性(經 .sof 做 SRAM 組態,斷電即失)或非揮發性(寫入板上的序列組態快閃,每次上電自動重載)。本專案目標元件為 5CGXFC5C6F27C7(Cyclone V GX),USE_CONFIGURATION_DEVICE ON;組態快閃是 EPCQ256(256 Mbit=32 MB 序列 NOR)。所有指令使用 Quartus 18.1($QBIN = /c/intelFPGA/18.1/quartus/bin64)與 USB-Blaster(-c 1)。

Two config paths: Volatile -o "p;_1Mhz_beta010.sof" configures the FPGA SRAM directly — fast, but gone at power-off. Non-volatile programs the EPCQ256, and the FPGA boots from it on power-up (requires the board's MSEL DIP switches set to Active Serial (AS)). A serial flash is indirect: JTAG cannot reach it directly, so the programmer first loads a flash-loader bitstream into the FPGA that bridges JTAG ↔ EPCQ.
兩條組態路徑: 揮發性-o "p;_1Mhz_beta010.sof" 直接組態 FPGA SRAM——快,但斷電就沒了。非揮發性:燒錄 EPCQ256,FPGA 上電時從它開機(需要板上 MSEL DIP 開關設為 Active Serial(AS))。序列快閃是間接的:JTAG 無法直接觸及,所以燒錄器先把一個flash-loader 位元流載入 FPGA,橋接 JTAG ↔ EPCQ。

Detect the chain first先偵測 JTAG 鏈

"$QBIN/jtagconfig.exe" # lists cable + the 5CGXFC5C… FPGA "$QBIN/quartus_pgm.exe" -c 1 -a # lists devices and their chain indices

"No JTAG hardware available" means the USB-Blaster / board is not connected or powered — nothing below (except SOF→JIC conversion) can run until the chain enumerates. The FPGA is normally chain index 1 and the attached EPCQ256 is index 2 (@2); confirm and adjust @N if different.

「No JTAG hardware available」表示 USB-Blaster/板子未連接或未上電——在鏈路列舉成功之前,下面的一切(除了 SOF→JIC 轉換)都無法執行。FPGA 通常是鏈路索引 1,掛載的 EPCQ256 是索引 2(@2);如有不同請確認並調整 @N

Program the flash (write image → EPCQ256)燒錄快閃(寫入映像 → EPCQ256)

Step 1 — convert .sof.jic (no board needed; the .jic embeds the flash-loader):

步驟 1——轉換 .sof.jic(不需要板子;.jic 內嵌 flash-loader):

"$QBIN/quartus_cpf.exe" -c -d EPCQ256 -s 5CGXFC5C6 _1Mhz_beta010.sof _1Mhz_beta010.jic

Equivalently, fix the stale <sof_filename> path in output_file.cof (it points at an old C:/Users/Chromeina/Desktop/… path) and run quartus_cpf -c output_file.cof. The .cof records <eprom_name>EPCQ256</eprom_name>, <flash_loader_device>5CGXFC5C6</flash_loader_device>, <mode>13</mode> (JIC, Active-Serial ×1).

等效做法:修正 output_file.cof 中過期的 <sof_filename> 路徑(它指向舊的 C:/Users/Chromeina/Desktop/…),再執行 quartus_cpf -c output_file.cof.cof 記錄了 <eprom_name>EPCQ256</eprom_name><flash_loader_device>5CGXFC5C6</flash_loader_device><mode>13</mode>(JIC,Active-Serial ×1)。

Step 2 — program the EPCQ over JTAG (board required):

步驟 2——經 JTAG 燒錄 EPCQ(需要板子):

"$QBIN/quartus_pgm.exe" -c 1 -m jtag -o "pvi;_1Mhz_beta010.jic"

Step 3 — set MSEL = AS and power-cycle; the FPGA now boots the design from flash.

步驟 3——設 MSEL=AS重新上電;FPGA 現在從快閃開機執行設計。

Back up the flash (read EPCQ256 → file)備份快閃(讀出 EPCQ256 → 檔案)

To capture whatever is currently on the flash (which may differ from any project .jic), use the programmer's Examine (E) operation. Syntax: -o "E;<output_file>;<device_name>@<index>".

要擷取快閃上目前的內容(可能與任何專案 .jic 不同),使用燒錄器的 ExamineE)操作。語法:-o "E;<output_file>;<device_name>@<index>"

"$QBIN/quartus_pgm.exe" -c 1 -m jtag -o "E;flash_backup.jic;EPCQ256@2"

This loads the flash-loader, reads the EPCQ256 back into flash_backup.jic, which is directly re-programmable later (-o "pvi;flash_backup.jic"). GUI alternative: quartus_pgmw → Auto Detect → right-click the FPGA → Attach Flash Device → EPCQ256 → tick Examine → choose output file → Start.

這會載入 flash-loader,把 EPCQ256 讀回 flash_backup.jic,之後可直接重新燒錄(-o "pvi;flash_backup.jic")。GUI 替代方案:quartus_pgmw → Auto Detect → 在 FPGA 上按右鍵 → Attach Flash Device → EPCQ256 → 勾選 Examine → 選輸出檔 → Start。

Backup caveats: EPCQ256 = 32 MB, so a full examine takes minutes. Verify the result is not blank/all-FF (check file size; optionally re-verify against the device with -o "v;flash_backup.jic;EPCQ256@2"). The existing *_Final.jic files in the project are previously built images, not a read-back of the live flash.
備份注意事項: EPCQ256=32 MB,完整 examine 需要幾分鐘。確認結果不是空白/全 FF(檢查檔案大小;可選擇再與元件驗證:-o "v;flash_backup.jic;EPCQ256@2")。專案中既有的 *_Final.jic 檔是先前建置的映像,不是現場快閃的回讀。

Operation-code cheat sheet (quartus_pgm -o)操作碼速查表(quartus_pgm -o

Code代碼Meaning意義Typical use典型用途
PProgram燒錄write image寫入映像
VVerify驗證compare device vs file比對元件與檔案
BBlank-check空白檢查confirm erased確認已抹除
EExamine (read)Examine(讀出)backup device → file備份元件 → 檔案
IErase (before program)抹除(燒錄前)combine, e.g. PVI = erase+program+verify可組合,例如 PVI=抹除+燒錄+驗證
RReset / examine-blank重置/空白檢視

Appendix B. DE25 HSMC ↔ ADA Pin Map附錄 B · HSMC ↔ ADA 腳位對照

(click to expand)

DE25 HSMC ↔ ADA (HSMC Daughter Card) Pin MapDE25 HSMC ↔ ADA(HSMC 子卡)腳位對照

Mapping rule (pair-wise reverse): ADA pair k (pins 2k-1, 2k) ↔ DE25 pair 81-k (pins 161-2k, 162-2k). Equivalently: DE25_odd = 160 - ADA_odd, DE25_even = 162 - ADA_even.
Anchors: ADA 1,2 ↔ DE25 159,160  |  ADA 159,160 ↔ DE25 1,2.
ADA pins 161–180 are QSH-090 extras (DE3/QB3 only) — not part of HSMC standard, listed at the bottom.
Sources: DE25-Standard.pdf p.28 (HSMC), ada_v11.pdf p.4 (J9 QSH-090).
對應規則(成對反向): ADA 第 k 對(腳位 2k-1, 2k)↔ DE25 第 81-k 對(腳位 161-2k, 162-2k)。等價地:DE25_odd = 160 - ADA_oddDE25_even = 162 - ADA_even
錨點:ADA 1,2 ↔ DE25 159,160 | ADA 159,160 ↔ DE25 1,2。
ADA 腳位 161–180 是 QSH-090 額外腳(僅 DE3/QB3 使用)——不屬於 HSMC 標準,列於表尾。
資料來源:DE25-Standard.pdf 第 28 頁(HSMC)、ada_v11.pdf 第 4 頁(J9 QSH-090)。
FPGA pinFPGA 腳位 DE25 HSMC #DE25 HSMC 編號 DE25 signalDE25 訊號 ADA HSMC #ADA HSMC 編號 ADA signalADA 訊號