「看見每一個 CK 邊緣,怎麼跑進 Process 裡」
這個電路會把每個 PulseIn 加 1,從 0000 數到 1111(0~15),再回到 0 ── 用 4 顆「Toggle Flip-Flop」串成的 4-bit 二進位計數器。
用法捲到下面的模擬器,按 Sub-step ▷ ── 我們會用動畫把每條線、每個 Process 怎麼被叫起來、Q 怎麼跳,一步一步走給你看。看完幾個循環之後,所有名詞(Generate / Process / Variable / Signal / ripple)就自然有畫面對應了。
Rising_Edge(CK) 真值表「Rising_Edge」是事件(剛從 0 變 1),不是「現在 CK = 1」。
| CK 變化 | 事件觸發 Process? | Rising_Edge? |
|---|---|---|
| 0 → 1 | ✓ | true |
| 1 → 0 | ✓ | false |
| 1 → 1 | 無事件 | false |
| 0 → 0 | 無事件 | false |
注意第 2 列:下降邊緣有觸發 Process(因為 CK 真的改變了),只是裡面的 Rising_Edge 判斷為 false。
Q(3 downto 0) 是 4 條獨立 wire;它們的權重就是二進位:
Q0 每個 PulseIn 翻一次 → 等同個位數;Q0 翻到 1 再翻到 0 才讓 Q1 翻 → Q1 等於 2 個 PulseIn 一次。一級一級往上,所以能 0,1,2,3,…,15 計數。
T='1' 是什麼?DEFF1 內部:if T='1' Then TMP := not TMP
T = 1 → 每次 CK 上升都翻轉(Toggle Flip-Flop = T-FF)
T = 0 → 不變(由 Else null 處理)
'1',就是要每個時脈都翻一次 ── 這是計數的基礎。嚴謹的 VHDL 模擬:Variable TMP : std_logic; 沒寫初值 → 預設是 'U'(uninitialised)。
真實硬體必須先用 CL='1' 把 4 顆 FF 全部清為 0,才有確定的起點。本頁為了好理解直接顯示 0000,但實務上「沒 reset 過的 counter 算出來的值不能信」。
兩段程式裡都有叫 TMP 的東西,但意義完全不同:
Signal TMP(4 downto 0)Variable TMP電路圖內,FF 方框內的小框是 Variable;方框外的橫線標籤 TMP(0..4) 才是 Signal。
這條 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 不會「執行」。
這 5 條訊號連線在電路裡的角色:
PulseIn ── TMP(0) <= PulseIn;
步進時對應的程式行會被黃色標出 ── 例如 TMP := not TMP 真正執行時。
※ 為閱讀方便,程式碼經過重新縮排與加上行尾註解;與專案內 Counter4B.vhd / DEFF1.vhd 邏輯完全等價,但行號可能略有出入。
每個 PulseIn 內逐步顯示:SIG=signal 賦值、PROC=Process 觸發、EDGE=邊緣偵測、WIRE=elaboration 期已連好的線把 signal 帶到下游。
| Cycle | Q(3) | Q(2) | Q(1) | Q(0) | Binary | Decimal |
|---|
前 3 張是必讀(必修),後 5 張是補充(補講)。※ 建議先讀 ❶~❸,熟練後再展開後續
For I in 0 to 3 Generate 是編譯期展開。合成出來就是 U0~U3 四顆實體 FF,電路裡沒有「迴圈」這個東西。
同樣叫 TMP 但完全不同。
Variable(DEFF1 內的 Variable TMP):FF 內部 1-bit 記憶,賦值 := 在 Process 內立即生效。
Signal(外層 Signal TMP):模組間連線,賦值 <= 會被排程到 δ-cycle,其他 Process 在後續模擬時刻才觀察到變化(不是立即可見)。
U(I+1).CK 接 U(I).Q̄。Q0 從 1→0 → Q̄0 從 0→1,U1 才看到上升邊緣 → toggle。一級一級「漣波」傳上去 ── 累積延遲。
Entity 是對外的介面(腳位、方向),Architecture 是內部實作。像 C 的 .h 與 .c。
要用 DEFF1,必須先在 Architecture 區內 Component DEFF1...End Component 宣告,再用 Port Map 把實際 signal 接到腳位上。Port Map 在 elaboration 時固定,不是執行期動作。
外層的 Signal TMP(4 downto 0) 是 5 條互連訊號連線。TMP(4) 雖然被 U3.Q̄ 驅動但沒有下游 ── 是「沒被讀取」不是「floating」。
Sensitivity list (CL, CK):只要 CL 或 CK 「有變化」(0→1 或 1→0) 就執行一次。下降邊緣也會觸發 Process,只是 Rising_Edge 判斷為 false 而已。
if CL='1' 先判斷;elsif Rising_Edge(CK) 後判斷。CL 永遠優先 ── 非同步清除:不需要等 CK 邊緣,只要 CL=1 就立刻把 FF 拉到 0。
先自己想/動筆算,再展開答案核對,最後用上面的模擬器驗證。
起點 Q3Q2Q1Q0 = 0000。請填出第 1~8 個 PulseIn 後的二進位,輸入時會即時 ✓/✗ 對答。
| Pulse | 你的答案 (Q3 Q2 Q1 Q0) | 十進位 | 狀態 |
|---|
如果 Counter4B 裡 Port Map 第 3 個位置從 '1' 改成 '0',Q3Q2Q1Q0 序列會變成怎樣?
if T='1' Then TMP := not TMP; Else null;Else null → FF 完全不 toggle → Q 維持原值。0000 → Q 永遠停在 0000。② 若 沒 reset 過,Variable TMP 是 'U'(uninitialised),Q 會永遠停在「未知/不可信」狀態,模擬器報 X。
這就是為什麼這顆 DEFF1 比一般 D-FF 多一隻 T 腳:它其實是「可被禁用的 T-FF」,T=0 等同 hold。
TMP(4 downto 0)?多一條?明明只需要 PulseIn + 3 條中繼線(U0→U1, U1→U2, U2→U3),為什麼宣告了 5 條?
TMP(I), TMP(I+1)。TMP(4)(U3.Q̄)。如果只宣告 (3 downto 0),Generate 在 I=3 時會超出 bound 編譯錯誤。假設 Q3Q2Q1Q0 = 0101,現在 CL 被拉高為 1,同時 PulseIn 來了一個上升邊緣。Q 變成多少?
if CL='1' Then TMP := '0'; elsif Rising_Edge(CK) Then ... ── CL 優先,不會進到 elsif 的 toggle 分支。