教材 Rev. 8
VHDL · デジタル回路講義

4ビット・リップルカウンタ
── 一個 4-bit Ripple Counter 的視覺化研究

「看見每一個 CK 邊緣,怎麼跑進 Process 裡」


個電路會把每個 PulseIn 加 1,從 0000 數到 1111(0~15),再回到 0 ── 用 4 顆「Toggle Flip-Flop」串成的 4-bit 二進位計數器

用法捲到下面的模擬器,按 Sub-step ▷ ── 我們會用動畫把每條線、每個 Process 怎麼被叫起來、Q 怎麼跳,一步一步走給你看。看完幾個循環之後,所有名詞(Generate / Process / Variable / Signal / ripple)就自然有畫面對應了。

4-bit ripple counter 2 entities Generate × 4 Async clear
手順
學習指引 ・ 建議路徑
先捲到下方模擬器 → 按 ⟲ Reset → 按 Sub-step ▷ 看單步,或 ⇥ 下個 PulseIn 看整個 ripple ── 看 3~4 個 cycle 後,再回頭看「程式碼」「Port Map 解碼」「概念」,所有名詞就有畫面對應了。

§ I程式結構 ・ Generate 與訊號連線


Generate 展開:For I in 0 to 3

這條 For I in 0 to 3 Generate 不是執行期迴圈。在 elaboration(精化期)就把 U: DEFF1 Port Map (CL, TMP(I), '1', Q(I), TMP(I+1)) 把 I 代進去 4 次、把實體電路接好。clock 來的時候只是讓「早就接好的線」上面的值跟著動,Port Map 不會「執行」。

I
CL
CK ← TMP(I)
T
Q → Q(I)
Qbar → TMP(I+1)
0
CL
TMP(0) =PulseIn
'1'
Q(0)
TMP(1)
1
CL
TMP(1) =U0.Q̄
'1'
Q(1)
TMP(2)
2
CL
TMP(2) =U1.Q̄
'1'
Q(2)
TMP(3)
3
CL
TMP(3) =U2.Q̄
'1'
Q(3)
TMP(4) 有值未被讀取

Signal TMP(4 downto 0) ・ 5 條訊號連線

這 5 條訊號連線在電路裡的角色:

TMP(0) 輸入時脈,等於 PulseIn ── TMP(0) <= PulseIn;
TMP(1) U0.Qbar,也就是 U1.CK ── 看 Q0 由 1→0 才會有上升邊緣
TMP(2) U1.Qbar = U2.CK
TMP(3) U2.Qbar = U3.CK
TMP(4) 被 U3.Q̄ 驅動(有值,不是 floating),只是沒有下游邏輯讀它。寫成 4 downto 0 是為了讓 Generate 邊界一致。

§ II互動模擬 ・ Signal Level Trace


程式碼


        
      

步進時對應的程式行會被黃色標出 ── 例如 TMP := not TMP 真正執行時。

※ 為閱讀方便,程式碼經過重新縮排與加上行尾註解;與專案內 Counter4B.vhd / DEFF1.vhd 邏輯完全等價,但行號可能略有出入。

電路 ・ 黃色 = 此刻正在動作

現在 尚未開始
提示
Sub-step ▶ 看每個 signal 怎麼變;按 ⇥ 下個 PulseIn 自動播放整個 ripple 鏈
PulseIn =TMP(0) CL (清除) U0 DEFF1 T='1' CK CL Q 0 Variable (FF 內存) Q(0) = 0 TMP(1) 看 Q̄ 上升,非 Q 下降 U1 DEFF1 T='1' CK CL Q 0 Variable (FF 內存) Q(1) = 0 TMP(2) U2 DEFF1 T='1' CK CL Q 0 Variable (FF 內存) Q(2) = 0 TMP(3) U3 DEFF1 T='1' CK CL Q 0 Variable (FF 內存) Q(3) = 0 TMP(4) Variable (內) Signal (外, TMP/Q) T='1' = 永遠 toggle 十進位 = 0
Cycle0 Phaseidle

16-cycle 時序圖 (為教學簡化,未畫 Q0→Q3 的 ns 級 propagation delay)

PulseIn Q(0) Q(1) Q(2) Q(3)
PulseIn Q(0) Q(1) Q(2) Q(3)

Signal-level Process Trace

每個 PulseIn 內逐步顯示:SIG=signal 賦值、PROC=Process 觸發、EDGE=邊緣偵測、WIRE=elaboration 期已連好的線把 signal 帶到下游。

狀態表 ・ Q3 Q2 Q1 Q0 在每個 PulseIn 後的值

CycleQ(3)Q(2)Q(1)Q(0)BinaryDecimal

§ III關鍵概念


前 3 張是必讀(必修),後 5 張是補充(補講)。※ 建議先讀 ❶~❸,熟練後再展開後續

01 必讀

Generate ≠ runtime loop

For I in 0 to 3 Generate編譯期展開。合成出來就是 U0~U3 四顆實體 FF,電路裡沒有「迴圈」這個東西。

02 必讀

Variable vs Signal

同樣叫 TMP 但完全不同。
Variable(DEFF1 內的 Variable TMP):FF 內部 1-bit 記憶,賦值 := 在 Process 內立即生效
Signal(外層 Signal TMP):模組間連線,賦值 <= 會被排程到 δ-cycle,其他 Process 在後續模擬時刻才觀察到變化(不是立即可見)。

03 必讀

為什麼叫 Ripple?

U(I+1).CK 接 U(I).Q̄。Q0 從 1→0 → Q̄0 從 0→1,U1 才看到上升邊緣 → toggle。一級一級「漣波」傳上去 ── 累積延遲。

04

Entity vs Architecture

Entity 是對外的介面(腳位、方向),Architecture 是內部實作。像 C 的 .h.c

05

Component + Port Map

要用 DEFF1,必須先在 Architecture 區內 Component DEFF1...End Component 宣告,再用 Port Map 把實際 signal 接到腳位上。Port Map 在 elaboration 時固定,不是執行期動作。

06

Signal TMP 即訊號連線

外層的 Signal TMP(4 downto 0) 是 5 條互連訊號連線。TMP(4) 雖然被 U3.Q̄ 驅動但沒有下游 ── 是「沒被讀取」不是「floating」。

07

Process 何時被觸發

Sensitivity list (CL, CK):只要 CL 或 CK 「有變化」(0→1 或 1→0) 就執行一次。下降邊緣也會觸發 Process,只是 Rising_Edge 判斷為 false 而已。

08

if / elsif 的順序

if CL='1' 先判斷;elsif Rising_Edge(CK) 後判斷。CL 永遠優先 ── 非同步清除:不需要等 CK 邊緣,只要 CL=1 就立刻把 FF 拉到 0。

📖 進階理論速查 ・ 5 個進階主題(Rising_Edge 真值表 / Q 權重 / T='1' / 初值 'U' / TMP 同名陷阱) 點我展開

§ IV練習問題


先自己想/動筆算,再展開答案核對,最後用上面的模擬器驗證。

Q1

手算前 8 個 PulseIn(互動填答)

起點 Q3Q2Q1Q0 = 0000。請填出第 1~8 個 PulseIn 後的二進位,輸入時會即時 ✓/✗ 對答。

Pulse你的答案 (Q3 Q2 Q1 Q0)十進位狀態
Q2

把 T 改成 '0' 會發生什麼?

如果 Counter4B 裡 Port Map 第 3 個位置從 '1' 改成 '0',Q3Q2Q1Q0 序列會變成怎樣?

解答
DEFF1 內 if T='1' Then TMP := not TMP; Else null;
T='0' 永遠走 Else null → FF 完全不 toggle → Q 維持原值
前提分兩種:① 若已先用 CL=1 reset 到 0000 → Q 永遠停在 0000。② 若 reset 過,Variable TMP 是 'U'(uninitialised),Q 會永遠停在「未知/不可信」狀態,模擬器報 X。 這就是為什麼這顆 DEFF1 比一般 D-FF 多一隻 T 腳:它其實是「可被禁用的 T-FF」,T=0 等同 hold。
Q3

為什麼 Signal 宣告成 TMP(4 downto 0)?多一條?

明明只需要 PulseIn + 3 條中繼線(U0→U1, U1→U2, U2→U3),為什麼宣告了 5 條?

解答
為了讓 Generate 迴圈的邊界統一寫成 TMP(I), TMP(I+1)
I=3 那次需要寫到 TMP(4)(U3.Q̄)。如果只宣告 (3 downto 0),Generate 在 I=3 時會超出 bound 編譯錯誤。
TMP(4) 有值,只是沒有下游 ── 合成工具會把這個未使用 net 標成「dangling output」並 trim 掉。
Q4

CK 來時 CL=1,結果?

假設 Q3Q2Q1Q0 = 0101,現在 CL 被拉高為 1,同時 PulseIn 來了一個上升邊緣。Q 變成多少?

解答
Q3Q2Q1Q0 = 0000
Process 內:if CL='1' Then TMP := '0'; elsif Rising_Edge(CK) Then ... ── CL 優先,不會進到 elsif 的 toggle 分支。
這就是「非同步清除」:即使 CK 真的來了上升邊緣,只要 CL=1 還在,FF 立刻被清為 0,CK 邊緣被忽略。