The long version: where the flip-flop inference rules come from, what asynchronous reset really costs, how a pipeline buys throughput, and why Mealy and Moore machines that detect the same sequence do not produce the same waveform.完整版:正反器推論規則從何而來、非同步重置真正的代價是什麼、管線如何買到吞吐量,以及為什麼偵測同一組序列的 Mealy 機與 Moore 機產生的波形並不相同。詳細版:フリップフロップ推論規則はどこから来るのか、非同期リセットが実際に何を代償とするのか、パイプラインはどのようにスループットを買うのか、そして同じ系列を検出するミーリ機械とムーア機械がなぜ同じ波形を出さないのか。
Lecture 5 dealt with combinational synthesis, where every output is a pure function of the current inputs and the only thing that can go wrong is an accidental latch. This lecture adds the one ingredient that makes a digital system a system rather than a calculator: memory. From here on, the circuit's output depends not only on what you present at the inputs but on what you presented at every earlier clock edge, and that history lives in flip-flops.第 5 講處理的是組合邏輯合成,那裡每個輸出都是當前輸入的純函數,唯一會出錯的地方就是意外產生的閂鎖。這一講加入了讓數位系統成為系統而不只是計算器的那項要素:記憶。從這裡開始,電路的輸出不只取決於你現在送進輸入端的值,還取決於你在之前每一個時脈邊緣所送進的值,而這段歷史就存在正反器裡。第 5 講では組合せ論理の合成を扱った。そこではすべての出力が現在の入力の純粋な関数であり、唯一の落とし穴は偶発的なラッチだけだった。本講では、ディジタルシステムを単なる計算器ではなくシステムたらしめる唯一の要素、すなわち記憶を加える。ここから先、回路の出力は入力に今与えた値だけでなく、過去のすべてのクロックエッジで与えた値にも依存する。そしてその履歴はフリップフロップの中に宿る。
The deceptively short slide for this is the one that says a register variable in a behaviour becomes a flip-flop if it is “aligned with the clock behaviours” and “assigned value by some assignments”. That single sentence is the contract between you and the synthesiser, and almost every sequential-synthesis bug a student meets is a violation of it. The whole of this first section is spent unpacking it.對應這件事的投影片短得令人誤會:它說行為描述中的 register 變數若「與時脈行為對齊」且「由某些指派賦值」,就會成為正反器。這一句話就是你與合成器之間的契約,而學生遇到的循序合成錯誤幾乎每一個都是違反了它。整個第一節就是在把這句話拆開來講。これに対応するスライドは、誤解を招くほど短い。曰く、ビヘイビア記述中の register 変数は「クロック動作に整合している」かつ「何らかの代入で値を与えられている」ならフリップフロップになる、と。この一文があなたと合成器のあいだの契約であり、学生が出会う順序回路合成のバグはほとんどすべてこの契約の違反である。第 1 節はまるごと、この一文をほどくために費やす。
The first thing to understand is that a logic synthesiser does not execute your Verilog. A simulator does: it maintains an event queue, wakes behaviours when their event controls fire, evaluates right-hand sides, schedules updates, and so on. A synthesiser does something quite different and much more limited. It pattern-matches your source against a small catalogue of templates, and for each template it knows a corresponding piece of hardware. Everything outside that catalogue is either rejected or, worse, accepted and mapped onto hardware whose simulation behaviour does not match your RTL.首先要理解的是:邏輯合成器並不會執行你的 Verilog。模擬器才會:它維護事件佇列、在事件控制觸發時喚醒行為、計算右手邊、排程更新,依此類推。合成器做的事相當不同,而且受限得多。它把你的原始碼拿去比對樣板——對照一份很小的樣板目錄,而每一個樣板它都知道對應到哪一塊硬體。落在目錄之外的東西不是被拒絕,就是更糟:被接受並對映到一塊模擬行為與你的 RTL 不一致的硬體。まず理解すべきは、論理合成器はあなたの Verilog を実行しないということである。実行するのはシミュレータのほうだ。シミュレータはイベントキューを保持し、イベント制御が発火したらビヘイビアを起こし、右辺を評価し、更新をスケジュールする。合成器がするのはまったく別の、はるかに限定された仕事である。合成器はソースを小さなテンプレートの目録に対してパターンマッチし、各テンプレートに対応するハードウェアを知っている。目録の外にあるものは拒否されるか、さらに悪いことに、受理されたうえでシミュレーション動作が RTL と一致しないハードウェアに写像される。
The catalogue for sequential logic is tiny. There are essentially three entries:循序邏輯的樣板目錄非常小,基本上只有三項:順序論理のテンプレート目録はごく小さい。実質的に三項目しかない。
always block whose event control contains at least one posedge or negedge. Everything assigned inside becomes the output of an edge-triggered flip-flop clocked by the edge signal, unless the tool can prove the variable never needs to survive the edge.邊緣敏感樣板。事件控制中至少含一個 posedge 或 negedge 的 always 區塊。裡面被指派的所有變數都會成為由該邊緣信號驅動的邊緣觸發正反器的輸出,除非工具能證明該變數不需要跨過邊緣存活。エッジ感応テンプレート。イベント制御に posedge または negedge を少なくとも一つ含む always ブロック。内部で代入される変数はすべて、そのエッジ信号でクロックされるエッジトリガ・フリップフロップの出力になる。ただし、その変数がエッジをまたいで保持される必要がないとツールが証明できる場合を除く。always block with a pure level sensitivity list (always @(a or b) or always @*). Variables assigned on every path through the block become wires; variables not assigned on some path become latches. This is the Lecture 5 material.位準敏感樣板。感應列中只有位準的 always 區塊(always @(a or b) 或 always @*)。在區塊的每一條路徑上都被指派的變數成為連線;在某條路徑上沒有被指派的變數則成為閂鎖。這是第 5 講的內容。レベル感応テンプレート。感度リストがレベルだけの always ブロック(always @(a or b) や always @*)。ブロック内のすべての経路で代入される変数は配線になり、ある経路で代入されない変数はラッチになる。これは第 5 講で扱った内容である。assign always produces combinational logic (or a tri-state driver), never storage.連續指派。assign 永遠產生組合邏輯(或三態驅動器),絕不產生儲存元件。連続代入。assign は常に組合せ論理(あるいはトライステートドライバ)を生み、記憶素子を生むことはない。Notice what this means in practice: the decision “flip-flop or not” is taken almost entirely by the shape of the event control, before the tool has even looked at the body. The body only decides which variables inside an edge-sensitive block can be optimised back down to wires. If you write always @(posedge clk), you have already committed to a register bank; the only remaining question is how wide it is.注意這在實務上代表什麼:「要不要正反器」這個決定幾乎完全由事件控制的形狀決定,工具甚至還沒看過區塊本體就已經決定了。本體只決定邊緣敏感區塊裡哪些變數可以被最佳化回連線。只要你寫下 always @(posedge clk),你就已經承諾了一排暫存器;剩下的問題只是它有多寬。これが実務上どういう意味かに注意してほしい。「フリップフロップにするかどうか」の判断は、ほぼ完全にイベント制御の形で決まり、ツールが本体を見るより前に決まっている。本体が決めるのは、エッジ感応ブロック内のどの変数が配線まで最適化し戻せるか、それだけだ。always @(posedge clk) と書いた時点で、あなたはすでにレジスタ群を確約している。残る問いはその幅だけである。
A synthesiser has to produce a circuit that behaves identically for every input sequence, not just the ones a testbench happens to apply. Proving that from arbitrary procedural code is undecidable in general. Restricting the input language to a handful of recognisable idioms — the “synthesisable subset” codified in IEEE 1364.1 — is what makes the problem tractable. The price you pay is that perfectly legal, perfectly simulatable Verilog can be unsynthesisable, and that is not a bug in the tool.合成器必須產生一個對每一種輸入序列都行為一致的電路,而不只是測試平台剛好餵進去的那些。從任意程序式程式碼證明這件事在一般情況下是不可判定的。把輸入語言限制成少數幾個可辨識的慣用寫法——也就是 IEEE 1364.1 所訂的「可合成子集」——正是讓問題變得可處理的手段。你付出的代價是:完全合法、完全可模擬的 Verilog 有可能不可合成,而這並不是工具的錯。合成器は、テストベンチがたまたま与える入力列だけでなく、あらゆる入力列に対して同一に振る舞う回路を生成しなければならない。任意の手続き的コードからそれを証明することは、一般には決定不能である。入力言語を少数の認識可能な定型表現に制限すること——IEEE 1364.1 に成文化された「合成可能サブセット」——こそが、この問題を扱えるものにしている。その代償として、完全に合法で完全にシミュレート可能な Verilog が合成不能になりうるが、それはツールの欠陥ではない。
Before the rules, the inventory. The slide lists the sequential structures that cell-based synthesis is routinely asked to produce. It is worth reading as a map of the rest of the lecture, because every one of them is just flip-flops plus a particular combinational cone in front of the D pins.在談規則之前,先清點一下庫存。投影片列出了 cell-based 合成經常被要求產生的循序結構。這份清單值得當成本講其餘部分的地圖來讀,因為其中每一項都只是正反器加上 D 腳前面某個特定的組合邏輯錐。規則に入る前に、まず在庫を数えておく。スライドには、セルベース合成が日常的に生成を求められる順序構造が列挙されている。この一覧は本講の残りの地図として読む価値がある。どの項目も、結局はフリップフロップと D ピンの前に置かれた特定の組合せ論理コーンにすぎないからだ。
Two entries on that list deserve a flag straight away. The transparent latch is there because latches are legitimate library cells that you sometimes want deliberately — in a clock-gating cell, in a pulse-latch datapath, in a retention register. What Lecture 5 warned against is the accidental latch produced by an incomplete combinational description, not latches as such. And the clock generator is there with a large asterisk: on an ASIC you do not synthesise clock generation out of standard cells, you instantiate a PLL or a clock-divider macro and let clock-tree synthesis own the net. A divider built from ordinary flip-flops whose Q drives another flip-flop's clock pin is exactly the ripple-counter structure of Section 5, with all of its problems.這份清單裡有兩項要馬上標註。透通閂鎖會在上面,是因為閂鎖是合法的函式庫單元,有時你是刻意要用它的——在 clock-gating cell 裡、在 pulse-latch 資料路徑裡、在保持暫存器裡。第 5 講警告的是組合邏輯描述不完整所造成的意外閂鎖,而不是閂鎖本身。而時脈產生器則要加上一個大大的星號:在 ASIC 上你不會用標準單元去合成時脈產生,你會實例化一顆 PLL 或除頻巨集,然後讓時脈樹合成去擁有那條網路。用普通正反器搭出來、Q 直接去驅動另一顆正反器時脈腳的除頻器,正好就是第 5 節的漣波計數器結構,連同它所有的問題。この一覧のうち二つは、すぐに旗を立てておく価値がある。トランスペアレントラッチが載っているのは、ラッチが正当なライブラリセルであり、意図して使いたい場面があるからだ——クロックゲーティングセル、パルスラッチ・データパス、リテンションレジスタなど。第 5 講が戒めたのは、組合せ記述の不完全さが生む偶発的なラッチであって、ラッチそのものではない。そしてクロック生成器には大きな注釈が付く。ASIC では標準セルからクロック生成を合成したりはしない。PLL か分周マクロをインスタンス化し、そのネットはクロックツリー合成に委ねる。普通のフリップフロップで組み、Q が別のフリップフロップのクロックピンを駆動する分周器は、まさに第 5 節のリプルカウンタ構造そのものであり、その問題もすべて引き継ぐ。
Here is the rule stated carefully enough to be useful:把規則講得夠精確、好用的版本是這樣:役に立つ程度にきちんと述べると、規則はこうなる。
A variable declared reg (or integer, or a vector thereof) is synthesised as the output of an edge-triggered flip-flop if and only if both of the following hold:一個宣告為 reg(或 integer,或其向量)的變數,若且唯若下列兩條都成立,才會被合成為邊緣觸發正反器的輸出:reg(あるいは integer、またはそのベクタ)として宣言された変数がエッジトリガ・フリップフロップの出力として合成されるのは、次の二条件がともに成り立つとき、かつそのときに限る。
always block whose event control contains an edge qualifier — this is what “aligned with the clock behaviour” means; and它是某個 always 區塊內指派的目標,而該區塊的事件控制含有邊緣修飾詞——這就是「與時脈行為對齊」的意思;且イベント制御にエッジ修飾子を含む always ブロック内で、代入の対象になっていること——これが「クロック動作に整合している」の意味である。かつCondition 1 is the one the slide states. Condition 2 is the one the slide leaves implicit by saying “assigned value by some assignments”, and it is the one that actually does the work. Consider the difference between these two blocks:條件 1 就是投影片講出來的那一條。條件 2 則是投影片用「由某些指派賦值」含糊帶過的那一條,而它才是真正在幹活的那一條。比較這兩個區塊的差別:条件 1 はスライドが述べているほうである。条件 2 はスライドが「何らかの代入で値を与えられている」と言って暗黙に済ませているほうで、実際に働いているのはこちらだ。次の二つのブロックの違いを見てほしい。
always @ (posedge clk) begin t = a & b; // written first y <= t | c; // then read end // 1 flip-flop: y. t is an AND gate.
always @ (posedge clk) begin y <= t | c; // read first t = a & b; // written afterwards end // 2 flip-flops: y and t.
Both blocks are edge-sensitive and both assign t, so condition 1 is satisfied in each. The difference is condition 2. On the left, t is written before it is read, so its value at the start of the block is irrelevant: nothing in the circuit ever needs yesterday's t, and the tool collapses it into the combinational cone feeding y's D pin. On the right, t is read before it is written, so y is a function of t's value at the previous clock edge — and the only way hardware remembers a previous value is with a flip-flop. The tool must allocate one.兩個區塊都是邊緣敏感的,也都指派了 t,所以條件 1 在兩邊都成立。差別在條件 2。左邊的 t 先寫後讀,所以它在區塊開始時的值無關緊要:電路裡沒有任何東西需要昨天的 t,於是工具把它收進餵給 y 的 D 腳的組合邏輯錐裡。右邊的 t 先讀後寫,於是 y 是 t 在前一個時脈邊緣的值的函數——而硬體要記住前一個值的唯一辦法就是正反器。工具非配一顆不可。どちらのブロックもエッジ感応で、どちらも t に代入しているので、条件 1 は両方で満たされる。違いは条件 2 にある。左では t は読まれる前に書かれるので、ブロック開始時点の値はどうでもよい。回路のどこも昨日の t を必要としないので、ツールはそれを y の D ピンを駆動する組合せコーンに畳み込む。右では t は書かれる前に読まれるので、y は直前のクロックエッジにおける t の値の関数になる——そしてハードウェアが過去の値を覚える唯一の手段はフリップフロップである。ツールは一つ割り当てざるを得ない。
This is why experienced designers call the test “read-before-write”. Write-before-read gives you a wire; read-before-write gives you storage. It is a purely syntactic test applied to the straight-line order of blocking assignments, and it is deterministic: two different synthesisers will agree.這就是為什麼老練的設計者把這個判準叫做「read-before-write」。先寫後讀給你連線,先讀後寫給你儲存元件。它是套用在阻斷式指派直線順序上的純語法判準,而且是確定性的:兩套不同的合成器會得到一致的結論。だからこそ経験を積んだ設計者はこの判定を「read-before-write」と呼ぶ。書いてから読めば配線、読んでから書けば記憶素子になる。これはブロッキング代入の直線的な順序に適用される純粋に構文的な判定であり、決定的である。異なる二つの合成器が同じ結論に達する。
Non-blocking assignments sidestep the question entirely. The semantics of <= are that every right-hand side in the block is evaluated using the values that existed at the edge, and all the left-hand sides are updated afterwards. There is no ordering to analyse. Every target of a non-blocking assignment in an edge-sensitive block is, by construction, read-before-write with respect to the edge, so every one of them becomes a flip-flop. That predictability is the entire reason the coding guideline “use <= for clocked logic” exists.非阻斷式指派則完全繞過了這個問題。<= 的語義是:區塊中所有右手邊都用邊緣當下存在的值計算,然後所有左手邊才一起更新。沒有順序可分析。邊緣敏感區塊中每一個非阻斷式指派的目標,就其構造而言,相對於該邊緣都是先讀後寫,所以每一個都會變成正反器。這種可預測性正是「時脈邏輯請用 <=」這條撰碼準則存在的全部理由。ノンブロッキング代入はこの問いを完全に回避する。<= の意味論は、ブロック内のすべての右辺がエッジ時点で存在した値を用いて評価され、そのあとで左辺がまとめて更新される、というものだ。解析すべき順序が存在しない。エッジ感応ブロック内のノンブロッキング代入の対象は、構成上すべてそのエッジに対して read-before-write であり、したがってすべてフリップフロップになる。この予測可能性こそが「クロック同期論理には <= を使え」というコーディング指針が存在する理由のすべてである。
Put the combinational rules from Lecture 5 next to the sequential rules from this one and you get a complete picture. The table below is worth memorising, because exam questions about “what does this infer?” are always one of these six cells.把第 5 講的組合邏輯規則和這一講的循序邏輯規則並排放在一起,整張圖就完整了。下面這張表值得背起來,因為考題問「這會推論出什麼?」時,答案永遠是這六格之一。第 5 講の組合せ論理の規則と本講の順序論理の規則を並べると、全体像が完成する。下の表は暗記する価値がある。「これは何を推論するか」という試験問題の答えは、必ずこの六つのセルのどれかだからだ。
| Event control事件控制イベント制御 | Variable assigned on every path?每條路徑都有指派?全経路で代入されるか | Read before written?先讀後寫?書く前に読むか | Result結果結果 |
|---|---|---|---|
@(posedge clk) | yes是はい | no否いいえ | Wire (combinational temp inside the cone)連線(邏輯錐內的組合暫時值)配線(コーン内の組合せ一時値) |
@(posedge clk) | yes是はい | yes是はい | Flip-flop正反器フリップフロップ |
@(posedge clk) | no否いいえ | — | Flip-flop with enable (hold when unassigned)附致能的正反器(未指派時保持)イネーブル付きフリップフロップ(未代入時は保持) |
@* (level)@*(位準)@*(レベル) | yes是はい | no否いいえ | Wire連線配線 |
@* (level)@*(位準)@*(レベル) | yes是はい | yes是はい | Combinational loop — synthesis error or oscillator組合迴路 — 合成錯誤或振盪器組合せループ — 合成エラーまたは発振器 |
@* (level)@*(位準)@*(レベル) | no否いいえ | — | Latch (usually a bug)閂鎖(通常是錯誤)ラッチ(たいていはバグ) |
The row that students find most surprising is the third one. In a combinational block, failing to assign a variable on some path means “hold the old value”, and the only hardware that holds a value without a clock is a latch. In an edge-triggered block, failing to assign on some path also means “hold the old value” — but holding a value across a clock edge is precisely what a flip-flop does. So the same source-level sin produces a disaster in one context and a perfectly ordinary load-enable register in the other. Section 4.4 is built entirely around that distinction.學生覺得最意外的是第三列。在組合區塊裡,某條路徑上沒有指派某個變數,意思是「保持舊值」,而沒有時脈卻能保持值的硬體只有閂鎖。在邊緣觸發區塊裡,某條路徑上沒有指派同樣意味著「保持舊值」——但跨越時脈邊緣保持值正是正反器在做的事。所以同一個原始碼層面的「罪」,在一個情境下是災難,在另一個情境下只是一顆再普通不過的載入致能暫存器。4.4 節整節就是圍著這個區別建起來的。学生がいちばん意外に思うのは三行目である。組合せブロックでは、ある経路で変数に代入しないことは「古い値を保持する」を意味し、クロックなしで値を保持できるハードウェアはラッチしかない。エッジトリガブロックでも、ある経路で代入しないことは同じく「古い値を保持する」を意味する——が、クロックエッジをまたいで値を保持するのはまさにフリップフロップの仕事である。つまり、ソース上の同じ「罪」が、一方の文脈では災厄を、他方ではごく当たり前のロードイネーブル付きレジスタを生む。4.4 節はまるごとこの区別の上に組み立てられている。
The fifth row is the combinational loop. always @* y = y + 1; is legal Verilog and will hang most simulators; in synthesis it produces either an error or an unconstrained asynchronous feedback path that static timing analysis will flag as a loop and break arbitrarily. If a timing report ever contains the phrase “combinational loop detected, loop broken at pin X”, you have written this by accident.第五列是組合迴路。always @* y = y + 1; 是合法的 Verilog,而且會讓多數模擬器當掉;在合成時它不是產生錯誤,就是產生一條未受約束的非同步回授路徑,靜態時序分析會把它標為迴路並任意地切斷它。如果時序報告裡出現「combinational loop detected, loop broken at pin X」這句話,那就是你不小心寫出了這個。五行目は組合せループである。always @* y = y + 1; は合法な Verilog であり、たいていのシミュレータをハングさせる。合成ではエラーになるか、制約のない非同期フィードバック経路が生じ、スタティックタイミング解析がそれをループとして報告し任意の箇所で切断する。タイミングレポートに「combinational loop detected, loop broken at pin X」という文が現れたら、あなたはこれを事故で書いてしまっている。
The slide's example is a two-bit register pair that can be set to one of two fixed patterns or, failing that, swapped. It is a good example precisely because the swap is the case where blocking and non-blocking assignments give genuinely different hardware.投影片的例子是一對兩位元的暫存器,可以被設成兩種固定的圖樣之一,否則就交換彼此的值。它之所以是好例子,正是因為「交換」這個情況下阻斷式與非阻斷式指派會給出真正不同的硬體。スライドの例は 2 ビットのレジスタ対で、二つの固定パターンのいずれかに設定するか、どちらでもなければ互いの値を入れ替える。これが良い例なのは、まさにスワップこそブロッキング代入とノンブロッキング代入が本当に異なるハードウェアを生む場面だからである。
module swap_synch (set1, set2, clk, data_a, data_b);
output data_a, data_b;
input clk, set1, set2;
reg data_a, data_b;
always @ (posedge clk)
begin
if (set1) begin data_a <= 1; data_b <= 0; end
else if (set2) begin data_a <= 0; data_b <= 1; end
else
begin
data_b <= data_a;
data_a <= data_b;
end
end
endmodule
Work through the inference. Both data_a and data_b are assigned inside an edge-sensitive behaviour, so condition 1 holds. Both are read in the else branch, so condition 2 holds. Two flip-flops. Each flip-flop's D input is whatever the if/else if/else chain selects, and a priority chain of two conditions over three mutually exclusive outcomes is exactly a 4-to-1 multiplexer addressed by the concatenation {set1, set2}:把推論走一遍。data_a 與 data_b 都在邊緣敏感的行為裡被指派,條件 1 成立。兩者都在 else 分支裡被讀取,條件 2 成立。兩顆正反器。每顆正反器的 D 輸入就是 if/else if/else 鏈所選出的值,而兩個條件、三個互斥結果的優先鏈,正好就是一顆由串接值 {set1, set2} 定址的 4 對 1 多工器:推論を順に追ってみよう。data_a と data_b はどちらもエッジ感応ビヘイビア内で代入されるので条件 1 が成り立つ。どちらも else 分岐で読まれるので条件 2 も成り立つ。フリップフロップは二つ。各フリップフロップの D 入力は if/else if/else 連鎖が選ぶ値であり、三つの排他的な結果に対する二条件の優先連鎖は、まさに連結 {set1, set2} でアドレスされる 4 対 1 マルチプレクサである。
{set1, set2} | 00 | 01 | 10 | 11 |
|---|---|---|---|---|
| Branch taken選到的分支選ばれる分岐 | final else最後的 else最後の else | set2 | set1 | set1 (priority)set1(優先)set1(優先) |
D of data_adata_a 的 Ddata_a の D | data_b | 0 | 1 | 1 |
D of data_bdata_b 的 Ddata_b の D | data_a | 1 | 0 | 0 |
The 11 column is the one people get wrong. The description is a priority chain, not a parallel case: when both set signals are asserted, set1 wins because it is tested first. The synthesiser encodes that priority by making mux inputs 2 and 3 identical, which means the mux select only really needs set1 and set2 & ~set1 — and that is why the gate-level netlist on the slide shows an inverter, a NAND and a NOR per bit rather than a literal four-input multiplexer cell. The tool has flattened the mux into a two-level gate network and then mapped it onto whatever the library offers cheaply.大家最常搞錯的是 11 這一欄。這段描述是優先鏈,不是平行的 case:當兩個 set 信號同時被確立時,set1 贏,因為它先被測試。合成器把這個優先次序編碼成「多工器的第 2 與第 3 個輸入相同」,這表示多工器的選擇訊號其實只需要 set1 與 set2 & ~set1——而這正是投影片上的閘級 netlist 每一位元顯示一顆反相器、一顆 NAND 和一顆 NOR,而不是一顆字面上的四輸入多工器單元的原因。工具已經把多工器壓平成兩層閘網路,再把它對映到函式庫中便宜的任何東西上。みなが間違えるのは 11 の列である。この記述は並列 case ではなく優先連鎖だ。二つの set 信号が同時にアサートされると、先に判定される set1 が勝つ。合成器はこの優先度を、マルチプレクサの入力 2 と 3 を同一にすることで符号化する。つまりマルチプレクサの選択信号は実質 set1 と set2 & ~set1 だけで足りる——スライドのゲートレベル・ネットリストが、文字どおりの 4 入力マルチプレクサセルではなく、1 ビットあたりインバータ 1 個と NAND 1 個、NOR 1 個を示しているのはそのためだ。ツールはマルチプレクサを 2 段のゲート網に平坦化し、ライブラリが安く提供するものへ写像したのである。
swap_synch. The edge in the event control chooses the register template; the priority if chain becomes the 4:1 multiplexer in front of each D pin; the cross-coupled feedback paths implement the swap. Because non-blocking assignments sample both Q outputs at the same edge, the two feedback wires cross without any ordering hazard.圖 1 — swap_synch 的正反器推論。事件控制中的邊緣決定了暫存器樣板;優先式 if 鏈變成每個 D 腳前的 4:1 多工器;交叉耦合的回授路徑實現了交換。由於非阻斷式指派在同一個邊緣同時取樣兩個 Q 輸出,這兩條回授線可以交叉而沒有任何順序上的風險。図 1 — swap_synch のフリップフロップ推論。イベント制御のエッジがレジスタ・テンプレートを選び、優先 if 連鎖が各 D ピン前の 4:1 マルチプレクサになり、たすき掛けのフィードバック経路がスワップを実現する。ノンブロッキング代入は同一エッジで両方の Q をサンプルするので、二本のフィードバック配線は順序ハザードなしに交差できる。Now replace the two non-blocking assignments in the else branch with blocking ones:現在把 else 分支裡的兩個非阻斷式指派換成阻斷式:次に、else 分岐の二つのノンブロッキング代入をブロッキング代入に置き換えてみる。
else begin data_b <= data_a; data_a <= data_b; end // both RHS sampled at the edge // a' = b, b' = a
else begin data_b = data_a; data_a = data_b; end // executes in order: // b := a, then a := b (= a) // a' = a, b' = a — no swap
In the blocking version, data_b is written and then immediately read, so it is write-before-read and collapses to a wire; data_a ends up assigned its own value, which the tool simplifies to a feedback. You would see this in the area report as one flip-flop where you expected two, and in simulation as both outputs stuck equal. This is the single most common sequential-Verilog bug and it is worth recognising from its symptom: a shift register that does not shift, a swap that does not swap, a pipeline whose stages all contain the same value.在阻斷式版本裡,data_b 被寫入之後馬上被讀取,所以它是先寫後讀,塌縮成一條連線;data_a 最後被指派成它自己的值,工具會把它簡化成回授。你會在面積報告裡看到一顆正反器,而你原本期待兩顆;在模擬裡則看到兩個輸出永遠相等。這是最常見的循序 Verilog 錯誤,值得從症狀認出來:不會移位的移位暫存器、不會交換的交換、每一級都裝著同一個值的管線。ブロッキング版では、data_b は書かれた直後に読まれるので write-before-read となり配線に潰れる。data_a は結局自分自身の値を代入されるので、ツールはそれをフィードバックに簡約する。面積レポートには、二つ期待していたところに一つのフリップフロップが現れ、シミュレーションでは両出力が常に等しくなる。これは順序 Verilog でもっとも多いバグであり、症状から見抜けるようにしておく価値がある。シフトしないシフトレジスタ、入れ替わらないスワップ、全段が同じ値を保持するパイプライン。
Four failure modes account for most of the trouble, and each has a distinctive signature in a tool report.四種失敗模式佔了大部分的麻煩,而每一種在工具報告裡都有獨特的特徵。トラブルの大半は四つの失敗モードで説明がつき、それぞれツールのレポートに固有の痕跡を残す。
Cause: blocking assignments in a cascade, as above. Symptom: the synthesis area or resource report lists fewer sequential cells than you expect. If you intended an n-stage shift register and the report says one DFFQX1, stop and look at the assignment operators before you look at anything else. In simulation the symptom is that all stages change together.成因:如前所述,串接中用了阻斷式指派。症狀:合成的面積或資源報告列出的循序單元比你預期的少。如果你想要的是 n 級移位暫存器,而報告說只有一顆 DFFQX1,那就先停下來看指派運算子,什麼都先不要看。在模擬裡的症狀是所有級一起變動。原因:前述のとおり、カスケードでブロッキング代入を使っている。症状:合成の面積・リソースレポートに並ぶ順序セルが期待より少ない。n 段のシフトレジスタのつもりでレポートに DFFQX1 が一つしかなければ、他を見る前にまず代入演算子を見ること。シミュレーション上の症状は、全段が一斉に変化することである。
Cause: a temporary read before it is written, usually because statements got reordered during editing, or because a variable is assigned in one branch and read in another. Symptom: the register count is higher than the architectural state you can name, and the extra registers often have tool-generated names. In simulation the symptom is an extra cycle of latency somewhere in a datapath.成因:某個暫時值在被寫入之前先被讀取,通常是因為編輯時把敘述順序調動了,或是某個變數在一個分支被指派、在另一個分支被讀取。症狀:暫存器數量高於你叫得出名字的架構狀態,而多出來的暫存器常常帶著工具自動產生的名字。在模擬裡的症狀是資料路徑某處多了一個週期的延遲。原因:一時変数が書かれる前に読まれている。たいていは編集中に文の順序が入れ替わったか、ある分岐で代入し別の分岐で読んでいるためだ。症状:レジスタ数が、名前を挙げられるアーキテクチャ上の状態より多く、余分なレジスタにはツール生成の名前が付いていることが多い。シミュレーション上の症状は、データパスのどこかでレイテンシが 1 サイクル増えることである。
Cause: the same reg assigned in two different always blocks. Verilog allows this — a simulator simply applies whichever assignment executes last — but hardware cannot have two flip-flops driving one net. Symptom: an outright synthesis error, usually worded “net ... has multiple drivers” or “variable is assigned in more than one block”. The fix is always to merge the blocks or to introduce separate variables and an explicit multiplexer.成因:同一個 reg 在兩個不同的 always 區塊裡被指派。Verilog 允許這樣——模擬器只會採用最後執行的那個指派——但硬體不可能讓兩顆正反器驅動同一條網路。症狀:直接的合成錯誤,措辭通常是「net ... has multiple drivers」或「variable is assigned in more than one block」。修法永遠是把區塊合併,或是改用不同的變數再加一顆明確的多工器。原因:同じ reg を二つの別々の always ブロックで代入している。Verilog はこれを許す——シミュレータは最後に実行された代入を採用するだけだ——が、ハードウェアでは二つのフリップフロップが一本のネットを駆動することはできない。症状:明確な合成エラーで、文言はたいてい「net ... has multiple drivers」か「variable is assigned in more than one block」。直し方は常に、ブロックを統合するか、変数を分けて明示的なマルチプレクサを置くかである。
Cause: an expression in the edge qualifier, e.g. always @(posedge (clk & en)), or using a flip-flop output as another flip-flop's clock. Symptom: the netlist shows a logic gate driving a clock pin; clock-tree synthesis then has to treat that gate's output as a new clock, static timing analysis needs a create_generated_clock constraint that nobody wrote, and the DFT flow cannot shift the scan chain through the gated domain. Symptom in the timing report: unconstrained paths, or a clock named something like clk_gated_i_n3 that you did not define. The correct way to express “only update when enabled” is an if (en) inside the block — then the tool can choose between a feedback mux and a proper integrated clock-gating cell, and it will tell timing analysis what it did.成因:在邊緣修飾詞裡放了運算式,例如 always @(posedge (clk & en)),或是拿某顆正反器的輸出去當另一顆正反器的時脈。症狀:netlist 裡出現一顆邏輯閘驅動著時脈腳;接著時脈樹合成必須把那顆閘的輸出當成一個新時脈,靜態時序分析需要一條沒人寫的 create_generated_clock 約束,而 DFT 流程無法把掃描鏈推過那個被閘控的區域。在時序報告裡的症狀是:未受約束的路徑,或是出現一個你沒定義過、名字長得像 clk_gated_i_n3 的時脈。要表達「只有致能時才更新」,正確的寫法是在區塊裡放一個 if (en)——這樣工具就能在回授多工器與正規的整合式時脈閘控單元之間選擇,而且它會把自己做了什麼告訴時序分析。原因:エッジ修飾子の中に式を書いている(たとえば always @(posedge (clk & en)))、あるいはフリップフロップの出力を別のフリップフロップのクロックに使っている。症状:ネットリストで論理ゲートがクロックピンを駆動している。するとクロックツリー合成はそのゲート出力を新しいクロックとして扱わねばならず、スタティックタイミング解析は誰も書いていない create_generated_clock 制約を必要とし、DFT フローはゲートされた領域にスキャンチェーンを通せなくなる。タイミングレポート上の症状は、制約のない経路、あるいは定義した覚えのない clk_gated_i_n3 のような名前のクロックである。「イネーブル時のみ更新」を正しく表現する方法は、ブロック内に if (en) を置くことだ。そうすればツールはフィードバック・マルチプレクサか、きちんとした集積クロックゲーティングセルかを選べるし、何をしたかをタイミング解析に伝えてくれる。
Anything you put a posedge or negedge in front of becomes, in the eyes of every downstream tool, either a clock or an asynchronous control. There is no third category. Do not use an edge qualifier to mean “when this changes” — that is what a level-sensitive list is for — and never put an edge on a data signal.凡是你在前面加了 posedge 或 negedge 的東西,在下游每一個工具眼中都會變成時脈或非同步控制,沒有第三種類別。不要用邊緣修飾詞來表達「當這個值改變時」——那是位準感應列的工作——也絕對不要在資料信號上加邊緣。posedge や negedge を前に置いたものは何であれ、下流のあらゆるツールの目にはクロックか非同期制御として映る。第三のカテゴリは存在しない。「これが変化したとき」を表すためにエッジ修飾子を使ってはいけない——それはレベル感度リストの役目だ——し、データ信号にエッジを付けることは決してしてはならない。
Problem: For each block, state how many flip-flops are inferred and what the circuit does. Assume a, b, c, d are primary inputs and y, q1, q2, q3 are module outputs.題目:針對每個區塊,說明推論出幾顆正反器,以及電路做了什麼。假設 a、b、c、d 是主要輸入,y、q1、q2、q3 是模組輸出。問題:各ブロックについて、推論されるフリップフロップの個数と回路の動作を述べよ。a、b、c、d は一次入力、y、q1、q2、q3 はモジュール出力とする。
// (a) always @ (posedge clk) begin t = a & b; y <= t | c; end // (b) always @ (posedge clk) begin y <= t | c; t = a & b; end // (c) always @ (posedge clk) begin q1 <= d; q2 <= q1; q3 <= q2; end // (d) always @ (posedge clk) begin q1 = d; q2 = q1; q3 = q2; end // (e) always @ (posedge clk) if (en) q1 <= d; // (f) always @ (posedge clk) begin q1 <= d; end always @ (posedge clk) begin q1 <= ~d; end
(a) One. t is written before it is read in the same pass, so it is a plain AND gate inside y's input cone. Hardware: y ← (a&b)|c, registered.(a) 一顆。t 在同一輪裡先寫後讀,所以它只是 y 輸入錐裡的一顆 AND 閘。硬體:y ← (a&b)|c,經過暫存。(a) 一つ。t は同じパス内で読まれる前に書かれるので、y の入力コーン内の単なる AND ゲートである。ハードウェア:y ← (a&b)|c をレジスタで受ける。
(b) Two. t is read in the first statement and written in the second, so y depends on the previous value of t. The tool must store t. Hardware: an AND feeding flip-flop t, whose Q is OR-ed with c and registered into y. Note the extra cycle of latency compared with (a) — this is the kind of silent behaviour change that a statement reorder can cause.(b) 兩顆。t 在第一個敘述被讀取、第二個敘述才被寫入,所以 y 取決於 t 的前一個值。工具必須把 t 存起來。硬體:一顆 AND 餵進正反器 t,它的 Q 與 c 做 OR 之後再暫存進 y。注意和 (a) 相比多了一個週期的延遲——這正是調動敘述順序會造成的那種無聲行為改變。(b) 二つ。t は最初の文で読まれ、二番目の文で書かれるので、y は t の直前の値に依存する。ツールは t を保持しなければならない。ハードウェア:AND がフリップフロップ t を駆動し、その Q と c の OR を y のレジスタで受ける。(a) に比べてレイテンシが 1 サイクル増えている点に注意——文の並べ替えが引き起こす、あの静かな動作変化である。
(c) Three. A textbook three-stage shift register. All three right-hand sides are sampled at the edge, so q2 gets the old q1.(c) 三顆。教科書式的三級移位暫存器。三個右手邊都在邊緣被取樣,所以 q2 拿到的是舊的 q1。(c) 三つ。教科書どおりの 3 段シフトレジスタ。三つの右辺はすべてエッジでサンプルされるので、q2 が受け取るのは古い q1 である。
(d) One. Blocking assignments execute in order: q1 gets d, then q2 gets the just-written q1 (that is, d), then q3 gets d too. The tool infers one flip-flop whose Q drives all three output ports. The classic broken shift register.(d) 一顆。阻斷式指派依序執行:q1 拿到 d,接著 q2 拿到剛寫好的 q1(也就是 d),然後 q3 也拿到 d。工具推論出一顆正反器,它的 Q 驅動三個輸出埠。這就是經典的壞掉的移位暫存器。(d) 一つ。ブロッキング代入は順に実行される。q1 が d を受け、次に q2 が書かれたばかりの q1(すなわち d)を受け、q3 も d を受ける。ツールは一つのフリップフロップを推論し、その Q が三つの出力ポートを駆動する。古典的な壊れたシフトレジスタである。
(e) One, with enable. q1 is not assigned when en is low, so it must hold. In hardware that is either a 2:1 feedback multiplexer in front of a plain DFF, or a library enable flip-flop (DFFEX, SDFFR and friends), or an integrated clock-gating cell if the register bank is wide enough to make gating worthwhile. No latch is created — the block is edge-sensitive.(e) 一顆,附致能。en 為低時 q1 沒有被指派,所以它必須保持。在硬體上那會是一顆普通 DFF 前面加 2:1 回授多工器,或是函式庫的致能型正反器(DFFEX、SDFFR 之類),或者在暫存器排夠寬、閘控划算時換成整合式時脈閘控單元。不會產生閂鎖——這個區塊是邊緣敏感的。(e) 一つ、イネーブル付き。en が低のとき q1 は代入されないので保持が必要になる。ハードウェアとしては、素の DFF の前に 2:1 フィードバック・マルチプレクサを置くか、ライブラリのイネーブル付きフリップフロップ(DFFEX、SDFFR など)を使うか、レジスタ群が十分に広くゲーティングが引き合うなら集積クロックゲーティングセルを使うかである。ラッチは生成されない——このブロックはエッジ感応だからだ。
(f) Synthesis error. q1 has two drivers. A simulator would resolve it by execution order within the time step (non-deterministically, since both blocks are triggered by the same edge), which is exactly the kind of race the synthesisable subset exists to forbid.(f) 合成錯誤。q1 有兩個驅動源。模擬器會依時間步內的執行順序去解(而且是非決定性的,因為兩個區塊都由同一個邊緣觸發),這正是可合成子集之所以要禁止的那種競爭。(f) 合成エラー。q1 にドライバが二つある。シミュレータはタイムステップ内の実行順で解決する(しかも両ブロックが同一エッジで起動されるため非決定的である)が、これこそ合成可能サブセットが禁じるために存在する類のレースである。
Problem: A synthesis report for a module you believed contained a 4-bit counter, a 4-bit holding register and a 3-state FSM shows: 8 instances of DFFRQX1, 2 instances of DLHQX1 (a transparent latch), and one combinational loop warning. Which three separate coding mistakes are most likely, and what would you grep the RTL for?題目:你以為某個模組裡有一個 4 位元計數器、一個 4 位元保持暫存器和一個 3 狀態 FSM,但合成報告顯示:DFFRQX1 8 個、DLHQX1(透通閂鎖)2 個,以及一則組合迴路警告。最可能是哪三個各自獨立的撰碼錯誤?你會在 RTL 裡 grep 什麼?問題:4 ビットカウンタ、4 ビット保持レジスタ、3 状態 FSM が入っているはずのモジュールの合成レポートに、DFFRQX1 が 8 個、DLHQX1(トランスペアレントラッチ)が 2 個、そして組合せループの警告が 1 件出ている。考えられる三つの別々のコーディングミスは何か。また RTL で何を grep するか。
Flip-flop shortfall. Expected 4 (counter) + 4 (register) + 2 (3-state FSM needs $\lceil\log_2 3\rceil = 2$) = 10 sequential elements; the report shows 8 plus 2 latches. So roughly two intended flip-flops became latches and the counts happen to line up, or two state bits were lost. Grep for = inside always @(posedge blocks — blocking assignments in a clocked cascade are the usual cause of a shortfall.正反器數量不足。預期是 4(計數器)+ 4(暫存器)+ 2(3 狀態 FSM 需要 $\lceil\log_2 3\rceil = 2$)= 10 個循序元件;報告顯示 8 個再加 2 個閂鎖。所以大致上是兩顆本該是正反器的東西變成了閂鎖而數字剛好對得上,或是丟了兩個狀態位元。去 grep always @(posedge 區塊裡的 =——時脈串接中的阻斷式指派是數量不足最常見的原因。フリップフロップ不足。期待値は 4(カウンタ)+ 4(レジスタ)+ 2(3 状態 FSM には $\lceil\log_2 3\rceil = 2$ ビット必要)= 10 個の順序素子。レポートは 8 個とラッチ 2 個である。つまり、本来フリップフロップになるはずの二つがラッチになって数が辻褄を合わせているか、状態ビットが二つ失われている。always @(posedge ブロック内の = を grep すること——クロック同期カスケード内のブロッキング代入が不足の典型的な原因である。
Latches. DLHQX1 can only come from a level-sensitive block with an incomplete assignment. The FSM's next-state block is the prime suspect: a case without a default, or a branch that does not assign next_state. Grep for always @( without posedge, then check each one has a default assignment before the case.閂鎖。DLHQX1 只可能來自指派不完整的位準敏感區塊。FSM 的次態區塊是頭號嫌犯:沒有 default 的 case,或是某個分支沒有指派 next_state。去 grep 沒有 posedge 的 always @(,然後檢查每一個在 case 之前都有預設指派。ラッチ。DLHQX1 は、代入が不完全なレベル感応ブロックからしか生まれない。第一の容疑者は FSM の次状態ブロックである。default のない case、あるいは next_state を代入しない分岐だ。posedge を含まない always @( を grep し、それぞれが case の前に既定値の代入を持つか確認すること。
Combinational loop. Almost certainly the same next-state block: if next_state is read and written in a level-sensitive block, or if the output logic feeds back into the next-state logic through a wire rather than through the state register, the tool sees asynchronous feedback. Grep for the FSM state variable appearing on both sides of an = in a non-clocked block.組合迴路。幾乎可以確定是同一個次態區塊:如果 next_state 在位準敏感區塊裡既被讀又被寫,或者輸出邏輯是透過一條連線(而不是透過狀態暫存器)回授進次態邏輯,工具就會看到非同步回授。去 grep FSM 的狀態變數是否在非時脈區塊裡的 = 兩邊同時出現。組合せループ。ほぼ確実に同じ次状態ブロックである。next_state がレベル感応ブロック内で読みも書きもされているか、出力論理が状態レジスタを経ずに配線で次状態論理へ戻っていれば、ツールには非同期フィードバックに見える。非クロックブロック内で FSM の状態変数が = の両辺に現れていないかを grep すること。
All three symptoms are typically one root cause: the next-state behaviour was written level-sensitive but incompletely specified. Adding next_state = state_reg; as the first statement of the block, plus a default in the case, usually clears all of them at once.這三個症狀通常只有一個根因:次態行為被寫成位準敏感,但描述不完整。把 next_state = state_reg; 加為區塊的第一個敘述,再在 case 裡補上 default,通常就能一次清掉全部。三つの症状の根本原因はたいてい一つ、次状態ビヘイビアをレベル感応で書きながら完全に指定していないことである。ブロックの最初の文として next_state = state_reg; を加え、case に default を補えば、たいていは一度にすべて解消する。
Reset is the part of sequential design that looks trivial on a slide and consumes weeks of schedule on a real chip. The RTL difference between the two styles is one line. The consequences reach into area, into every setup path in the design, into clock-tree synthesis, into static timing analysis, into design-for-test, and into a class of silicon failures that only shows up at temperature corners. This section goes through all of it.重置是循序設計中在投影片上看起來無足輕重、在真實晶片上卻吃掉好幾週時程的那一塊。兩種風格在 RTL 上的差別只有一行。後果卻延伸到面積、設計中每一條 setup 路徑、時脈樹合成、靜態時序分析、可測試性設計,以及一整類只在溫度 corner 才現形的矽上失效。本節會把這些全部走一遍。リセットは、スライド上では取るに足らなく見えて実際のチップでは何週間もの日程を食い潰す、順序設計のその部分である。二つの方式の RTL 上の差は 1 行。その帰結は面積、設計中のあらゆるセットアップ経路、クロックツリー合成、スタティックタイミング解析、テスト容易化設計、そして温度コーナーでしか現れない一群のシリコン故障にまで及ぶ。本節ではそのすべてを辿る。
The slide states three rules for a behaviour whose event control contains more than one edge:投影片為事件控制含有多於一個邊緣的行為訂了三條規則:スライドは、イベント制御に複数のエッジを含むビヘイビアについて三つの規則を挙げている。
if statement must be the first statement in the behaviour;if 敘述必須是該行為的第一個敘述;if 文がビヘイビアの最初の文でなければならない。if;同步信號(時脈)在 if 的本體中不會被明確測試;同期用信号(クロック)は if の本体で明示的に判定されない。These are not stylistic preferences, they are the exact shape of the template. Understanding why they exist makes them easy to remember.這些不是風格偏好,而是樣板的精確形狀。理解它們為什麼存在,就很容易記住。これらは文体の好みではなく、テンプレートの正確な形そのものである。なぜ存在するのかを理解すれば覚えるのは簡単だ。
A standard-cell library contains flip-flops with asynchronous clear and/or preset pins. Those pins are not clocked: pulling CLR low forces Q to 0 immediately, regardless of the clock, through a direct path into the feedback loop of the slave latch. There is no logic in front of them except, at most, an inverter for polarity. So when the synthesiser sees always @(posedge clk or posedge rst), it must decide which of the two edge signals is the clock and which is the asynchronous control, and then it must map the asynchronous one onto a pin that has absolute priority and no data.標準單元函式庫裡有附非同步清除與/或預設腳的正反器。那些腳不受時脈控制:把 CLR 拉低會透過一條直接進入從閂鎖回授迴路的路徑,不管時脈如何立刻把 Q 強制為 0。它們前面沒有任何邏輯,頂多一顆調整極性的反相器。所以當合成器看到 always @(posedge clk or posedge rst) 時,它必須決定兩個邊緣信號中哪一個是時脈、哪一個是非同步控制,然後把非同步的那個對映到一支具有絕對優先權且沒有資料的接腳上。標準セルライブラリには、非同期クリアやプリセットのピンを備えたフリップフロップがある。これらのピンはクロックに従わない。CLR を低に引けば、スレーブラッチのフィードバックループへ直接入る経路を通って、クロックに関係なく Q は即座に 0 に強制される。その前段には、せいぜい極性合わせのインバータ以外の論理はない。したがって合成器は always @(posedge clk or posedge rst) を見たとき、二つのエッジ信号のどちらがクロックでどちらが非同期制御かを決め、非同期のほうを絶対優先かつデータを持たないピンへ写像しなければならない。
The template makes that decision mechanical. The first statement must be an if whose condition tests one of the edge signals. That signal is the asynchronous control; the branch body is what the control forces. Everything that remains after the priority chain has tested all the asynchronous controls — the final else — is by elimination the clocked behaviour. The clock is never tested because there is nothing to test: by the time control reaches the last branch, the only reason the behaviour could have woken is the clock edge.樣板讓這個決定變得機械化。第一個敘述必須是一個 if,其條件測試其中一個邊緣信號。那個信號就是非同步控制;分支本體就是該控制所強制的結果。等優先鏈把所有非同步控制都測試完之後剩下來的——也就是最後的 else——以排除法推斷就是時脈行為。時脈從來不被測試,因為根本沒什麼好測:當控制流走到最後一個分支時,這個行為會被喚醒的唯一理由就是時脈邊緣。テンプレートはこの判断を機械的にする。最初の文は、いずれかのエッジ信号を判定する条件をもつ if でなければならない。その信号が非同期制御であり、分岐の本体がその制御の強制する結果である。優先連鎖がすべての非同期制御を判定し終えて残るもの——最後の else——が、消去法によりクロック同期動作ということになる。クロックが判定されないのは、判定すべきものが何もないからだ。制御が最後の分岐に達した時点で、このビヘイビアが起動されえた理由はクロックエッジ以外にない。
Three corollaries follow immediately, and all three are examinable:從這裡立刻推出三個推論,三個都會考:ここから直ちに三つの系が導かれ、どれも試験に出る。
posedge reset must be paired with if (reset) or if (reset == 1'b1); negedge reset_n must be paired with if (!reset_n) or if (reset_n == 1'b0). If you mismatch them — always @(posedge clk or posedge rst) with if (!rst) — the simulation and the synthesised circuit disagree. Most tools reject it with an error such as “cannot determine the asynchronous control condition”; some infer the wrong polarity silently, and then the gate-level simulation fails where the RTL simulation passed.posedge reset 必須搭配 if (reset) 或 if (reset == 1'b1);negedge reset_n 必須搭配 if (!reset_n) 或 if (reset_n == 1'b0)。如果你配錯了——always @(posedge clk or posedge rst) 配上 if (!rst)——模擬和合成出來的電路就會不一致。多數工具會以類似「cannot determine the asynchronous control condition」的錯誤拒絕它;有些則會默默推論出錯誤的極性,然後閘級模擬在 RTL 模擬通過的地方失敗。posedge reset は if (reset) または if (reset == 1'b1) と、negedge reset_n は if (!reset_n) または if (reset_n == 1'b0) と組にしなければならない。組み合わせを誤ると——always @(posedge clk or posedge rst) に if (!rst) を合わせるなど——シミュレーションと合成後の回路が食い違う。多くのツールは「cannot determine the asynchronous control condition」のようなエラーで拒否するが、黙って誤った極性を推論するものもあり、その場合 RTL シミュレーションが通った箇所でゲートレベル・シミュレーションが落ちる。
Second: you cannot put anything before the if. A statement placed ahead of it would have to execute on both the clock edge and the reset edge, which no library cell can do. Third: you cannot have more asynchronous controls than the library has pins. Two edges (clock plus one reset) is always safe; three edges (clock, set and clear) is safe only if the library has a set-reset flip-flop and your priority order matches the cell's. Four edges is nothing at all — the tool will simply refuse.第二:if 之前不能放任何東西。放在它前面的敘述勢必要在時脈邊緣和重置邊緣兩種情況下都執行,而沒有任何函式庫單元做得到。第三:非同步控制的數量不能超過函式庫提供的接腳數。兩個邊緣(時脈加一個重置)永遠安全;三個邊緣(時脈、set 和 clear)只有在函式庫有 set-reset 正反器、而且你的優先次序與該單元一致時才安全。四個邊緣則完全不可能——工具會直接拒絕。第二に、if の前には何も置けない。その手前に置かれた文は、クロックエッジでもリセットエッジでも実行されねばならず、そんなライブラリセルは存在しない。第三に、非同期制御の数はライブラリのピン数を超えられない。二つのエッジ(クロックとリセット一本)は常に安全。三つのエッジ(クロック、セット、クリア)は、ライブラリにセットリセット付きフリップフロップがあり、かつ優先順位がそのセルと一致する場合にのみ安全である。四つのエッジは論外で、ツールはそのまま拒否する。
The slide shows the two four-bit registers that differ only in the event control. Reproduce them carefully, because the body is identical — the entire difference in hardware comes from the sensitivity list.投影片展示了兩個只在事件控制上不同的四位元暫存器。請仔細讀,因為本體完全相同——硬體上的全部差異都來自感應列。スライドには、イベント制御だけが異なる二つの 4 ビットレジスタが示されている。注意深く読んでほしい。本体は同一であり、ハードウェア上の差はすべて感度リストから生じている。
module reg_4_a (Data_in, clock, reset, Data_out);
input [3:0] Data_in;
input clock, reset;
output [3:0] Data_out;
reg [3:0] Data_out;
always @ (posedge clock or posedge reset)
begin
if (reset == 1'b1) Data_out <= 4'b0;
else Data_out <= Data_in;
end
endmodulemodule reg_4_b (Data_in, clock, reset, Data_out);
input [3:0] Data_in;
input clock, reset;
output [3:0] Data_out;
reg [3:0] Data_out;
always @ (posedge clock)
begin
if (reset == 1'b1) Data_out <= 4'b0;
else Data_out <= Data_in;
end
endmoduleOn the original slides the module headers are printed as module reg_4_a (Data_in, clock, reset, Data_out) with no terminating semicolon. That is a transcription slip, not a language feature — a Verilog module header always ends in ;. The versions above are corrected.原始投影片上的模組標頭印成 module reg_4_a (Data_in, clock, reset, Data_out),沒有結尾的分號。那是謄寫時的疏漏,不是語言特性——Verilog 的模組標頭一定以 ; 結尾。上面的版本已經更正。元のスライドではモジュールヘッダが module reg_4_a (Data_in, clock, reset, Data_out) と、末尾のセミコロンなしで印刷されている。これは転記ミスであって言語仕様ではない——Verilog のモジュールヘッダは必ず ; で終わる。上に示した版では訂正してある。
In reg_4_a, reset never appears in the data path. It is wired directly to the CLR pin of four flip-flops. The D pin of each flip-flop is connected straight to the corresponding bit of Data_in; there is no logic between them at all. In reg_4_b, reset is just another data input: the synthesiser builds D = Data_in & ~reset (one AND-with-inverted-input, or a 2:1 multiplexer, per bit) and uses plain flip-flops with no CLR pin.在 reg_4_a 裡,reset 從來不出現在資料路徑上。它直接接到四顆正反器的 CLR 腳。每顆正反器的 D 腳直接接到 Data_in 的對應位元;兩者之間完全沒有邏輯。在 reg_4_b 裡,reset 只是另一個資料輸入:合成器建出 D = Data_in & ~reset(每位元一顆「一端反相的 AND」,或一顆 2:1 多工器),並使用沒有 CLR 腳的普通正反器。reg_4_a では reset はデータパスに一度も現れない。四つのフリップフロップの CLR ピンへ直結される。各フリップフロップの D ピンは Data_in の対応ビットへそのまま接続され、あいだに論理は一切ない。reg_4_b では reset は単なるもう一つのデータ入力である。合成器は D = Data_in & ~reset(ビットあたり反転入力付き AND 一つ、または 2:1 マルチプレクサ一つ)を構築し、CLR ピンのない素のフリップフロップを使う。
reset drives the CLR pin of a flip-flop with asynchronous clear; the D path is untouched. Right: reset becomes ordinary combinational logic in front of a plain flip-flop, lengthening every data path by one gate but leaving the library cell smaller and the reset net an ordinary signal.圖 2 — 同一段 RTL 本體、兩種感應列、兩種不同的 netlist。左:reset 驅動附非同步清除之正反器的 CLR 腳;D 路徑完全沒被動到。右:reset 變成普通正反器前面的組合邏輯,使每一條資料路徑多了一顆閘,但函式庫單元比較小,而重置網路只是一條普通訊號。図 2 — 同一の RTL 本体、二つの感度リスト、二つの異なるネットリスト。左:reset が非同期クリア付きフリップフロップの CLR ピンを駆動し、D 経路には一切触れない。右:reset は素のフリップフロップの前段の普通の組合せ論理となり、全データ経路が 1 ゲート分長くなる代わりに、ライブラリセルは小さく、リセットネットはただの信号で済む。Now the trade-offs, one axis at a time.現在一軸一軸地看取捨。ここからトレードオフを一軸ずつ見ていく。
A flip-flop with asynchronous clear is physically bigger than one without — typically 10–25 % more area in a modern standard-cell library, because the clear transistor stack has to be strong enough to overpower the feedback inverter in the slave latch. A synchronous-reset design uses the smaller plain cell but pays for a gate per bit in front of it. Which wins depends on the library and the fanin: for a wide register with nothing else in its D cone, the extra AND gates can cost more than the bigger flip-flops; for a register whose D input is already the output of a deep logic cone, the synthesiser can often absorb the reset term into the existing last-level gate for free. The honest answer is “measure it”, but as a rule of thumb the two styles land within a few per cent of each other on area.附非同步清除的正反器在實體上比沒有的大——在現代標準單元函式庫裡通常多出 10–25 % 的面積,因為清除用的電晶體堆疊必須強到足以壓過從閂鎖裡的回授反相器。同步重置的設計用的是比較小的普通單元,但每一位元前面要付出一顆閘。哪一邊划算取決於函式庫和扇入:對於 D 錐裡什麼都沒有的寬暫存器,多出來的 AND 閘可能比較大的正反器還貴;對於 D 輸入本來就是一個深邏輯錐輸出的暫存器,合成器往往可以免費把重置項吸收進既有的最後一層閘裡。誠實的回答是「量一量」,但經驗法則是:兩種風格在面積上的差距通常落在百分之幾以內。非同期クリア付きフリップフロップは、ない場合より物理的に大きい——現代の標準セルライブラリでは面積にして通常 10–25 % 増である。クリア用のトランジスタ列が、スレーブラッチのフィードバック・インバータを押し切れるだけの強さを要するからだ。同期リセット方式は小さな素のセルを使うが、ビットあたり 1 ゲートを前段に支払う。どちらが勝つかはライブラリとファンインによる。D コーンに他に何もない広いレジスタでは、追加の AND ゲートのほうが大きなフリップフロップより高くつくことがある。D 入力がすでに深い論理コーンの出力であるレジスタなら、合成器はリセット項を既存の最終段ゲートに無償で吸収できることが多い。正直な答えは「測れ」だが、経験則としては両方式の面積差は数パーセント以内に収まる。
This is where the difference is real. The setup constraint for a flip-flop pair is差異真正發生作用的是這裡。一對正反器的 setup 約束是本当に差が出るのはここである。フリップフロップ対のセットアップ制約は次式で与えられる。
With synchronous reset, the reset gate is part of $t_{\text{logic}}$ on every single path into every single register. If the reset gate costs 60 ps and you are closing timing at a 1 ns period, you have just given away 6 % of the cycle, chip-wide, to a signal that is inactive for all but the first microsecond of the chip's life. With asynchronous reset, $t_{\text{logic}}$ is untouched: reset enters through a pin that is not in the D-to-Q path at all. For a design that is timing-critical everywhere, this alone decides the question.用同步重置時,重置閘是每一條進入每一顆暫存器的路徑上 $t_{\text{logic}}$ 的一部分。如果重置閘要花 60 ps,而你正在 1 ns 的週期下收時序,你就等於把整顆晶片 6 % 的週期,送給了一個除了晶片生命最初一微秒以外都不作用的訊號。用非同步重置時,$t_{\text{logic}}$ 完全沒被動到:重置是從一支根本不在 D 到 Q 路徑上的接腳進去的。對一個處處都時序吃緊的設計來說,光這一點就能決定答案。同期リセットでは、リセットゲートはすべてのレジスタへ入るすべての経路上で $t_{\text{logic}}$ の一部になる。リセットゲートが 60 ps かかり、1 ns の周期でタイミングを収束させようとしているなら、チップの寿命の最初の 1 マイクロ秒を除いてずっと非活性な信号に、サイクルの 6 % をチップ全体で明け渡したことになる。非同期リセットなら $t_{\text{logic}}$ は手つかずだ。リセットは D-Q 経路にまったく含まれないピンから入る。どこもかしこもタイミングが厳しい設計では、この一点だけで答えが決まる。
The asynchronous style buys its timing advantage by creating a second enormous net. Every flip-flop in the design has its CLR pin connected to reset, so the reset net has the same fanout as the clock. It must be buffered into a tree, that tree consumes routing resources and cells, and — the part people forget — the tree has skew. Two flip-flops at opposite corners of a block may see the reset edge hundreds of picoseconds apart. For the assertion edge that is harmless: everybody gets cleared, just not at exactly the same instant, and nothing is sampling. For the deassertion edge it is a correctness problem, which is the whole of Sections 2.4 and 2.5.非同步風格是靠製造出第二棵巨大的網路,才買到它的時序優勢。設計中每一顆正反器的 CLR 腳都接到 reset,所以重置網路的扇出和時脈一樣大。它必須被緩衝成一棵樹,那棵樹吃掉繞線資源和單元,而且——大家最容易忘記的部分——那棵樹有歪斜。位於區塊兩個對角的兩顆正反器,看到重置邊緣的時間可能差到數百皮秒。對確立邊緣而言這沒有害處:大家都被清除,只是不在同一瞬間,而且當下沒有人在取樣。對解除邊緣而言這就是正確性問題,而這正是 2.4 與 2.5 兩節的全部內容。非同期方式は、巨大な第二のネットを作ることでタイミング上の優位を買っている。設計中のすべてのフリップフロップの CLR ピンが reset に接続されるので、リセットネットのファンアウトはクロックと同じになる。これはツリーにバッファリングされねばならず、そのツリーは配線資源とセルを消費し、そして——みなが忘れる点だが——ツリーにはスキューがある。ブロックの対角にある二つのフリップフロップは、リセットのエッジを数百ピコ秒ずれて見ることがある。アサート側のエッジではこれは無害だ。全員がクリアされ、ただ同じ瞬間ではないというだけで、誰もサンプルしていない。デアサート側のエッジでは正しさの問題になる。それが 2.4 節と 2.5 節のすべてである。
With synchronous reset the net is still high fanout, but it is an ordinary timed signal: the tool buffers it like any other, static timing analysis constrains it like any other, and skew on it is absorbed by the normal setup/hold machinery.用同步重置時,那條網路一樣是高扇出,但它是一條普通的受時序約束的訊號:工具像對待其他訊號一樣把它緩衝,靜態時序分析像對待其他訊號一樣約束它,而它上面的歪斜會被一般的 setup/hold 機制吸收掉。同期リセットでも、そのネットは依然として高ファンアウトだが、普通にタイミングを取られる信号である。ツールは他の信号と同様にバッファリングし、スタティックタイミング解析も他と同様に制約し、そのスキューは通常の setup/hold の仕組みが吸収する。
An asynchronous reset works when the clock is not running. That matters more than it sounds. At power-up the PLL has not locked; during a low-power shutdown the clock is gated off; during a hardware recovery from a hung state the clock source may be the thing that failed. In all of those situations a synchronous reset does nothing at all, because there is no edge to carry it. This is the single strongest argument for an asynchronous assertion, and it is why nearly every real ASIC uses asynchronous assert plus synchronous release (Section 2.6).非同步重置在時脈沒在跑的時候照樣有效。這件事比聽起來重要得多。上電時 PLL 還沒鎖定;低功耗關斷期間時脈被閘掉;從當掉狀態做硬體復原時,時脈源本身可能就是壞掉的那個。在上述所有情況下,同步重置什麼也做不到,因為沒有邊緣可以載送它。這是非同步確立最強的一項論據,也是為什麼幾乎每一顆真實的 ASIC 都採用非同步確立加同步釋放(2.6 節)。非同期リセットはクロックが動いていなくても効く。これは聞こえ以上に重要だ。電源投入時には PLL がまだロックしていない。低消費電力のシャットダウン中はクロックがゲートで止められている。ハングからのハードウェア復帰では、クロック源そのものが壊れた当人かもしれない。これらいずれの場面でも、同期リセットはまったく何もできない。運んでくれるエッジが存在しないからだ。これが非同期アサートを支持する最強の論拠であり、ほぼすべての実用 ASIC が非同期アサート+同期リリース(2.6 節)を採る理由である。
During scan shift, the asynchronous reset must be held inactive, otherwise shifting a pattern through the chain would be destroyed by a stray reset pulse. DFT insertion therefore adds a multiplexer or an AND gate into the reset tree, controlled by the test mode pin. This is standard and automated, but it means the reset tree is not a straight wire in the final netlist, and it adds a constraint: the reset must be controllable from a primary input. A synchronous reset needs no such treatment — it is just data, and scan handles data.在掃描移位期間,非同步重置必須被維持在非作用狀態,否則一道雜散的重置脈衝就會毀掉正在鏈中移位的圖樣。因此 DFT 插入會在重置樹裡加一顆多工器或一顆 AND 閘,由測試模式接腳控制。這是標準且自動化的流程,但它意味著最終 netlist 裡的重置樹不是一條直通的線,而且多了一項約束:重置必須能從主要輸入控制。同步重置不需要這種處理——它只是資料,而掃描本來就在處理資料。スキャンシフト中、非同期リセットは非活性に保たれねばならない。さもないとチェーンを通しているパターンが迷子のリセットパルスで破壊される。そのため DFT 挿入はリセットツリーにテストモードピンで制御されるマルチプレクサか AND ゲートを追加する。これは標準的で自動化された処理だが、最終ネットリストのリセットツリーが素通しの配線ではないことを意味し、「リセットは一次入力から制御可能でなければならない」という制約も加わる。同期リセットにはそうした処置は要らない——ただのデータであり、スキャンはデータを扱うものだからだ。
A gate-level simulation that starts with all flip-flops at x will clean up as soon as an asynchronous reset is applied, before any clock edge. With synchronous reset you need at least one clock edge, and if any part of the design has its clock gated off at that moment, those flip-flops stay x and the x spreads. Teams that use synchronous reset often spend real effort on an “X-propagation” cleanup plan for exactly this reason.一場所有正反器都從 x 開始的閘級模擬,只要一加上非同步重置就會乾淨下來,根本不用等任何時脈邊緣。用同步重置的話你至少需要一個時脈邊緣,而如果設計中某部分在那一刻的時脈正被閘掉,那些正反器就會維持 x,而 x 會擴散出去。採用同步重置的團隊常常為了這個理由在「X 傳播」清理計畫上花掉實實在在的力氣。全フリップフロップが x から始まるゲートレベル・シミュレーションは、非同期リセットを与えた瞬間に、クロックエッジを待たずに片付く。同期リセットでは最低一つのクロックエッジが要り、そのとき設計の一部でクロックがゲートされていれば、そこのフリップフロップは x のまま残り、x は広がっていく。同期リセットを使うチームがまさにこの理由で「X 伝播」対策に実質的な労力を割くのはよくあることだ。
| Axis面向観点 | Asynchronous reset非同步重置非同期リセット | Synchronous reset同步重置同期リセット |
|---|---|---|
| Event control事件控制イベント制御 | posedge clock or posedge reset | posedge clock only只有 posedge clockposedge clock のみ |
| Where reset enters重置從哪裡進入リセットの入口 | CLR/PRE pin of each flip-flop每顆正反器的 CLR/PRE 腳各フリップフロップの CLR/PRE ピン | Ordinary logic in front of DD 前面的普通邏輯D の前段の通常論理 |
| Library cell函式庫單元ライブラリセル | Larger (DFFR / DFFS)較大(DFFR/DFFS)大きい(DFFR/DFFS) | Smaller (plain DFF)較小(普通 DFF)小さい(素の DFF) |
| Effect on $t_{\text{logic}}$對 $t_{\text{logic}}$ 的影響$t_{\text{logic}}$ への影響 | None無なし | +1 gate on every path每條路徑多一顆閘全経路に 1 ゲート追加 |
| Reset net重置網路リセットネット | Clock-like tree, must be skew-managed類時脈的樹,必須管理歪斜クロック同様のツリー、スキュー管理が必要 | Ordinary timed signal普通的受時序約束訊號通常のタイミング付き信号 |
| Works with clock stopped時脈停止時仍可用クロック停止時も動作するか | Yes可以する | No不行しない |
| Minimum pulse width最小脈寬最小パルス幅 | Only the cell's $t_{w(\text{rst})}$ — can be far shorter than a clock period只受單元自身的 $t_{w(\text{rst})}$ 限制 — 可以遠短於一個時脈週期セル自身の $t_{w(\text{rst})}$ のみ — クロック周期よりはるかに短くてよい | Must still be high at a rising clock edge: in practice > one period必須在某個時脈上升緣時仍為高:實務上 > 一個週期立ち上がりクロックエッジの時点でまだ高でなければならない:実務上は 1 周期超 |
| STA checks needed需要的 STA 檢查必要な STA チェック | Recovery and removal on the CLR pinCLR 腳上的 recovery 與 removalCLR ピンのリカバリとリムーバル | Ordinary setup and hold普通的 setup 與 hold通常のセットアップとホールド |
| DFT | Needs a test-mode disable in the tree樹裡需要一個測試模式的停用機制ツリー内にテストモード無効化が必要 | Nothing special不需特別處理特別な処置は不要 |
| Noise sensitivity雜訊敏感度ノイズ感受性 | A glitch on the reset net resets the chip重置網路上的一道毛刺就會重置整顆晶片リセットネット上のグリッチ一つでチップがリセットされる | A glitch is filtered unless it lands at a clock edge毛刺會被濾掉,除非它剛好落在時脈邊緣グリッチはクロックエッジに重ならない限り濾過される |
An asynchronous reset is asynchronous when it asserts. When it deasserts, the flip-flop is about to resume normal clocked operation, and the internal race between the clearing path and the clocking path reopens. Two timing checks guard that moment, and they are the exact mirror images of setup and hold:非同步重置在確立的時候是非同步的。當它解除時,正反器即將恢復正常的時脈運作,而清除路徑與時脈路徑之間的內部競爭又重新打開。有兩項時序檢查守著這一刻,而它們正好是 setup 與 hold 的鏡像:非同期リセットが非同期なのはアサートするときである。デアサートするとき、フリップフロップは通常のクロック動作に戻ろうとしており、クリア経路とクロック経路のあいだの内部レースが再び開く。その瞬間を守るタイミングチェックが二つあり、それぞれ setup と hold のちょうど鏡像になっている。
Read them as a pair. Recovery says: if you want the clock edge to take effect normally, the reset must have been released at least $t_{rec}$ before the edge, so that the internal clear node has had time to settle. It is a setup check on the asynchronous pin. Removal says: if you want the reset to take effect and not be disturbed, the reset must stay asserted for at least $t_{rem}$ after the edge. It is a hold check on the asynchronous pin.請把兩者當成一對來讀。Recovery 說:如果你要讓時脈邊緣正常生效,重置必須在邊緣之前至少 $t_{rec}$ 就已經被釋放,好讓內部的清除節點有時間安定。它是非同步接腳上的 setup 檢查。Removal 說:如果你要讓重置生效而且不被干擾,重置必須在邊緣之後至少再維持 $t_{rem}$ 的確立。它是非同步接腳上的 hold 檢查。この二つは対にして読むこと。リカバリはこう言う。クロックエッジを正常に効かせたいなら、内部のクリアノードが落ち着く時間を確保するため、リセットはエッジの少なくとも $t_{rec}$ 前に解除されていなければならない。これは非同期ピンに対するセットアップ検査である。リムーバルはこう言う。リセットを確実に効かせ乱されたくないなら、リセットはエッジの後少なくとも $t_{rem}$ はアサートされたままでなければならない。これは非同期ピンに対するホールド検査である。
Between the two lies a forbidden window of width $t_{rec} + t_{rem}$ around every active clock edge. A reset deassertion that lands inside that window leaves the flip-flop in an indeterminate condition: it may come out of reset on this edge, it may come out on the next one, or it may go metastable and resolve to either value some unpredictable time later.兩者之間,在每一個作用時脈邊緣周圍形成一個寬度為 $t_{rec} + t_{rem}$ 的禁區。落在這個窗口內的重置解除,會讓正反器處於不確定狀態:它可能在這個邊緣脫離重置,可能在下一個邊緣才脫離,也可能進入亞穩態,並在某個不可預測的時間後安定到任一值。両者のあいだには、すべての有効クロックエッジの周りに幅 $t_{rec} + t_{rem}$ の禁止窓がある。その窓の内側に入るリセット解除は、フリップフロップを不定状態に残す。このエッジでリセットから抜けるかもしれないし、次のエッジかもしれないし、メタステーブルになって予測不能な時間ののちにどちらかの値へ落ち着くかもしれない。
In a static timing report, these appear as recovery and removal arcs on the CLR or PRE pin, with the usual slack columns. They are reported only if the reset is a timed signal, which is to say only if it is released synchronously. If you drive the reset straight from a chip pin, the tool has nothing to time it against, and you will typically see the path reported as false_path or simply not reported — which is comfortable and wrong.在靜態時序報告裡,它們會以 CLR 或 PRE 腳上的 recovery 與 removal 弧出現,附上一般的 slack 欄位。只有當重置是一條受時序約束的訊號時才會被報出來,也就是說,只有當它是同步釋放的時候。如果你讓重置直接由晶片接腳驅動,工具沒有東西可以拿來計時,你通常會看到那條路徑被報成 false_path,或根本不被報出來——那很舒服,而且是錯的。スタティックタイミングレポートでは、これらは CLR または PRE ピン上の recovery / removal アークとして、通常のスラック欄とともに現れる。報告されるのはリセットがタイミングの取れた信号であるとき、すなわち同期的に解除されるときだけである。リセットをチップピンから直接駆動すると、ツールには基準にすべきものがなく、その経路はたいてい false_path と報告されるか、そもそも報告されない——心地よいが、間違っている。
Now put the recovery window together with the reset-tree skew of Section 2.3, and the real hazard appears.現在把 recovery 窗口和 2.3 節的重置樹歪斜放在一起,真正的危險就出現了。ここでリカバリ窓と 2.3 節のリセットツリー・スキューを重ね合わせると、本当の危険が姿を現す。
Suppose an external pin releases reset at some arbitrary moment, uncorrelated with the clock. Three things can go wrong at once.假設某支外部接腳在某個與時脈無關的任意時刻釋放重置。三件事會同時出錯。外部ピンがクロックと無相関の任意の時点でリセットを解除するとしよう。三つのことが同時に起こりうる。
First, individual metastability. Any flip-flop whose reset release lands inside its recovery/removal window can go metastable. The output hovers near the switching threshold for a time that is unbounded in principle and exponentially distributed in practice. The mean time between failures for a single such event follows the usual form第一,個別的亞穩態。任何一顆正反器,只要它看到的重置釋放落在自己的 recovery/removal 窗口裡,就可能進入亞穩態。輸出會在切換門檻附近徘徊一段原則上無上限、實務上呈指數分布的時間。單一這類事件的平均失效間隔時間遵循常見的形式第一に、個々のメタステーブル。リセット解除が自分のリカバリ/リムーバル窓に入ったフリップフロップは、どれもメタステーブルになりうる。出力はスイッチング閾値付近を、原理的には上限のない、実際には指数分布に従う時間だけ漂う。この種の事象一つあたりの平均故障間隔は、おなじみの形をとる。
where $t_r$ is the resolution time you allow before the value is used, $\tau$ is the flip-flop's regeneration time constant and $T_0$ its metastability window parameter. The practical point is the exponential: every extra $\tau$ of settling time multiplies the MTBF by $e$. One extra clock period of slack is usually worth many orders of magnitude.其中 $t_r$ 是你在使用該值之前允許的解析時間,$\tau$ 是正反器的再生時間常數,$T_0$ 是它的亞穩態窗口參數。實務上的重點在那個指數:每多給一個 $\tau$ 的安定時間,MTBF 就乘上 $e$。多留一個時脈週期的餘裕,通常就值好幾個數量級。ここで $t_r$ は値を使う前に許す分解時間、$\tau$ はフリップフロップの再生時定数、$T_0$ はそのメタステーブル窓パラメータである。実務上の要点は指数にある。整定時間を $\tau$ 一つ分増やすごとに MTBF は $e$ 倍になる。クロック 1 周期分の余裕を足すだけで、たいてい何桁も効く。
Take a characterised flip-flop with $\tau = 25$ ps and $T_0 = 10$ fs, a 500 MHz clock, and an asynchronous event arriving on average 1000 times per second. The denominator is $T_0 f_{\text{clk}} f_{\text{data}} = 10^{-14} \times 5\times10^{8} \times 10^{3} = 5\times10^{-3}\ \text{s}^{-1}$.取一顆已特性化的正反器,$\tau = 25$ ps、$T_0 = 10$ fs,時脈 500 MHz,非同步事件平均每秒到來 1000 次。分母是 $T_0 f_{\text{clk}} f_{\text{data}} = 10^{-14} \times 5\times10^{8} \times 10^{3} = 5\times10^{-3}\ \text{s}^{-1}$。特性化済みのフリップフロップで $\tau = 25$ ps、$T_0 = 10$ fs、クロック 500 MHz、非同期イベントが平均して毎秒 1000 回到来するとする。分母は $T_0 f_{\text{clk}} f_{\text{data}} = 10^{-14} \times 5\times10^{8} \times 10^{3} = 5\times10^{-3}\ \text{s}^{-1}$ である。
The shape of the result is always the same: zero synchronisers is hopeless, one is poor, two is overwhelmingly sufficient, and three is insurance. That is why “two flip-flops” has become a design rule rather than a calculation anyone repeats.結果的形狀總是一樣的:零級同步器毫無希望,一級很差,兩級綽綽有餘,三級是保險。這就是為什麼「兩顆正反器」已經變成一條設計規則,而不是誰還會重算一次的計算。結果の形はいつも同じだ。同期化段数ゼロは論外、1 段は不十分、2 段は圧倒的に十分、3 段は保険である。だからこそ「フリップフロップ 2 段」は、誰かが計算し直すものではなく設計規則になった。
Second, skewed release. Even if no flip-flop goes metastable, the reset tree's skew means different flip-flops see the release at different times relative to the same clock edge. Flip-flop A, early in the tree, sees release 200 ps before the edge and comes out of reset on that edge. Flip-flop B, late in the tree, sees release 100 ps after the edge and stays reset for one more cycle. The two are now one cycle out of step.第二,歪斜的釋放。就算沒有任何一顆正反器進入亞穩態,重置樹的歪斜仍意味著不同的正反器相對於同一個時脈邊緣,在不同時刻看到釋放。樹中位置較前的正反器 A 在邊緣前 200 ps 看到釋放,於是在那個邊緣脫離重置。位置較後的正反器 B 在邊緣後 100 ps 才看到釋放,於是又多被重置了一個週期。兩者現在差了一個週期。第二に、ずれた解除。どのフリップフロップもメタステーブルにならなかったとしても、リセットツリーのスキューにより、同じクロックエッジに対して解除を見る時刻がフリップフロップごとに異なる。ツリーの手前にあるフリップフロップ A はエッジの 200 ps 前に解除を見て、そのエッジでリセットを抜ける。奥にあるフリップフロップ B はエッジの 100 ps 後に解除を見るので、もう 1 サイクル長くリセットされたままになる。両者は 1 サイクルずれてしまう。
Third, illegal states. For a datapath register, a one-cycle disagreement usually just corrupts one sample. For a state machine it can be fatal. If a 5-state FSM uses a 3-bit binary encoding and two of its three state bits leave reset a cycle before the third, the machine can start in a code that is not any of its five states. If the case has a sensible default it will recover on the next edge; if it does not, it has latched into a region of the state graph that the designer never drew, and only another reset will get it out. With one-hot encoding this is worse, because there are far more illegal codes.第三,非法狀態。對資料路徑暫存器而言,差一個週期通常只是弄壞一個取樣。對狀態機而言卻可能致命。如果一台 5 狀態的 FSM 用 3 位元二進位編碼,而三個狀態位元中有兩個比第三個早一個週期離開重置,這台機器就可能從一個不屬於它那五個狀態的碼開始跑。如果 case 有合理的 default,它會在下一個邊緣復原;如果沒有,它就鎖進了設計者從未畫過的那塊狀態圖區域,只有再一次重置才能把它弄出來。用 one-hot 編碼時更糟,因為非法碼的數量多得多。第三に、不正状態。データパスのレジスタなら、1 サイクルの食い違いはたいていサンプル一つを壊すだけだ。状態機械では致命的になりうる。5 状態の FSM が 3 ビット二進符号を使い、3 本の状態ビットのうち 2 本が残りの 1 本より 1 サイクル早くリセットを抜けると、機械は五つの状態のどれでもない符号から動き出すことがある。case にまともな default があれば次のエッジで復帰するが、なければ設計者が描いたことのない状態グラフの領域にラッチされ、もう一度リセットするまで抜け出せない。one-hot 符号化では不正符号がはるかに多いぶん、さらに悪い。
Reset-release problems do not fail in simulation, because an RTL simulator releases reset everywhere at the same instant and has no concept of recovery time. They do not usually fail in gate-level simulation either, unless the SDF back-annotation includes recovery/removal checks and the testbench happens to deassert reset in the window. They fail in the lab, intermittently, on a small fraction of parts, more often cold than hot, and they are sensitive to exactly where the reset deasserts relative to the clock. If a board “sometimes comes up in a weird state and a second reset fixes it”, suspect this.重置釋放的問題不會在模擬中失敗,因為 RTL 模擬器會在同一瞬間到處釋放重置,而且根本沒有 recovery 時間的概念。它們通常也不會在閘級模擬中失敗,除非 SDF 回標含有 recovery/removal 檢查,而且測試平台剛好在那個窗口裡解除重置。它們是在實驗室裡失敗的:間歇性地、只發生在一小部分晶片上、冷的時候比熱的時候多,而且對重置相對於時脈在哪裡解除極度敏感。如果一塊板子「有時候開機進到奇怪的狀態,再按一次重置就好了」,就懷疑這個。リセット解除の問題はシミュレーションでは落ちない。RTL シミュレータはどこでも同じ瞬間にリセットを解除し、リカバリ時間という概念をもたないからだ。ゲートレベル・シミュレーションでもたいてい落ちない。SDF の逆アノテーションにリカバリ/リムーバル検査が含まれ、かつテストベンチがたまたまその窓でリセットを解除しない限りは。落ちるのは実験室である。間欠的に、ごく一部の個体で、高温より低温で多く、そしてリセットがクロックに対してどこで解除されるかに極端に敏感に。ボードが「ときどき妙な状態で立ち上がり、もう一度リセットすると直る」なら、これを疑うこと。
The fix is well established and costs two flip-flops per clock domain. Assert the reset asynchronously, so that it works with no clock; release it synchronously, so that every flip-flop in the domain leaves reset on the same clock edge with a clean recovery margin.修法早已確立,代價是每個時脈域兩顆正反器。以非同步方式確立重置,讓它在沒有時脈時也有效;以同步方式釋放它,讓該域中每一顆正反器都在同一個時脈邊緣、帶著乾淨的 recovery 餘裕離開重置。対処法は確立されており、コストはクロックドメインあたりフリップフロップ 2 個である。リセットは非同期にアサートしてクロックなしでも効くようにし、解除は同期的に行って、そのドメインのすべてのフリップフロップが同一のクロックエッジで、きれいなリカバリ余裕をもってリセットを抜けるようにする。
module reset_sync ( input clk, // the destination domain's clock input arst_n, // raw asynchronous reset, active low output rst_n // clean reset for this domain ); reg sync0, sync1; always @ (posedge clk or negedge arst_n) if (!arst_n) begin sync0 <= 1'b0; sync1 <= 1'b0; end else begin sync0 <= 1'b1; // a constant 1 walks down the chain sync1 <= sync0; end assign rst_n = sync1; endmodule
Trace what happens. When arst_n falls, both flip-flops clear immediately, with no clock required, so rst_n falls immediately too: assertion is asynchronous. When arst_n rises, the two flip-flops are released from their CLR pins at some arbitrary moment, but their D inputs are a hard-wired 1'b1 and sync0 respectively. The constant 1 now has to be clocked through: sync0 takes it on the first clock edge after release, sync1 on the second. The output rst_n therefore rises on a clock edge, with a full clock period of margin, and every flip-flop fed by it sees the release as a timed, synchronous event: deassertion is synchronous.把過程追一遍。當 arst_n 下降時,兩顆正反器立刻被清除,不需要時脈,所以 rst_n 也立刻下降:確立是非同步的。當 arst_n 上升時,兩顆正反器在某個任意時刻從 CLR 腳被釋放,但它們的 D 輸入分別是硬接的 1'b1 與 sync0。那個常數 1 現在必須被時脈一級一級推過去:sync0 在釋放後的第一個時脈邊緣拿到它,sync1 在第二個邊緣拿到。因此輸出 rst_n 是在某個時脈邊緣上升的,帶著完整一個時脈週期的餘裕,而每一顆由它餵入的正反器看到的都是一個受時序約束的同步事件:解除是同步的。動作を追ってみよう。arst_n が下がると、二つのフリップフロップはクロックなしで直ちにクリアされ、rst_n も直ちに下がる。アサートは非同期である。arst_n が上がると、二つのフリップフロップは任意の時点で CLR ピンから解放されるが、その D 入力はそれぞれハードワイヤの 1'b1 と sync0 である。定数 1 はこれからクロックで送り込まれねばならない。sync0 は解除後の最初のクロックエッジでそれを取り込み、sync1 は二番目のエッジで取り込む。したがって出力 rst_n はクロックエッジ上で、丸 1 周期の余裕をもって立ち上がり、それを受けるすべてのフリップフロップにとって解除はタイミングの取れた同期事象となる。デアサートは同期である。
1'b1 at the head of the chain makes deassertion take two clock edges, giving the first stage a full period to resolve any metastability before rst_n moves.圖 3 — 非同步確立/同步解除的重置同步器。鏈本身採用非同步重置,使確立不需要時脈;鏈頭硬接的 1'b1 讓解除必須經過兩個時脈邊緣,使第一級在 rst_n 動作之前有完整一個週期可以解開任何亞穩態。図 3 — 非同期アサート/同期デアサートのリセット同期化回路。チェーン自体が非同期リセットを使うのでアサートにクロックは要らない。チェーン先頭にハードワイヤされた 1'b1 により解除はクロックエッジ 2 回分かかり、rst_n が動く前に初段が丸 1 周期かけてメタステーブルを解消できる。Two details are easy to get wrong.有兩個細節很容易弄錯。間違えやすい点が二つある。
Why two flip-flops and not one? The first flip-flop's CLR pin is released asynchronously, so it can violate its own recovery time and go metastable. That is unavoidable — something has to be the first thing to see the asynchronous edge. The job of the second flip-flop is to give the first a full clock period to resolve. Plugging $t_r \approx T_{\text{clk}}$ into the MTBF formula is what turns “fails occasionally” into “fails once per geological age”. Fast clocks or aggressive corners sometimes justify three stages.為什麼是兩顆而不是一顆正反器?第一顆正反器的 CLR 腳是被非同步釋放的,所以它自己可能違反自己的 recovery 時間而進入亞穩態。這是躲不掉的——總得有某個東西第一個看到那個非同步邊緣。第二顆正反器的任務就是給第一顆完整一個時脈週期去解開。把 $t_r \approx T_{\text{clk}}$ 代進 MTBF 公式,就是把「偶爾會失效」變成「每一個地質年代失效一次」。快時脈或嚴苛的 corner 有時能正當化三級。なぜ 1 個でなく 2 個なのか。初段フリップフロップの CLR ピンは非同期に解放されるので、それ自身が自分のリカバリ時間を破ってメタステーブルになりうる。これは避けられない——何かが最初に非同期エッジを見る役を負わねばならない。二段目の役目は、初段に丸 1 周期の解消時間を与えることである。MTBF 式に $t_r \approx T_{\text{clk}}$ を代入すれば、「ときどき落ちる」が「地質年代に一度落ちる」に変わる。高速クロックや厳しいコーナーでは 3 段が正当化されることもある。
One synchroniser per clock domain. A chip with four clocks needs four of these, each clocked by its own domain's clock, all fed from the same raw arst_n. Sharing one synchroniser across domains re-creates exactly the problem it was built to solve.每個時脈域一個同步器。一顆有四個時脈的晶片需要四組,各自由自己那個域的時脈驅動,全部餵自同一條原始的 arst_n。跨域共用一組同步器,等於把它當初要解決的問題原封不動地再造一次。同期化回路はクロックドメインごとに一つ。クロックが四つあるチップには四組が必要で、それぞれ自ドメインのクロックで駆動し、すべて同じ生の arst_n から給電する。一組をドメイン間で共用すれば、それが解決するはずだった問題をそっくり再生産することになる。
Once this is in place, the rest of the design uses the clean rst_n in the ordinary asynchronous style, always @(posedge clk or negedge rst_n), and static timing analysis can finally check recovery and removal against a real clock, because the reset now originates at a flip-flop clocked by that clock.這套機制就位之後,設計的其餘部分就用乾淨的 rst_n、以一般的非同步風格 always @(posedge clk or negedge rst_n) 撰寫,而靜態時序分析終於可以對著一個真正的時脈檢查 recovery 與 removal,因為重置現在起源於一顆由該時脈驅動的正反器。この仕組みが入れば、設計の残りはきれいな rst_n を使い、通常の非同期スタイル always @(posedge clk or negedge rst_n) で書けばよい。そしてスタティックタイミング解析はようやく実在のクロックに対してリカバリとリムーバルを検査できる。リセットが、そのクロックで駆動されるフリップフロップから発するようになったからだ。
The slide's two waveforms make the pulse-width point concretely. Both circuits have active-high reset, Data_in = 4'd8 held constant, and Data_out starting at 4'dx.投影片的兩張波形把脈寬這件事講得很具體。兩個電路都是高位準作用的重置,Data_in = 4'd8 維持不變,Data_out 從 4'dx 開始。スライドの二つの波形はパルス幅の論点を具体的に示している。どちらの回路も正論理リセットで、Data_in = 4'd8 は一定、Data_out は 4'dx から始まる。
Data_out the instant it rises, with no clock edge involved. On the right the reset must still be high when a rising clock edge arrives, which is why its pulse has to be wider than one clock period; Data_out then reloads 4'd8 at the first edge after the reset falls.圖 4 — 重置波形。左邊那道窄窄的重置脈衝一上升就立刻清除 Data_out,完全不牽涉時脈邊緣。右邊的重置必須在時脈上升緣到來時仍然為高,這就是為什麼它的脈衝必須比一個時脈週期更寬;之後 Data_out 在重置下降後的第一個邊緣重新載入 4'd8。図 4 — リセット波形。左では細いリセットパルスが立ち上がった瞬間に Data_out をクリアし、クロックエッジは一切関与しない。右ではリセットが立ち上がりクロックエッジの時点でまだ高でなければならず、だからパルス幅はクロック 1 周期より広い必要がある。その後 Data_out はリセットが下がった後の最初のエッジで 4'd8 を読み直す。The asymmetry is worth stating plainly, because it is the examinable point. For the asynchronous register the reset pulse can be arbitrarily narrow — down to the library cell's own minimum reset pulse width, which is typically a few tens of picoseconds. For the synchronous register the pulse must contain an active clock edge, with the usual setup and hold margins around it. Since you do not in general know the phase of an external reset relative to the clock, the only safe rule is “longer than one clock period”, and in practice people specify several periods.這個不對稱值得明講,因為它是會考的重點。對非同步暫存器而言,重置脈衝可以任意窄——窄到函式庫單元自身的最小重置脈寬為止,通常是幾十皮秒。對同步暫存器而言,脈衝必須包含一個作用時脈邊緣,而且前後要有通常的 setup 與 hold 餘裕。既然你一般並不知道外部重置相對於時脈的相位,唯一安全的規則就是「比一個時脈週期長」,而實務上大家會規定好幾個週期。この非対称性ははっきり述べておく価値がある。試験に出る要点だからだ。非同期レジスタではリセットパルスはいくらでも細くてよい——ライブラリセル自身の最小リセットパルス幅、通常は数十ピコ秒まで。同期レジスタではパルスは有効クロックエッジを含み、その前後に通常の setup と hold の余裕を要する。外部リセットのクロックに対する位相は一般に分からないので、安全な規則は「クロック 1 周期より長く」だけであり、実務では数周期と規定する。
Notice also what happens after release. In both cases Data_out returns to 4'd8 at the first clock edge for which the reset is inactive. The asynchronous register spends a sub-cycle interval at 4'd0; the synchronous one spends a full cycle there. That difference matters if anything downstream is counting cycles.也請注意釋放之後會發生什麼。兩種情況下,Data_out 都在重置已非作用的第一個時脈邊緣回到 4'd8。非同步暫存器在 4'd0 上待的是不到一個週期的區間;同步的那個則待滿一整個週期。如果下游有東西在數週期,這個差別就會有影響。解除後に何が起きるかにも注意してほしい。どちらの場合も Data_out は、リセットが非活性になった最初のクロックエッジで 4'd8 に戻る。非同期レジスタが 4'd0 に留まるのは 1 周期未満の区間、同期レジスタは丸 1 周期である。下流でサイクルを数えている回路があれば、この差は効いてくる。
Problem: The clock has period 10 ns with rising edges at $t = 5, 15, 25, \dots$ The signal reset is high only during the interval $[7, 12]$ ns. Data_in = 4'd8 throughout and Data_out = 4'd3 before $t = 7$. (a) Give Data_out over $[0, 30]$ ns for reg_4_a and for reg_4_b. (b) How wide would the pulse have to be to guarantee the synchronous version resets, for an arbitrary start time? (c) The design is later changed so that the clock is gated off while an external power-management block asserts reset. Which version still works?題目:時脈週期 10 ns,上升緣在 $t = 5, 15, 25, \dots$。訊號 reset 只在區間 $[7, 12]$ ns 為高。全程 Data_in = 4'd8,而在 $t = 7$ 之前 Data_out = 4'd3。(a) 分別給出 reg_4_a 與 reg_4_b 在 $[0, 30]$ ns 期間的 Data_out。(b) 若起始時間任意,脈衝要多寬才能保證同步版本一定會被重置?(c) 這個設計後來被改成:當外部電源管理區塊確立重置時,時脈會被閘掉。哪一個版本仍然有效?問題:クロック周期は 10 ns、立ち上がりエッジは $t = 5, 15, 25, \dots$ にある。信号 reset は区間 $[7, 12]$ ns のあいだだけ高である。全期間 Data_in = 4'd8、$t = 7$ 以前は Data_out = 4'd3 とする。(a) reg_4_a と reg_4_b について $[0, 30]$ ns の Data_out を示せ。(b) 開始時刻が任意のとき、同期版が必ずリセットされることを保証するにはパルス幅はいくら必要か。(c) 後に設計が変更され、外部の電源管理ブロックがリセットをアサートしているあいだクロックがゲートで止まるようになった。どちらの版がなお機能するか。
(a) reg_4_a (asynchronous): 4'd3 until $t=5$; at the edge at $t=5$ it loads 4'd8; at $t=7$ the rising edge of reset clears it to 4'd0 with no clock involved; it stays 4'd0 through the edge at $t=15$? No — reset has already fallen at $t=12$, so the edge at $t=15$ is a normal one and Data_out reloads 4'd8. Summary: 3, then 8 at 5, 0 at 7, 8 at 15.(a) reg_4_a(非同步):到 $t=5$ 為止是 4'd3;在 $t=5$ 的邊緣載入 4'd8;在 $t=7$ reset 的上升緣不經時脈直接把它清成 4'd0;它會一直維持 4'd0 到 $t=15$ 的邊緣嗎?不會——reset 已經在 $t=12$ 下降,所以 $t=15$ 的邊緣是正常的邊緣,Data_out 重新載入 4'd8。整理:3,在 5 變 8,在 7 變 0,在 15 變 8。(a) reg_4_a(非同期):$t=5$ までは 4'd3。$t=5$ のエッジで 4'd8 を取り込む。$t=7$ の reset 立ち上がりがクロックを介さず 4'd0 にクリアする。$t=15$ のエッジまで 4'd0 のままか? いや——reset は $t=12$ で既に下がっているので $t=15$ のエッジは通常のエッジであり、Data_out は 4'd8 を読み直す。まとめ:3、5 で 8、7 で 0、15 で 8。
reg_4_b (synchronous): the reset interval $[7,12]$ contains no rising clock edge (edges are at 5 and 15). The register therefore never sees reset high at a sampling instant and the reset is silently lost: 3 until $t=5$, then 8 from $t=5$ onwards. This is the slide's caution made concrete.reg_4_b(同步):重置區間 $[7,12]$ 裡沒有任何時脈上升緣(邊緣在 5 和 15)。因此暫存器從來沒有在取樣瞬間看到 reset 為高,重置被無聲地丟掉:到 $t=5$ 之前是 3,從 $t=5$ 起是 8。這就是投影片那句警告的具體化。reg_4_b(同期):リセット区間 $[7,12]$ には立ち上がりクロックエッジが一つもない(エッジは 5 と 15)。したがってレジスタはサンプリング時点で reset が高であるのを一度も見ず、リセットは黙って失われる。$t=5$ までは 3、$t=5$ 以降は 8。スライドの警告を具体化したものである。
(b) To guarantee capture for an arbitrary phase, the pulse must be at least one full clock period plus the setup and hold requirements of the reset input: $t_w \ge T_{\text{clk}} + t_{su} + t_h$. With $T = 10$ ns and, say, $t_{su} = 0.1$, $t_h = 0.05$ ns, a 10.15 ns pulse is the theoretical minimum. In a real specification you would write “at least 3 clock cycles” and not think about it again.(b) 要在任意相位下都保證被捕捉到,脈衝至少必須是一整個時脈週期再加上重置輸入的 setup 與 hold 需求:$t_w \ge T_{\text{clk}} + t_{su} + t_h$。取 $T = 10$ ns,並假設 $t_{su} = 0.1$、$t_h = 0.05$ ns,理論最小值是 10.15 ns 的脈衝。在真實規格書裡你會寫「至少 3 個時脈週期」,然後再也不去想它。(b) 任意の位相で捕捉を保証するには、パルス幅は少なくともクロック 1 周期にリセット入力の setup と hold を足した値が必要である:$t_w \ge T_{\text{clk}} + t_{su} + t_h$。$T = 10$ ns、仮に $t_{su} = 0.1$、$t_h = 0.05$ ns とすれば、理論上の最小は 10.15 ns のパルスとなる。実際の仕様書では「クロック 3 サイクル以上」と書いて、それ以上考えないのが普通だ。
(c) Only reg_4_a. A synchronous reset is carried by the clock; with the clock stopped there is no mechanism for it to take effect, and the register holds whatever it contained. This is the most important practical argument for asynchronous assertion.(c) 只有 reg_4_a。同步重置是靠時脈載送的;時脈停了就沒有機制讓它生效,暫存器會保持原本的內容。這是支持非同步確立最重要的實務論據。(c) reg_4_a だけである。同期リセットはクロックが運ぶものであり、クロックが止まれば効かせる手段がなく、レジスタは中身を保持したままになる。これが非同期アサートを支持する、実務上もっとも重要な論拠である。
Problem: A design runs at 500 MHz. The flip-flops specify $t_{rec} = 180$ ps and $t_{rem} = 120$ ps on the CLR pin. The reset tree has 450 ps of insertion-delay spread between its earliest and latest leaf; the clock tree has 90 ps of skew. (a) How wide is the forbidden window around each clock edge for one flip-flop? (b) If reset is released straight from a chip pin, what fraction of release times lands in a forbidden window somewhere in the design? (c) After inserting the two-flip-flop synchroniser of Section 2.6, what is the recovery slack at the worst leaf, assuming the synchroniser's clock-to-Q is 110 ps and the reset tree spread is unchanged? (d) Does the answer to (c) change if the designer uses only one synchroniser flip-flop?題目:某設計跑在 500 MHz。正反器規格在 CLR 腳上標示 $t_{rec} = 180$ ps、$t_{rem} = 120$ ps。重置樹在最早與最晚的葉節點之間有 450 ps 的插入延遲分布;時脈樹有 90 ps 的歪斜。(a) 對單一顆正反器而言,每個時脈邊緣周圍的禁區有多寬?(b) 若重置直接由晶片接腳釋放,有多少比例的釋放時刻會落在設計中某處的禁區裡?(c) 插入 2.6 節的兩級同步器之後,假設同步器的 clock-to-Q 是 110 ps、重置樹分布不變,最糟葉節點的 recovery slack 是多少?(d) 如果設計者只用一顆同步器正反器,(c) 的答案會改變嗎?問題:ある設計は 500 MHz で動作する。フリップフロップは CLR ピンに $t_{rec} = 180$ ps、$t_{rem} = 120$ ps を規定している。リセットツリーは最早と最遅の葉のあいだに 450 ps の挿入遅延の広がりをもち、クロックツリーのスキューは 90 ps である。(a) フリップフロップ 1 個について、各クロックエッジ周りの禁止窓の幅はいくらか。(b) リセットをチップピンから直接解除する場合、解除時刻のうちどれだけの割合が設計内のどこかの禁止窓に入るか。(c) 2.6 節の 2 段同期化回路を挿入したあと、同期化回路の clock-to-Q を 110 ps、リセットツリーの広がりは不変として、最悪の葉でのリカバリ・スラックはいくらか。(d) 設計者が同期化フリップフロップを 1 段しか使わなかった場合、(c) の答えは変わるか。
(a) $t_{rec} + t_{rem} = 180 + 120 = 300$ ps per flip-flop, centred on its own view of the clock edge.(a) 每顆正反器 $t_{rec} + t_{rem} = 180 + 120 = 300$ ps,以它自己所看到的時脈邊緣為中心。(a) フリップフロップあたり $t_{rec} + t_{rem} = 180 + 120 = 300$ ps で、そのフリップフロップ自身が見るクロックエッジを中心とする。
(b) The clock period is $T = 1/500\,\text{MHz} = 2000$ ps. Because the reset tree spreads arrivals over 450 ps and the clock tree over a further 90 ps, the union of all the individual 300 ps windows is about $300 + 450 + 90 = 840$ ps wide. So roughly $840/2000 = 42\,\%$ of possible release instants violate recovery or removal somewhere. An asynchronously released reset is not a rare hazard — it is close to a coin flip.(b) 時脈週期是 $T = 1/500\,\text{MHz} = 2000$ ps。因為重置樹把到達時刻攤開了 450 ps,時脈樹又多攤開 90 ps,所有個別 300 ps 窗口的聯集大約寬 $300 + 450 + 90 = 840$ ps。所以大約 $840/2000 = 42\,\%$ 的可能釋放時刻會在設計中某處違反 recovery 或 removal。非同步釋放的重置不是罕見的危險——它接近擲銅板。(b) クロック周期は $T = 1/500\,\text{MHz} = 2000$ ps。リセットツリーが到着を 450 ps、クロックツリーがさらに 90 ps 広げるので、個々の 300 ps 窓の和集合はおよそ $300 + 450 + 90 = 840$ ps の幅になる。つまり解除時刻のおよそ $840/2000 = 42\,\%$ が、設計のどこかでリカバリまたはリムーバルを破る。非同期解除のリセットは稀なハザードではない——コイン投げに近い。
(c) After synchronisation the reset launches from a flip-flop clocked by the same clock. The arrival at the worst leaf is $t_{cq} + \text{tree delay} = 110 + 450 = 560$ ps after the launching edge, measured against a capture edge one full period later. Slack $= T - (t_{cq} + t_{\text{tree}}) - t_{rec} - t_{\text{clk skew}} = 2000 - 560 - 180 - 90 = 1170$ ps. Comfortable, and — the important part — it is now a number that static timing analysis will compute and report, rather than an unconstrained path.(c) 同步化之後,重置是從一顆由同一時脈驅動的正反器發射出去的。到達最糟葉節點的時間是發射邊緣之後 $t_{cq} + \text{樹延遲} = 110 + 450 = 560$ ps,而捕捉邊緣在整整一個週期之後。Slack $= T - (t_{cq} + t_{\text{tree}}) - t_{rec} - t_{\text{clk skew}} = 2000 - 560 - 180 - 90 = 1170$ ps。很寬裕,而且——重點在這——它現在是一個靜態時序分析會算出來並報告的數字,而不是一條未受約束的路徑。(c) 同期化後、リセットは同じクロックで駆動されるフリップフロップから発射される。最悪の葉への到着は発射エッジから $t_{cq} + \text{ツリー遅延} = 110 + 450 = 560$ ps 後で、キャプチャエッジは丸 1 周期後にある。スラック $= T - (t_{cq} + t_{\text{tree}}) - t_{rec} - t_{\text{clk skew}} = 2000 - 560 - 180 - 90 = 1170$ ps。十分な余裕であり、そして肝心なのは、これがもはや制約のない経路ではなく、スタティックタイミング解析が計算して報告する数値になったことである。
(d) The recovery slack at the leaves is unchanged, because that path starts at the last synchroniser flip-flop either way. What changes is the metastability margin of the synchroniser itself. With one flip-flop, whatever that flip-flop resolves to propagates straight into the reset tree, so a metastable output can drive the whole design's CLR pins to an intermediate voltage. With two, the first stage gets a full clock period to settle. The second flip-flop costs nothing in slack and buys many orders of magnitude of MTBF.(d) 葉節點的 recovery slack 不變,因為不論如何那條路徑都是從最後一顆同步器正反器出發的。改變的是同步器本身的亞穩態餘裕。只用一顆時,那顆正反器解出什麼就直接傳進重置樹,所以一個亞穩態輸出可以把整個設計的 CLR 腳都驅動到中間電位。用兩顆時,第一級有完整一個時脈週期可以安定。第二顆正反器在 slack 上不花半點代價,卻買到好幾個數量級的 MTBF。(d) 葉でのリカバリ・スラックは変わらない。その経路はどちらにせよ最終段の同期化フリップフロップから始まるからだ。変わるのは同期化回路自身のメタステーブル余裕である。1 段だと、そのフリップフロップが落ち着いた値がそのままリセットツリーへ伝わるので、メタステーブル出力が設計全体の CLR ピンを中間電位へ駆動しうる。2 段なら初段に丸 1 周期の整定時間がある。二段目はスラックを一切消費せずに、MTBF を何桁も買ってくれる。
The simplest useful sequential structure is a cloud of combinational logic with a register on its output. It is also the structure that every synchronous design is built out of: a chip is nothing but registers separated by logic clouds, and the clock period is the longest such cloud plus the register overhead. The two slide examples are deliberately minimal so that the mapping from source to gates is unambiguous.最簡單而有用的循序結構,就是一團組合邏輯外加一個輸出端的暫存器。它也是每一個同步設計所由建構的結構:一顆晶片無非就是被邏輯雲隔開的暫存器,而時脈週期就是最長的那團雲再加上暫存器的開銷。投影片的兩個例子刻意做得極簡,好讓從原始碼到閘的對映毫無歧義。もっとも単純で有用な順序構造は、組合せ論理の雲とその出力に置かれたレジスタである。これはあらゆる同期設計が組み立てられる構造でもある。チップとは論理の雲で隔てられたレジスタの集まりにすぎず、クロック周期は最長の雲にレジスタのオーバーヘッドを足したものだ。スライドの二例は、ソースからゲートへの写像が一意になるよう意図的に最小限にしてある。
module reg_and (a, b, c, clk, y);
input a, b, c, clk;
output y;
reg y;
always @ (posedge clk)
begin
y <= a & b & c;
end
endmodule
Apply the rule from Section 1.3. The block is edge-sensitive, y is assigned non-blockingly, so y is a flip-flop. Its D input is the expression a & b & c, which is combinational. The result is one three-input AND gate driving one D flip-flop. The three inputs a, b, c are not registered — nothing in the description says to remember them.套用 1.3 節的規則。這個區塊是邊緣敏感的,y 以非阻斷方式被指派,所以 y 是正反器。它的 D 輸入是運算式 a & b & c,那是組合邏輯。結果就是一顆三輸入 AND 閘驅動一顆 D 正反器。三個輸入 a、b、c 沒有被暫存——描述裡沒有任何地方叫它記住它們。1.3 節の規則を当てはめる。このブロックはエッジ感応で、y はノンブロッキングで代入されるので y はフリップフロップになる。その D 入力は式 a & b & c で、これは組合せ論理である。結果は 3 入力 AND ゲート 1 個が D フリップフロップ 1 個を駆動する回路だ。三つの入力 a、b、c はレジスタに入らない——記憶せよと述べた箇所がどこにもないからである。
Two observations that students often need spelled out. First, the declaration reg y; does not by itself create a register: reg in Verilog means “a variable that holds its value between assignments in procedural code”, which is a simulation concept. Whether it becomes hardware storage is decided entirely by the rules of Section 1. A reg assigned in an always @* block is a wire. Second, the port y is declared both output and reg; in IEEE 1364-1995 style that requires two separate declarations, as above. The ANSI-style header of Verilog-2001 lets you write output reg y in the port list, which is less error-prone and which Lecture 7 covers in more detail.有兩點學生常常需要人講明。第一,宣告 reg y; 本身並不會產生暫存器:Verilog 裡的 reg 意思是「在程序式程式碼中兩次指派之間保持其值的變數」,那是一個模擬層面的概念。它會不會變成硬體儲存元件,完全由第 1 節的規則決定。在 always @* 區塊裡被指派的 reg 就是一條連線。第二,埠 y 同時被宣告為 output 與 reg;在 IEEE 1364-1995 風格下這需要兩個分開的宣告,如上所示。Verilog-2001 的 ANSI 風格標頭允許你在埠列表裡直接寫 output reg y,比較不容易出錯,第 7 講會更詳細地談。学生に明示しておく必要のある点が二つある。第一に、reg y; という宣言そのものはレジスタを作らない。Verilog の reg は「手続き的コード中で代入と代入のあいだ値を保持する変数」という意味であり、これはシミュレーション上の概念である。それがハードウェアの記憶素子になるかどうかは、完全に第 1 節の規則が決める。always @* ブロック内で代入される reg は配線である。第二に、ポート y は output と reg の両方で宣言されている。IEEE 1364-1995 のスタイルでは上記のように二つの宣言が必要だ。Verilog-2001 の ANSI スタイルのヘッダならポートリストに output reg y と書けて誤りが減る。これは第 7 講で詳しく扱う。
module mux_reg (a, b, c, d, y, select, clock);
input [7:0] a, b, c, d;
output [7:0] y;
input [1:0] select;
reg [7:0] y;
always @ (posedge clock)
case (select)
0: y <= a; // non-blocking
1: y <= b; // same result with =
2: y <= c;
3: y <= d;
default: y <= 8'bx;
endcase
endmodule
Eight flip-flops, one per bit of y, each fed by an 8-bit-wide 4:1 multiplexer that is decoded once from select and shared across all eight slices. The synthesis-result slide confirms exactly that: eight mux4 cells in a column, eight dffp cells behind them, and a small decoder built from an inverter and a few NAND/NOR gates feeding all the mux selects in parallel.八顆正反器,y 的每一位元一顆,各自由一個 8 位元寬的 4:1 多工器餵入,而該多工器的選擇訊號只從 select 解碼一次,由八個切片共用。合成結果的投影片正好證實了這點:一欄八顆 mux4 單元,後面一欄八顆 dffp 單元,再加上一個由反相器與幾顆 NAND/NOR 組成的小解碼器,平行地餵給所有多工器的選擇腳。フリップフロップは 8 個、y の 1 ビットにつき 1 個。各々は 8 ビット幅の 4:1 マルチプレクサに駆動され、その選択信号は select から一度だけデコードされて 8 スライスで共有される。合成結果のスライドはまさにそれを示している。mux4 セルが 8 個縦に並び、その後ろに dffp セルが 8 個、そしてインバータと数個の NAND/NOR からなる小さなデコーダが、すべてのマルチプレクサの選択端を並列に駆動している。
The slide prints the last branch as default y <= 8'bx; without the colon. In Verilog a case item label must be followed by : — default: y <= 8'bx;. Without it the file does not compile. The version above is corrected. The slide also capitalises Input in two places; Verilog keywords are case-sensitive and must be lower case.投影片把最後一個分支印成 default y <= 8'bx;,沒有冒號。在 Verilog 裡 case 項目標籤後面必須接 :——default: y <= 8'bx;。少了它檔案根本編不過。上面的版本已經更正。投影片還在兩個地方把 Input 寫成大寫開頭;Verilog 關鍵字區分大小寫,必須全小寫。スライドは最後の分岐を default y <= 8'bx; とコロンなしで印刷している。Verilog では case 項目のラベルの後に : が必要である——default: y <= 8'bx;。これがないとファイルはコンパイルできない。上の版では訂正してある。スライドではさらに 2 か所で Input が大文字始まりになっている。Verilog のキーワードは大文字小文字を区別し、小文字でなければならない。
8'bx means to a synthesiser8'bx 對合成器的意義8'bx が合成器にとって意味するものIn simulation, assigning 8'bx propagates unknowns, which is useful: it makes an illegal select visible as X on the output instead of silently producing stale data. In synthesis, x on the right-hand side means don't care. The tool is free to produce whatever output minimises the logic for that input combination. Here the default is actually unreachable — select is two bits wide, and 0, 1, 2, 3 cover all four codes — so it costs nothing either way, and the tool's case analysis will see the case as full and skip the hold-the-previous-value logic.在模擬中,指派 8'bx 會傳播未知值,這很有用:它讓非法的 select 以輸出上的 X 顯現出來,而不是無聲地給出過期的資料。在合成中,右手邊的 x 意思是 don't care。工具可以為那組輸入組合產生任何能讓邏輯最小化的輸出。這裡的 default 其實是不可能到達的——select 只有兩位元寬,而 0、1、2、3 已涵蓋全部四種碼——所以兩種寫法都不花成本,而工具的 case 分析會把這個 case 視為 full,略過「保持前值」的邏輯。シミュレーションでは 8'bx を代入すると不定値が伝播する。これは有用だ。不正な select が、黙って古いデータを出す代わりに出力の X として見えるようになる。合成では右辺の x は don't care を意味する。ツールはその入力組合せに対して論理を最小化する任意の出力を出してよい。ここでの default は実際には到達不能である——select は 2 ビット幅で、0、1、2、3 が四つの符号すべてを覆う——ので、どちらでもコストは変わらず、ツールのケース解析はこの case を full と見なして前値保持の論理を省く。
That reachability matters. If select were three bits and you wrote only four branches plus default: y <= 8'bx;, the four unlisted codes would be don't-cares and the synthesiser could map them onto any of a, b, c, d — whichever is cheapest. Your RTL simulation would show X, your gate simulation would show real data, and the two would mismatch. That is the classic cause of an “RTL and netlist disagree” bug report. If you want the hardware to be predictable for illegal inputs, assign a defined value in the default.那個「可達性」很要緊。如果 select 是三位元,而你只寫了四個分支再加 default: y <= 8'bx;,那四個沒列出來的碼就是 don't-care,合成器可以把它們對映到 a、b、c、d 中任何一個——哪個便宜就用哪個。你的 RTL 模擬會顯示 X,閘級模擬會顯示真實資料,兩者不一致。這就是「RTL 和 netlist 不一致」這類臭蟲報告的經典成因。如果你希望硬體對非法輸入也是可預測的,就在 default 裡指派一個有定義的值。この到達可能性は重要だ。もし select が 3 ビットで、分岐を四つと default: y <= 8'bx; だけ書いたなら、列挙されなかった四つの符号は don't-care となり、合成器はそれらを a、b、c、d のどれに写像してもよい——いちばん安いものに。RTL シミュレーションは X を示し、ゲートシミュレーションは実データを示し、両者は食い違う。「RTL とネットリストが一致しない」というバグ報告の古典的な原因がこれである。不正入力に対してもハードウェアを予測可能にしたいなら、default で定義された値を代入すること。
The slide's comment is correct here and misleading in general. In this block there is exactly one assignment to y per clock edge, nothing else in the block reads y, and no other variable is involved. Under those conditions blocking and non-blocking assignment produce the same hardware, because the ordering that = imposes has nothing to order against.投影片的註解在這裡是對的,但一般而言會誤導。在這個區塊裡,每個時脈邊緣對 y 只有恰好一次指派,區塊中沒有別的東西讀 y,也沒有其他變數牽涉其中。在這些條件下,阻斷式與非阻斷式指派產生相同的硬體,因為 = 所強加的順序根本沒有對象可排。スライドの注釈はここでは正しいが、一般には誤解を招く。このブロックではクロックエッジごとに y への代入がちょうど一つで、ブロック内の他の何も y を読まず、他の変数も関与しない。その条件下ではブロッキング代入とノンブロッキング代入は同じハードウェアを生む。= が課す順序に、順序づける相手がいないからだ。
The moment a second register appears in the block, that stops being true — Practice 1(c) versus 1(d) is the demonstration. More subtly, it also stops being true across different blocks even when each block has only one assignment. If block A drives q1 with = and block B reads q1 at the same clock edge, the simulator's execution order between the two blocks is unspecified, and you have a simulation race: run the same testbench on two simulators and get two answers. Non-blocking assignment removes the race by construction, because every right-hand side is evaluated before any left-hand side is updated. That is why the guideline is unconditional: clocked logic uses <=, with no exceptions worth arguing about.一旦區塊裡出現第二個暫存器,這就不成立了——練習 1(c) 對照 1(d) 就是示範。更微妙的是,就算每個區塊各自只有一次指派,跨不同區塊時它也不再成立。如果區塊 A 用 = 驅動 q1,而區塊 B 在同一個時脈邊緣讀 q1,兩個區塊之間的模擬器執行順序是未規定的,你就有了一場模擬競爭:同一個測試平台在兩套模擬器上跑會得到兩種答案。非阻斷式指派從構造上消除了這場競爭,因為所有右手邊都在任何左手邊被更新之前就算完了。這就是為什麼那條準則是無條件的:時脈邏輯用 <=,沒有什麼值得爭論的例外。ブロックに二つ目のレジスタが現れた瞬間、それは成り立たなくなる——練習 1(c) と 1(d) の対比が実演である。さらに微妙なことに、各ブロックの代入が一つずつでも、異なるブロックをまたぐと成り立たなくなる。ブロック A が = で q1 を駆動し、ブロック B が同じクロックエッジで q1 を読むなら、両ブロック間のシミュレータの実行順序は未規定であり、シミュレーション・レースが生じる。同じテストベンチを二つのシミュレータで走らせると二つの答えが出る。ノンブロッキング代入は構成上このレースを消す。すべての右辺が、どの左辺が更新されるより前に評価されるからだ。だから指針は無条件である。クロック同期論理には <=、議論に値する例外はない。
mux_reg: an 8-bit 4:1 multiplexer in front of an 8-bit register. The select decode is shared; the mux and the flip-flop are replicated once per bit.圖 5 — mux_reg:一顆 8 位元 4:1 多工器放在 8 位元暫存器前面。選擇解碼是共用的;多工器與正反器則每位元各複製一份。図 5 — mux_reg:8 ビット 4:1 マルチプレクサを 8 ビットレジスタの前に置いた構成。選択デコードは共有され、マルチプレクサとフリップフロップはビットごとに複製される。Registered logic is the right place to introduce the arithmetic that the rest of the lecture depends on. A path runs from the Q pin of a launching flip-flop, through a combinational cloud, to the D pin of a capturing flip-flop. For that path to work, the data must arrive before the capture edge with enough margin:暫存化的邏輯正是引入本講其餘部分所倚賴之算式的好時機。一條路徑從發射端正反器的 Q 腳出發,穿過一團組合邏輯雲,到達捕捉端正反器的 D 腳。要讓這條路徑成立,資料必須在捕捉邊緣之前帶著足夠餘裕抵達:レジスタ付き論理は、本講の残りが依拠する計算を導入するのにちょうどよい場所である。経路は発射側フリップフロップの Q ピンから出発し、組合せ論理の雲を抜け、捕捉側フリップフロップの D ピンに至る。この経路が成立するには、データが捕捉エッジより前に十分な余裕をもって到着しなければならない。
and it must not arrive too soon after the capture edge, or it will race through and corrupt the value the capturing flip-flop was supposed to hold:而且它不能在捕捉邊緣之後太快抵達,否則它會一路衝過去,破壞捕捉端正反器本來應該保持的值:同時に、捕捉エッジの後早すぎて到着してもいけない。突き抜けて、捕捉側フリップフロップが保持すべきだった値を壊してしまう。
Here $t_{cq}$ is clock-to-Q of the launching flip-flop, $t_{su}$ and $t_h$ are setup and hold of the capturing one, and $t_{\text{skew}}$ is the arrival-time difference of the clock at the two flip-flops (positive when the capture clock is later).這裡 $t_{cq}$ 是發射端正反器的 clock-to-Q,$t_{su}$ 與 $t_h$ 是捕捉端的 setup 與 hold,而 $t_{\text{skew}}$ 是時脈到達兩顆正反器的時間差(捕捉時脈較晚時為正)。ここで $t_{cq}$ は発射側フリップフロップの clock-to-Q、$t_{su}$ と $t_h$ は捕捉側の setup と hold、$t_{\text{skew}}$ は二つのフリップフロップへのクロック到着時刻の差(捕捉クロックが遅いとき正)である。
Three things follow that matter for the rest of this lecture. First, the setup constraint scales with the period: if the design is too slow you can always slow the clock down. Second, the hold constraint does not — $T_{\text{clk}}$ does not appear in it, so a hold violation is a functional failure that no clock frequency can fix, and the only repair is to add delay (buffers) to the short path. Third, $t_{cq} + t_{su}$ is a fixed tax paid once per register stage; it is the reason the pipelining speed-up in Section 6 has a ceiling.由此得出三件對本講其餘部分重要的事。第一,setup 約束隨週期縮放:如果設計太慢,你永遠可以把時脈放慢。第二,hold 約束不會——$T_{\text{clk}}$ 根本沒出現在裡面,所以 hold 違規是任何時脈頻率都救不了的功能性失效,唯一的修法是在那條短路徑上加延遲(緩衝器)。第三,$t_{cq} + t_{su}$ 是每一級暫存器都要繳一次的固定稅;這就是第 6 節的管線加速有上限的原因。ここから、本講の残りに効いてくる三点が導かれる。第一に、セットアップ制約は周期に比例する。設計が遅すぎるならクロックを遅くすればいつでも救える。第二に、ホールド制約はそうではない——$T_{\text{clk}}$ が式に現れないので、ホールド違反はどんなクロック周波数でも直らない機能故障であり、唯一の対処は短い経路に遅延(バッファ)を足すことだ。第三に、$t_{cq} + t_{su}$ はレジスタ段ごとに一度支払う固定の税であり、第 6 節のパイプライン高速化に天井がある理由でもある。
A worked example with plausible 65 nm numbers: $t_{cq} = 120$ ps, $t_{su} = 60$ ps, $t_h = 40$ ps, clock skew 50 ps, and a logic cloud whose maximum delay is 1.2 ns and minimum delay 0.15 ns. Setup requires $T \ge 120 + 1200 + 60 - 50 = 1330$ ps, so the design runs at at most 752 MHz. Hold requires $120 + 150 = 270 \ge 40 + 50 = 90$ ps, which passes with 180 ps of slack. Add a synchronous reset gate to the cloud and the maximum delay becomes perhaps 1.26 ns, pushing the limit down to 723 MHz — the 4 % that Section 2.3 was talking about.用 65 nm 的合理數字做一個演練:$t_{cq} = 120$ ps、$t_{su} = 60$ ps、$t_h = 40$ ps、時脈歪斜 50 ps,邏輯雲的最大延遲 1.2 ns、最小延遲 0.15 ns。Setup 要求 $T \ge 120 + 1200 + 60 - 50 = 1330$ ps,所以設計最高跑到 752 MHz。Hold 要求 $120 + 150 = 270 \ge 40 + 50 = 90$ ps,以 180 ps 的 slack 通過。在雲裡加一顆同步重置閘,最大延遲大概變成 1.26 ns,把上限壓到 723 MHz——正是 2.3 節講的那 4 %。65 nm のもっともらしい数値で一例を計算してみる。$t_{cq} = 120$ ps、$t_{su} = 60$ ps、$t_h = 40$ ps、クロックスキュー 50 ps、論理雲の最大遅延 1.2 ns、最小遅延 0.15 ns。セットアップは $T \ge 120 + 1200 + 60 - 50 = 1330$ ps を要求するので、動作上限は約 752 MHz。ホールドは $120 + 150 = 270 \ge 40 + 50 = 90$ ps を要求し、180 ps のスラックで通る。雲に同期リセット用ゲートを足すと最大遅延はおよそ 1.26 ns になり、上限は 723 MHz まで下がる——2.3 節が言っていた 4 % である。
Problem: A pipeline stage has $t_{cq} = 110$ ps, $t_{su} = 55$ ps, $t_h = 35$ ps and a logic cloud with $t_{\text{max}} = 820$ ps, $t_{\text{min}} = 70$ ps. Clock skew is $+40$ ps (capture clock arrives later than launch clock). (a) What is the maximum clock frequency? (b) Does the stage meet hold? (c) The clock tree is rebuilt and the skew becomes $-40$ ps. What happens to each check? (d) The team wants 1.5 GHz. How much logic delay must be removed, and what are the two ways to get there?題目:某管線級的 $t_{cq} = 110$ ps、$t_{su} = 55$ ps、$t_h = 35$ ps,邏輯雲的 $t_{\text{max}} = 820$ ps、$t_{\text{min}} = 70$ ps。時脈歪斜是 $+40$ ps(捕捉時脈比發射時脈晚到)。(a) 最高時脈頻率是多少?(b) 這一級通過 hold 嗎?(c) 時脈樹重建之後歪斜變成 $-40$ ps,兩項檢查各自會怎麼變?(d) 團隊想要 1.5 GHz。必須拿掉多少邏輯延遲?有哪兩條路可以走到?問題:あるパイプライン段は $t_{cq} = 110$ ps、$t_{su} = 55$ ps、$t_h = 35$ ps、論理雲は $t_{\text{max}} = 820$ ps、$t_{\text{min}} = 70$ ps である。クロックスキューは $+40$ ps(捕捉クロックが発射クロックより遅く到着)。(a) 最高クロック周波数は。(b) この段はホールドを満たすか。(c) クロックツリーを作り直してスキューが $-40$ ps になった。各チェックはどうなるか。(d) チームは 1.5 GHz を望んでいる。論理遅延をどれだけ削る必要があり、そこへ至る二つの方法は何か。
(a) $T \ge t_{cq} + t_{\text{max}} + t_{su} - t_{\text{skew}} = 110 + 820 + 55 - 40 = 945$ ps, so $f_{\max} = 1/945\,\text{ps} \approx 1.058$ GHz.(a) $T \ge t_{cq} + t_{\text{max}} + t_{su} - t_{\text{skew}} = 110 + 820 + 55 - 40 = 945$ ps,所以 $f_{\max} = 1/945\,\text{ps} \approx 1.058$ GHz。(a) $T \ge t_{cq} + t_{\text{max}} + t_{su} - t_{\text{skew}} = 110 + 820 + 55 - 40 = 945$ ps なので、$f_{\max} = 1/945\,\text{ps} \approx 1.058$ GHz。
(b) Hold requires $t_{cq} + t_{\text{min}} \ge t_h + t_{\text{skew}}$, i.e. $110 + 70 = 180 \ge 35 + 40 = 75$. Passes with 105 ps of slack.(b) Hold 要求 $t_{cq} + t_{\text{min}} \ge t_h + t_{\text{skew}}$,也就是 $110 + 70 = 180 \ge 35 + 40 = 75$。以 105 ps 的 slack 通過。(b) ホールドは $t_{cq} + t_{\text{min}} \ge t_h + t_{\text{skew}}$、すなわち $110 + 70 = 180 \ge 35 + 40 = 75$ を要求する。105 ps のスラックで通る。
(c) With $t_{\text{skew}} = -40$ ps the capture clock is earlier. Setup gets worse: $T \ge 110 + 820 + 55 + 40 = 1025$ ps, so $f_{\max}$ drops to about 976 MHz. Hold gets better: the requirement becomes $180 \ge 35 - 40 = -5$, trivially satisfied. Skew always trades one check against the other; this is why clock-tree synthesis targets low skew rather than zero skew, and why useful skew is sometimes deliberately introduced to steal time for a critical stage.(c) $t_{\text{skew}} = -40$ ps 時捕捉時脈較早到。Setup 變糟:$T \ge 110 + 820 + 55 + 40 = 1025$ ps,所以 $f_{\max}$ 掉到大約 976 MHz。Hold 變好:要求變成 $180 \ge 35 - 40 = -5$,輕鬆滿足。歪斜永遠是拿一項檢查去換另一項;這就是為什麼時脈樹合成的目標是低歪斜而不是零歪斜,也是為什麼有時候會刻意引入 useful skew 去替某個關鍵級偷時間。(c) $t_{\text{skew}} = -40$ ps では捕捉クロックのほうが早い。セットアップは悪化し、$T \ge 110 + 820 + 55 + 40 = 1025$ ps、$f_{\max}$ は約 976 MHz に下がる。ホールドは良くなり、要求は $180 \ge 35 - 40 = -5$ で自明に満たされる。スキューは常に一方の検査を他方と引き換えにする。クロックツリー合成がゼロスキューではなく低スキューを狙う理由であり、重要な段のために時間を盗む useful skew があえて導入されることがある理由でもある。
(d) 1.5 GHz means $T = 667$ ps. With the original $+40$ ps skew, the logic budget is $667 + 40 - 110 - 55 = 542$ ps, so 278 ps must come out of the 820 ps cloud — a 34 % reduction. Two routes: restructure the logic (a faster adder topology, balanced trees instead of chains, better mapping) or add a pipeline stage and split the cloud in two. Note that even an ideal split gives $110 + 410 + 55 - 40 = 535$ ps, comfortably inside 667 ps, but costs one cycle of latency and a full register bank. Section 6 quantifies that trade.(d) 1.5 GHz 代表 $T = 667$ ps。用原本的 $+40$ ps 歪斜,邏輯預算是 $667 + 40 - 110 - 55 = 542$ ps,所以必須從 820 ps 的雲裡拿掉 278 ps——減少 34 %。兩條路:重構邏輯(更快的加法器拓撲、用平衡樹取代串鏈、更好的對映),或加一級管線把雲切成兩半。注意即使是理想的對半切也得到 $110 + 410 + 55 - 40 = 535$ ps,舒適地落在 667 ps 之內,但代價是一個週期的延遲和一整排暫存器。第 6 節會把這個取捨量化。(d) 1.5 GHz は $T = 667$ ps を意味する。元の $+40$ ps のスキューなら論理の予算は $667 + 40 - 110 - 55 = 542$ ps なので、820 ps の雲から 278 ps を削らねばならない——34 % 減である。道は二つ。論理を再構成する(より速い加算器トポロジ、連鎖ではなく平衡木、よりよいマッピング)か、パイプライン段を追加して雲を二分するかだ。理想的に二分しても $110 + 410 + 55 - 40 = 535$ ps で 667 ps に十分収まるが、レイテンシ 1 サイクルとレジスタ 1 段ぶんの代償を伴う。第 6 節でこのトレードオフを定量化する。
Everything in this section is the same hardware — a bank of flip-flops — differing only in what is wired to the D inputs. That is worth keeping in mind as a unifying idea: a shift register is a register whose D inputs come from its neighbours' Q outputs; a rotator is a shift register whose chain is closed into a ring; a load register is a register with a multiplexer deciding between “new data” and “what I already have”.本節裡的每一樣東西都是同一種硬體——一排正反器——差別只在 D 輸入接了什麼。這個統一的想法值得放在心上:移位暫存器就是 D 輸入來自鄰居 Q 輸出的暫存器;旋轉器就是把鏈頭尾接成環的移位暫存器;載入暫存器就是前面加一顆多工器、在「新資料」與「我原本就有的東西」之間做選擇的暫存器。本節に出てくるものはすべて同じハードウェア——フリップフロップの列——であり、違うのは D 入力に何が繋がっているかだけである。統一的な見方として覚えておく価値がある。シフトレジスタとは D 入力が隣の Q 出力から来るレジスタ、ローテータとはチェーンを環に閉じたシフトレジスタ、ロードレジスタとは「新しいデータ」と「いま持っているもの」をマルチプレクサで選ぶレジスタである。
module Shift_reg4 (Data_in, Data_out, clock, reset); input Data_in, clock, reset; output Data_out; reg [3:0] Data_reg; assign Data_out = Data_reg[0]; always @ (negedge reset or posedge clock) begin if (reset == 1'b0) Data_reg <= 4'b0; // active low, asynchronous else Data_reg <= {Data_in, Data_reg[3:1]}; end endmodule
Four points to extract from eight lines.從八行程式碼裡可以抽出四個重點。8 行のコードから四つの要点を取り出せる。
The reset is active low and asynchronous. negedge reset in the event control paired with if (reset == 1'b0) in the body — polarity matched, exactly as Section 2.1 requires. This is the convention most real designs use, named rst_n or reset_n, because CMOS reset-generation circuits naturally produce an active-low signal and because a floating or unpowered net tends to sit low, which puts the chip in reset rather than running wild.重置是低位準作用且非同步的。事件控制裡的 negedge reset 搭配本體裡的 if (reset == 1'b0)——極性一致,正是 2.1 節所要求的。這是多數真實設計採用的慣例,名字叫 rst_n 或 reset_n,因為 CMOS 的重置產生電路天生產出低位準作用的訊號,也因為浮接或未供電的網路傾向停在低位準,那會讓晶片進入重置而不是亂跑。リセットは負論理かつ非同期である。イベント制御の negedge reset と本体の if (reset == 1'b0) が対になっている——極性が一致しており、2.1 節の要求どおりだ。これは実際の設計の多くが採る慣習で、名前は rst_n や reset_n とされる。CMOS のリセット生成回路が自然に負論理の信号を出すこと、そして浮いたネットや非給電のネットが低位に落ち着きやすく、その結果チップが暴走せずリセットに入ることが理由である。
The shift direction. {Data_in, Data_reg[3:1]} builds a 4-bit value whose top bit is the new input and whose lower three bits are the old bits 3, 2, 1. So bit 3 receives Data_in, bit 2 receives old bit 3, bit 1 receives old bit 2, bit 0 receives old bit 1. Data enters at the most significant end and walks down towards bit 0. Since Data_out taps bit 0, the module is a four-clock delay line: whatever you present at Data_in appears at Data_out four rising edges later.移位方向。{Data_in, Data_reg[3:1]} 組出一個 4 位元值,最高位是新的輸入,較低三位是舊的第 3、2、1 位。所以位元 3 收到 Data_in,位元 2 收到舊的位元 3,位元 1 收到舊的位元 2,位元 0 收到舊的位元 1。資料從最高位端進入,往位元 0 的方向往下走。既然 Data_out 取的是位元 0,這個模組就是一條四拍的延遲線:你在 Data_in 放進去的東西,會在四個上升緣之後出現在 Data_out。シフトの向き。{Data_in, Data_reg[3:1]} は、最上位が新しい入力、下位 3 ビットが古いビット 3、2、1 である 4 ビット値を作る。つまりビット 3 は Data_in を、ビット 2 は古いビット 3 を、ビット 1 は古いビット 2 を、ビット 0 は古いビット 1 を受け取る。データは最上位側から入り、ビット 0 に向かって下へ進む。Data_out はビット 0 を取り出すので、このモジュールはクロック 4 拍の遅延線である。Data_in に与えたものは立ち上がりエッジ 4 回後に Data_out に現れる。
Why non-blocking is mandatory here. The concatenation reads Data_reg[3:1] while assigning Data_reg[3:0]. With <= the right-hand side is sampled at the edge, so every bit sees its neighbour's old value and the shift is a true shift. Write = instead and the four bits are still assigned as one vector in one statement, so this particular line happens to survive — but split it into four separate statements with = and you get the Practice 1(d) disaster.為什麼這裡非用非阻斷式不可。這個串接在指派 Data_reg[3:0] 的同時讀取 Data_reg[3:1]。用 <= 時右手邊在邊緣被取樣,所以每一位元看到的都是鄰居的舊值,移位才是真正的移位。改寫成 = 的話,這四個位元仍然是在一個敘述裡以一個向量被指派的,所以這一行剛好活得下來——但把它拆成四個分開的 = 敘述,你就得到練習 1(d) 的災難。なぜここでノンブロッキングが必須なのか。この連結は Data_reg[3:0] へ代入しながら Data_reg[3:1] を読んでいる。<= なら右辺はエッジでサンプルされるので、各ビットは隣の古い値を見て、シフトは本物のシフトになる。= に替えても、4 ビットは一つの文で一つのベクタとして代入されるので、この行はたまたま生き延びる——が、= で四つの別々の文に分ければ練習 1(d) の惨事になる。
What the netlist looks like. Four flip-flops with asynchronous clear, Q of each wired directly to D of the next, no logic in between whatsoever. This is the cheapest sequential structure there is, which is why shift registers are the default implementation of delay lines, serial interfaces, and the synchronisers of Section 2.6.netlist 長什麼樣。四顆附非同步清除的正反器,每一顆的 Q 直接接到下一顆的 D,中間完全沒有邏輯。這是現存最便宜的循序結構,也是為什麼移位暫存器是延遲線、序列介面以及 2.6 節同步器的預設實作方式。ネットリストの姿。非同期クリア付きフリップフロップ 4 個、各々の Q が次の D に直結し、あいだに論理はまったくない。これは存在する中でもっとも安い順序構造であり、シフトレジスタが遅延線、シリアルインタフェース、そして 2.6 節の同期化回路の既定の実装である理由でもある。
Shift_reg4 with the stream 1, 1, 0, 1, 0, 0. Each bit enters at Data_reg[3] and steps one position per rising edge; Data_out, which taps Data_reg[0], reproduces the input four edges later. Values shown in red are the bits sampled at each edge.圖 6 — Shift_reg4 在輸入串流 1, 1, 0, 1, 0, 0 下的移位暫存器波形。每一位元從 Data_reg[3] 進入,每個上升緣往前走一格;取自 Data_reg[0] 的 Data_out 在四個邊緣之後重現輸入。紅色顯示的值是在各邊緣被取樣的位元。図 6 — 入力列 1, 1, 0, 1, 0, 0 を与えたときの Shift_reg4 のシフトレジスタ波形。各ビットは Data_reg[3] から入り、立ち上がりエッジごとに 1 段進む。Data_reg[0] を取り出す Data_out は 4 エッジ後に入力を再現する。赤で示した値が各エッジでサンプルされたビットである。The second example attaches a small decision to the chain.第二個例子在鏈上附加了一個小小的判斷。二つ目の例は、チェーンに小さな判断を付け加えたものである。
module shifter_1 (Data_in, clock, reset, sig_d, new_signal);
input Data_in, clock, reset;
output sig_d, new_signal;
reg sig_a, sig_b, sig_c, sig_d, new_signal;
always @ (posedge reset or posedge clock)
begin
if (reset == 1'b1)
begin
sig_a <= 0; sig_b <= 0; sig_c <= 0; sig_d <= 0;
new_signal <= 0;
end
else
begin
sig_a <= Data_in;
sig_b <= sig_a;
sig_c <= sig_b;
sig_d <= sig_c;
new_signal <= (~sig_a) & sig_b;
end
end
endmodule
Five flip-flops: four for the chain and a fifth for new_signal. The chain is written as four separate non-blocking statements rather than one concatenation, which is the more readable style and which works for exactly the reason given in Section 1.3 — the right-hand sides are all sampled at the edge, so sig_b gets the old sig_a.五顆正反器:四顆給鏈,第五顆給 new_signal。鏈是寫成四個分開的非阻斷式敘述,而不是一個串接,這是比較易讀的風格,而它之所以能用,理由正是 1.3 節講的——所有右手邊都在邊緣被取樣,所以 sig_b 拿到的是舊的 sig_a。フリップフロップは 5 個。4 個がチェーン、5 個目が new_signal 用である。チェーンは連結ではなく四つの独立したノンブロッキング文として書かれている。こちらのほうが読みやすく、しかも成り立つ理由はまさに 1.3 節の説明どおり——右辺はすべてエッジでサンプルされるので、sig_b は古い sig_a を受け取る。
The interesting line is the last one. At any moment sig_a holds the most recent sample and sig_b the one before it, so (~sig_a) & sig_b is true exactly when the stream went 1 then 0. The module is a falling-edge detector. In the netlist this is one inverter, one two-input AND, and one flip-flop — which is precisely what the slide circles in blue on the schematic.有意思的是最後一行。在任何時刻 sig_a 保存的是最新的取樣,sig_b 是前一個,所以 (~sig_a) & sig_b 恰好在串流走了 1 然後 0 時為真。這個模組是一個下降緣偵測器。在 netlist 裡這就是一顆反相器、一顆兩輸入 AND 和一顆正反器——正是投影片在電路圖上用藍色圈起來的那一塊。面白いのは最後の行である。任意の時点で sig_a は最新のサンプル、sig_b はその一つ前を保持しているので、(~sig_a) & sig_b は入力列が 1 のあと 0 になったときちょうど真になる。このモジュールは立ち下がりエッジ検出器である。ネットリスト上ではインバータ 1 個、2 入力 AND 1 個、フリップフロップ 1 個——スライドが回路図上で青く囲んでいるのがまさにこれだ。
Why register the detector output at all? Because the alternative, assign new_signal = ~sig_a & sig_b;, produces a combinational output that can glitch: sig_a and sig_b change at the same clock edge, but not at exactly the same instant, so if sig_a falls a few picoseconds before sig_b falls, the AND gate briefly sees (~0) & 1 = 1 and emits a spike. Inside a synchronous design that spike is harmless — it settles long before the next edge. Driven off-chip, or used as an asynchronous clear somewhere, it is a disaster. Registering costs one flip-flop and one clock cycle of latency and removes the problem entirely. This is the same Mealy-versus-Moore argument that Section 7 makes for state machines, in miniature.為什麼要把偵測器輸出暫存起來?因為另一種寫法 assign new_signal = ~sig_a & sig_b; 會產生一個可能出現毛刺的組合輸出:sig_a 與 sig_b 在同一個時脈邊緣改變,但不會在完全相同的瞬間,所以如果 sig_a 比 sig_b 早幾皮秒下降,AND 閘就會短暫看到 (~0) & 1 = 1 而送出一根尖波。在同步設計內部那根尖波無害——它早在下一個邊緣之前就安定了。但若驅動到晶片外,或是被拿去當某處的非同步清除,那就是災難。暫存只花一顆正反器和一個時脈週期的延遲,卻徹底消除這個問題。這就是第 7 節為狀態機所做的那個 Mealy 對 Moore 論證的迷你版。そもそもなぜ検出器の出力をレジスタで受けるのか。代案の assign new_signal = ~sig_a & sig_b; は、グリッチを出しうる組合せ出力になるからだ。sig_a と sig_b は同じクロックエッジで変化するが、まったく同じ瞬間ではない。sig_a が sig_b より数ピコ秒早く落ちると、AND ゲートは一瞬 (~0) & 1 = 1 を見てスパイクを出す。同期設計の内部ならこのスパイクは無害で、次のエッジのはるか前に収まる。チップ外へ出す場合や、どこかの非同期クリアに使う場合は大惨事だ。レジスタで受ければフリップフロップ 1 個とレイテンシ 1 サイクルの代償で問題は完全に消える。これは第 7 節が状態機械について述べるミーリ対ムーアの議論の縮小版である。
sig_a & ~sig_b (new is 1, old was 0).上升緣:sig_a & ~sig_b(新值為 1,舊值為 0)。立ち上がり:sig_a & ~sig_b(新が 1、旧が 0)。~sig_a & sig_b — the slide's version.下降緣:~sig_a & sig_b — 投影片的版本。立ち下がり:~sig_a & sig_b — スライドの版。sig_a ^ sig_b.任意邊緣:sig_a ^ sig_b。任意のエッジ:sig_a ^ sig_b。One caution the slide does not mention: an edge detector only works on a signal that is already synchronous to clock. If Data_in comes from another clock domain or from a chip pin, sig_a can go metastable, and the detector will happily report edges that never happened. The fix is the same two-flip-flop synchroniser as in Section 2.6, placed in front of the detector — which, conveniently, the first two stages of this very chain already are, provided you take the detector from sig_b and sig_c rather than sig_a and sig_b.投影片沒提到的一個警告:邊緣偵測器只對已經與 clock 同步的訊號有效。如果 Data_in 來自另一個時脈域或來自晶片接腳,sig_a 可能進入亞穩態,偵測器就會興高采烈地報出從未發生過的邊緣。修法就是 2.6 節那個兩級正反器同步器,放在偵測器前面——方便的是,這條鏈的前兩級本來就是,只要你改從 sig_b 與 sig_c(而不是 sig_a 與 sig_b)取偵測訊號就行。スライドが触れていない注意点が一つある。エッジ検出器は、すでに clock に同期した信号に対してしか機能しない。Data_in が別のクロックドメインやチップピンから来る場合、sig_a はメタステーブルになりうるし、検出器は起きてもいないエッジを嬉々として報告する。対処は 2.6 節と同じ 2 段フリップフロップの同期化回路を検出器の前に置くこと——好都合なことに、このチェーンの最初の 2 段がすでにそれであり、検出を sig_a と sig_b ではなく sig_b と sig_c から取ればよい。
module barrel_shifter (Data_out, Data_in, load, clock, reset);
output [7:0] Data_out;
input [7:0] Data_in;
input load, clock, reset;
reg [7:0] Data_out;
always @ (posedge reset or posedge clock)
begin
if (reset == 1'b1) Data_out <= 8'b0;
else if (load == 1'b1) Data_out <= Data_in;
else Data_out <= {Data_out[6:0], Data_out[7]};
end
endmodule
Work the concatenation out bit by bit, because the direction is the examinable detail. {Data_out[6:0], Data_out[7]} places old bits 6 down to 0 in positions 7 down to 1, and old bit 7 in position 0. So every bit moves up one position and the top bit wraps around to the bottom: a rotate left by one. The slide's example confirms it:把串接一位元一位元算出來,因為方向才是會考的細節。{Data_out[6:0], Data_out[7]} 把舊的位元 6 到 0 放進位置 7 到 1,把舊的位元 7 放進位置 0。所以每一位元都往上移一格,而最高位繞回到最低位:這是左旋轉一位。投影片的例子證實了這點:連結をビットごとに展開してみよう。向きこそが試験に出る細部だからだ。{Data_out[6:0], Data_out[7]} は古いビット 6〜0 を位置 7〜1 に、古いビット 7 を位置 0 に置く。つまり各ビットは 1 つ上へ動き、最上位が最下位へ回り込む。1 ビット左回転である。スライドの例がそれを裏づけている。
| Bit位元ビット | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| Before之前変化前 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 0 |
| After one clock一個時脈之後クロック 1 回後 | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 1 |
Each bit gets a 2:1 multiplexer in front of its flip-flop — load ? Data_in[i] : Data_out[i-1 mod 8] — and the flip-flop has an asynchronous clear. Eight muxes, eight flip-flops, one inverter on the reset. The synthesis-result slide shows exactly that: a column of eight mux2_a cells feeding a column of eight flip-flops, with the wrap-around wire from bit 7 running back to bit 0.每一位元的正反器前面都有一顆 2:1 多工器——load ? Data_in[i] : Data_out[i-1 mod 8]——而正反器帶有非同步清除。八顆多工器、八顆正反器,重置上一顆反相器。合成結果的投影片顯示的正是這樣:一欄八顆 mux2_a 單元餵給一欄八顆正反器,還有那條從位元 7 繞回位元 0 的環繞線。各ビットのフリップフロップの前には 2:1 マルチプレクサが置かれる——load ? Data_in[i] : Data_out[i-1 mod 8]——そしてフリップフロップは非同期クリア付きだ。マルチプレクサ 8 個、フリップフロップ 8 個、リセットにインバータ 1 個。合成結果のスライドはまさにそれを示す。mux2_a セルが 8 個並んでフリップフロップ 8 個を駆動し、ビット 7 からビット 0 へ戻る回り込み配線がある。
A barrel shifter is a combinational circuit that shifts or rotates its input by an arbitrary amount, specified by a shift-count input, in a single operation. The standard construction is a logarithmic cascade: for an $n$-bit word you build $\log_2 n$ stages of 2:1 multiplexers, where stage $k$ either passes the data through or rotates it by $2^k$, controlled by bit $k$ of the shift count. For $n = 8$ that is three stages of eight multiplexers — 24 muxes, no flip-flops, delay of three mux levels.barrel shifter 是一個組合電路,能在單一次運算中把輸入移位或旋轉任意量,量由一個移位計數輸入指定。標準做法是對數級聯:對一個 $n$ 位元字,你蓋 $\log_2 n$ 級的 2:1 多工器,第 $k$ 級由移位量的第 $k$ 位元控制,要嘛讓資料直通,要嘛把它旋轉 $2^k$。對 $n = 8$ 就是三級、每級八顆多工器——24 顆多工器、零顆正反器、延遲三層多工器。バレルシフタとは、シフト量入力で指定された任意量だけ入力をシフトまたは回転させる、単一演算の組合せ回路である。標準的な構成は対数段のカスケードだ。$n$ ビット語に対し 2:1 マルチプレクサを $\log_2 n$ 段積み、第 $k$ 段はシフト量の第 $k$ ビットで制御されて、データを素通しするか $2^k$ だけ回転させるかを選ぶ。$n = 8$ なら 8 個のマルチプレクサ×3 段——計 24 個、フリップフロップはゼロ、遅延はマルチプレクサ 3 段ぶんである。
What the slide shows is a rotate-by-one register. To rotate by $k$ you clock it $k$ times, so it takes $k$ cycles instead of one gate delay. Both circuits are useful and both appear in real designs, but they are not the same thing and an exam question that asks you to “design a barrel shifter” is asking for the logarithmic mux network. If you are asked to compare them: the register version costs $n$ flip-flops and $n$ muxes and $k$ cycles; the true barrel shifter costs $n\log_2 n$ muxes and zero cycles.投影片展示的其實是一個單位旋轉暫存器。要旋轉 $k$ 位,你得給它 $k$ 個時脈,所以它花的是 $k$ 個週期而不是一層閘延遲。兩種電路都有用,也都出現在真實設計裡,但它們不是同一樣東西;考題若要你「設計一個 barrel shifter」,要的是那個對數級的多工器網路。如果要你比較兩者:暫存器版本花 $n$ 顆正反器、$n$ 顆多工器和 $k$ 個週期;真正的 barrel shifter 花 $n\log_2 n$ 顆多工器和零個週期。スライドが示しているのは実のところ1 ビット回転レジスタである。$k$ ビット回転させるには $k$ 回クロックを入れるので、ゲート 1 段ぶんの遅延ではなく $k$ サイクルかかる。どちらの回路も有用で実際の設計に現れるが、同じものではない。「バレルシフタを設計せよ」という試験問題が求めているのは対数段のマルチプレクサ網である。両者を比較せよと言われたら:レジスタ版はフリップフロップ $n$ 個、マルチプレクサ $n$ 個、$k$ サイクル。真のバレルシフタはマルチプレクサ $n\log_2 n$ 個、サイクルはゼロ。
else4.4 並列載入暫存器,以及那個不見的 else4.4 パラレルロードレジスタと、欠けた elsemodule Par_load_reg4 (Data_out, Data_in, load, clock, reset);
input [3:0] Data_in;
input load, clock, reset;
output [3:0] Data_out;
reg [3:0] Data_out;
always @ (posedge reset or posedge clock)
begin
if (reset == 1'b1) Data_out <= 4'b0;
else if (load == 1'b1) Data_out <= Data_in;
// no final else -- and that is deliberate
end
endmodule
This is the module that exists to make one point, and the point is important enough that it is worth restating in three different ways.這個模組的存在是為了講一個重點,而那個重點重要到值得用三種不同的方式重說一遍。このモジュールは一つの要点を述べるために存在しており、その要点は三通りに言い直す価値があるほど重要である。
There is no final else. When reset is low and load is low, the behaviour assigns nothing, so Data_out keeps its value. In a level-sensitive block that would be a catastrophe: holding a value without a clock requires a transparent latch, and the tool would build four of them, and Lecture 5 spent several slides explaining why that is a bug. In an edge-triggered block it is completely routine: holding a value across a clock edge is the definition of a flip-flop, so nothing extra is needed. The tool implements the hold either as a feedback multiplexer (load ? Data_in : Data_out) in front of a plain flip-flop, which is what the slide's schematic shows, or by picking an enable flip-flop from the library, or — for a wide enough bank — by inserting an integrated clock-gating cell so that the clock itself is suppressed when load is low.沒有最後的 else。當 reset 為低而 load 也為低時,這個行為什麼都不指派,所以 Data_out 保持它的值。在位準敏感區塊裡那會是場災難:沒有時脈卻要保持值就需要透通閂鎖,工具會蓋出四顆,而第 5 講花了好幾張投影片解釋為什麼那是錯誤。在邊緣觸發區塊裡這完全是家常便飯:跨越時脈邊緣保持值正是正反器的定義,所以不需要任何額外的東西。工具會把這個保持實作成普通正反器前面的一顆回授多工器(load ? Data_in : Data_out),也就是投影片電路圖畫的那樣;或是從函式庫挑一顆致能型正反器;或者——當暫存器排夠寬時——插入一顆整合式時脈閘控單元,在 load 為低時直接把時脈本身擋掉。最後の else がない。reset が低で load も低のとき、このビヘイビアは何も代入しないので Data_out は値を保持する。レベル感応ブロックならこれは災厄だ。クロックなしで値を保持するにはトランスペアレントラッチが要り、ツールは 4 個こしらえる。第 5 講はそれがバグである理由に何枚もスライドを費やした。エッジトリガブロックではまったく日常的である。クロックエッジをまたいで値を保持することはフリップフロップの定義そのものなので、余計なものは要らない。ツールはこの保持を、素のフリップフロップの前のフィードバック・マルチプレクサ(load ? Data_in : Data_out、スライドの回路図が示すもの)として実装するか、ライブラリのイネーブル付きフリップフロップを選ぶか、あるいは——十分広いレジスタ群なら——集積クロックゲーティングセルを挿入して load が低のときクロック自体を止める。
An incomplete if in a combinational block infers a latch and is almost always a bug. An incomplete if in a clocked block infers an enable and is almost always what you meant.組合區塊裡不完整的 if 推論出閂鎖,而且幾乎一定是錯誤。時脈區塊裡不完整的 if 推論出致能,而且幾乎一定正是你的本意。組合せブロックの不完全な if はラッチを推論し、ほぼ必ずバグである。クロック同期ブロックの不完全な if はイネーブルを推論し、ほぼ必ず意図どおりである。
The clock-gating option deserves a note because it changes the timing picture. A feedback mux puts a multiplexer in the D path of every bit, which costs area and a little setup margin but leaves the clock alone. An integrated clock-gating cell removes the mux, saves dynamic power on every cycle the register is not loading, and — this is the catch — creates a gated clock that clock-tree synthesis and static timing analysis must handle, with its own setup and hold requirements on the enable signal. Tools do this automatically above a configurable register width (eight or sixteen bits is typical), and it is the single largest dynamic-power saving available in a synchronous design. The difference from the accidental gated clock of Section 1.6 is that here the tool built it, knows about it, and constrained it.時脈閘控這個選項值得加註,因為它改變了時序的樣貌。回授多工器在每一位元的 D 路徑上放一顆多工器,花掉面積和一點 setup 餘裕,但不動時脈。整合式時脈閘控單元拿掉了多工器,在暫存器沒有載入的每一個週期省下動態功耗,而且——這是陷阱——造出一個閘控時脈,時脈樹合成與靜態時序分析都必須處理它,並對致能訊號施加它自己的 setup 與 hold 要求。工具在暫存器寬度超過某個可設定的門檻(八或十六位元是典型值)之後會自動這麼做,而這是同步設計中可取得的最大一筆動態功耗節省。它與 1.6 節那個意外閘控時脈的差別在於:這裡是工具蓋的、工具知道它存在、而且工具約束了它。クロックゲーティングという選択肢はタイミングの様相を変えるので注記しておく。フィードバック・マルチプレクサは全ビットの D 経路にマルチプレクサを置き、面積とわずかなセットアップ余裕を消費するが、クロックには触れない。集積クロックゲーティングセルはマルチプレクサを取り除き、レジスタがロードしていないサイクルごとに動的電力を節約し、そして——ここが落とし穴だが——ゲーテッドクロックを作る。クロックツリー合成とスタティックタイミング解析はそれを扱わねばならず、イネーブル信号には固有の setup と hold の要求が生じる。ツールは設定可能なレジスタ幅(8 ビットや 16 ビットが典型)を超えると自動的にこれを行い、同期設計で得られる動的電力削減としては最大のものである。1.6 節の偶発的ゲーテッドクロックとの違いは、ここではツールが自分で作り、把握し、制約していることだ。
Problem: (a) Shift_reg4 starts at 4'b0000 after reset. Data_in takes the values 1, 1, 0, 1, 0, 0 at six successive rising edges. Tabulate Data_reg and Data_out after each edge. (b) With the same stream, after which edge does shifter_1 first assert new_signal? (c) The barrel shifter holds 8'b1100_0110; what does it hold after three more clocks with load = 0? (d) After how many clocks does it return to its starting value, and what does that tell you about using it as a counter? (e) Rewrite Par_load_reg4 so that it also supports a synchronous “clear” input clr with lower priority than load.題目:(a) Shift_reg4 重置後從 4'b0000 開始。Data_in 在連續六個上升緣依序取值 1, 1, 0, 1, 0, 0。列表給出每個邊緣後的 Data_reg 與 Data_out。(b) 同一串輸入下,shifter_1 第一次確立 new_signal 是在哪個邊緣之後?(c) barrel 移位器持有 8'b1100_0110;在 load = 0 的情況下再過三個時脈,它持有什麼?(d) 要幾個時脈它才回到起始值?這件事告訴你拿它當計數器用會如何?(e) 改寫 Par_load_reg4,讓它也支援一個優先權低於 load 的同步「清除」輸入 clr。問題:(a) Shift_reg4 はリセット後 4'b0000 から始まる。Data_in は連続する 6 回の立ち上がりエッジで 1, 1, 0, 1, 0, 0 の値をとる。各エッジ後の Data_reg と Data_out を表にせよ。(b) 同じ入力列で、shifter_1 が最初に new_signal をアサートするのはどのエッジの後か。(c) barrel シフタが 8'b1100_0110 を保持している。load = 0 のままさらに 3 クロック後、何を保持しているか。(d) 何クロックで初期値に戻るか。そのことはこれをカウンタとして使うことについて何を示すか。(e) Par_load_reg4 を書き換え、load より優先度の低い同期「クリア」入力 clr も備えるようにせよ。
| Edge邊緣エッジ | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
Data_in | 1 | 1 | 0 | 1 | 0 | 0 |
Data_reg after之後的 Data_reg直後の Data_reg | 1000 | 1100 | 0110 | 1011 | 0101 | 0010 |
Data_out | 0 | 0 | 0 | 1 | 1 | 0 |
Read the Data_out row against the Data_in row shifted right by four: input 1 at edge 1 emerges at edge 5? In fact Data_out after edge 4 is the input of edge 1 — four edges of latency, counting the edge that loads it.把 Data_out 那一列對照右移四格的 Data_in 那一列來讀:邊緣 1 的輸入 1 會在邊緣 5 出現嗎?其實邊緣 4 之後的 Data_out 就是邊緣 1 的輸入——四個邊緣的延遲,包含載入它的那個邊緣。Data_out の行を、4 つ右へずらした Data_in の行と対照して読む。エッジ 1 の入力 1 はエッジ 5 で現れるのか。実際にはエッジ 4 の直後の Data_out がエッジ 1 の入力である——それを取り込むエッジを数えて、エッジ 4 つ分のレイテンシだ。
(b) After each edge, sig_a holds the bit just sampled and sig_b the one before. The pairs $(\texttt{sig\_a},\texttt{sig\_b})$ after edges 1–5 are $(1,0), (1,1), (0,1), (1,0), (0,1)$. new_signal is computed from the values present at the edge, i.e. from the pair established one edge earlier, and it needs $(\texttt{sig\_a},\texttt{sig\_b}) = (0,1)$. That pair first exists after edge 3, so new_signal goes high after edge 4. The detector therefore reports the 1→0 transition that occurred between inputs 2 and 3, two edges after the fact: one edge for the input to reach sig_a, one more for the registered AND.(b) 每個邊緣之後,sig_a 保存剛取樣到的位元,sig_b 保存前一個。邊緣 1–5 之後的配對 $(\texttt{sig\_a},\texttt{sig\_b})$ 依序是 $(1,0), (1,1), (0,1), (1,0), (0,1)$。new_signal 是用邊緣當下存在的值算出來的,也就是用前一個邊緣所建立的那組配對,而它需要 $(\texttt{sig\_a},\texttt{sig\_b}) = (0,1)$。那組配對最早在邊緣 3 之後存在,所以 new_signal 在邊緣 4 之後拉高。因此偵測器是在事發兩個邊緣之後,報出發生在輸入 2 與 3 之間的那個 1→0 轉換:一個邊緣讓輸入走到 sig_a,再一個邊緣給那顆暫存化的 AND。(b) 各エッジの後、sig_a は直前にサンプルしたビットを、sig_b はその一つ前を保持する。エッジ 1–5 の後の組 $(\texttt{sig\_a},\texttt{sig\_b})$ は順に $(1,0), (1,1), (0,1), (1,0), (0,1)$ である。new_signal はエッジ時点で存在する値、すなわち一つ前のエッジで確定した組から計算され、$(\texttt{sig\_a},\texttt{sig\_b}) = (0,1)$ を必要とする。その組が最初に存在するのはエッジ 3 の後なので、new_signal はエッジ 4 の後に高くなる。つまり検出器は、入力 2 と 3 のあいだで起きた 1→0 遷移を 2 エッジ遅れて報告する。入力が sig_a に届くのに 1 エッジ、レジスタ付き AND にもう 1 エッジである。
(c) Rotate left three times: 1100_0110 → 1000_1101 → 0001_1011 → 0011_0110.(c) 左旋轉三次:1100_0110 → 1000_1101 → 0001_1011 → 0011_0110。(c) 左に 3 回回転させる:1100_0110 → 1000_1101 → 0001_1011 → 0011_0110。
(d) After 8 clocks, always — rotating an 8-bit word by 8 is the identity. So the circuit has a period of 8 regardless of its contents, and any pattern you load defines a cycle of length 8 (or a divisor of 8, for patterns with internal symmetry such as 1010_1010, which repeats every 2). As a counter it is a ring counter with an arbitrary loaded pattern: it gives you at most 8 distinguishable states from 8 flip-flops, where a binary counter would give 256. You use it when you want the decoded, glitch-free, one-bit-per-state output, not when you want counting range.(d) 永遠是 8 個時脈之後——把一個 8 位元字旋轉 8 次就是恆等運算。所以這個電路的週期是 8,與內容無關,而你載入的任何圖樣都定義出一個長度 8 的循環(對於 1010_1010 這種有內部對稱性的圖樣,則是 8 的因數,它每 2 拍就重複)。當計數器用時,它是一個載入任意圖樣的環形計數器:8 顆正反器最多只給你 8 個可分辨的狀態,而二進位計數器會給你 256 個。你會在想要那種已解碼、無毛刺、一狀態一位元的輸出時用它,而不是在想要計數範圍時用它。(d) つねに 8 クロック後である——8 ビット語を 8 回回転させるのは恒等変換だ。したがってこの回路の周期は中身によらず 8 であり、ロードしたどんなパターンも長さ 8 の巡回を定める(1010_1010 のように内部対称性をもつパターンでは 8 の約数、この例なら 2 ごとに繰り返す)。カウンタとして見れば、任意のパターンをロードしたリングカウンタである。フリップフロップ 8 個から区別できる状態はせいぜい 8 で、二進カウンタなら 256 得られる。使うのは、デコード済みでグリッチのない 1 状態 1 ビットの出力が欲しいときであって、計数範囲が欲しいときではない。
(e) Insert the new branch after load, since priority follows source order:(e) 把新分支插在 load 之後,因為優先權依原始碼順序決定:(e) 優先度はソース順に従うので、新しい分岐は load の後に挿入する。
always @ (posedge reset or posedge clock) if (reset == 1'b1) Data_out <= 4'b0; // async, highest priority else if (load == 1'b1) Data_out <= Data_in; else if (clr == 1'b1) Data_out <= 4'b0; // sync clear // still no final else: hold
Note that clr is inside the clocked branch, so it is a synchronous clear and shares the data-path mux; reset remains asynchronous on the CLR pin. Mixing the two in one register like this is perfectly normal.注意 clr 位於時脈分支之內,所以它是同步清除,並且共用資料路徑的多工器;reset 則仍是 CLR 腳上的非同步重置。像這樣把兩者混在同一個暫存器裡,完全是正常做法。clr がクロック同期の分岐の内側にある点に注意。これは同期クリアであり、データパスのマルチプレクサを共有する。reset のほうは CLR ピン上の非同期リセットのままだ。このように一つのレジスタで両者を併用するのはごく普通のことである。
Counters are the first place in this lecture where a perfectly correct Verilog description produces a circuit that an ASIC methodology will reject. Both of the slide's counters work; one of them is a structure you should recognise so that you can avoid it.計數器是本講中第一個「完全正確的 Verilog 描述卻產生出 ASIC 方法論會拒絕的電路」的地方。投影片的兩個計數器都能動;其中一個是你應該認得出來、好讓自己避開的結構。カウンタは本講で初めて、まったく正しい Verilog 記述が ASIC の方法論に拒絶される回路を生む場所である。スライドの二つのカウンタはどちらも動く。片方は、避けられるように見分けがつくようにしておくべき構造だ。
module ripple_counter (clock, toggle, reset, count); input clock, toggle, reset; output [3:0] count; reg [3:0] count; wire c0, c1, c2; assign c0 = count[0]; assign c1 = count[1]; assign c2 = count[2]; always @ (posedge reset or posedge clock) if (reset == 1'b1) count[0] <= 1'b0; else if (toggle == 1'b1) count[0] <= ~count[0]; always @ (posedge reset or negedge c0) if (reset == 1'b1) count[1] <= 1'b0; else if (toggle == 1'b1) count[1] <= ~count[1]; always @ (posedge reset or negedge c1) if (reset == 1'b1) count[2] <= 1'b0; else if (toggle == 1'b1) count[2] <= ~count[2]; always @ (posedge reset or negedge c2) if (reset == 1'b1) count[3] <= 1'b0; else if (toggle == 1'b1) count[3] <= ~count[3]; endmodule
Four separate behaviours, one per bit. Each is a textbook toggle flip-flop: when enabled, invert. The thing to notice is the second signal in each event control. Stage 0 is clocked by clock. Stage 1 is clocked by the falling edge of count[0]. Stage 2 by the falling edge of count[1], and stage 3 by the falling edge of count[2]. Nothing except stage 0 is connected to the real clock at all.四個分開的行為,每位元一個。每一個都是教科書式的 toggle 正反器:被致能時就反相。要注意的是每個事件控制裡的第二個訊號。第 0 級由 clock 驅動。第 1 級由 count[0] 的下降緣驅動。第 2 級由 count[1] 的下降緣,第 3 級由 count[2] 的下降緣。除了第 0 級之外,沒有任何一級接到真正的時脈。ビットごとに一つ、計四つの独立したビヘイビア。いずれも教科書的なトグル・フリップフロップで、イネーブル時に反転する。注目すべきは各イベント制御の二番目の信号だ。第 0 段は clock で駆動される。第 1 段は count[0] の立ち下がり、第 2 段は count[1] の立ち下がり、第 3 段は count[2] の立ち下がりで駆動される。第 0 段以外、本物のクロックに繋がっているものは一つもない。
Why does this count? A toggle flip-flop that inverts on every enabled clock produces an output at exactly half the input frequency. Chain four of them and you get divisions by 2, 4, 8 and 16 — which, read as a binary word, is an up-counter. The choice of negedge rather than posedge as the inter-stage trigger is what makes it count up: bit $i+1$ must toggle when bit $i$ goes from 1 to 0, which is precisely when a binary carry propagates out of position $i$. Trigger on posedge instead and you get a down-counter.為什麼它會計數?一顆在每個被致能的時脈都反相的 toggle 正反器,產生的輸出頻率恰好是輸入的一半。把四顆串起來就得到除 2、4、8、16——把它們當成二進位字來讀,就是一個上數計數器。選 negedge 而不是 posedge 當作級間觸發,正是讓它往上數的原因:位元 $i+1$ 必須在位元 $i$ 從 1 變 0 時反轉,而那正好是二進位進位從位置 $i$ 傳出去的時刻。改成 posedge 觸發就得到下數計數器。なぜ数えられるのか。イネーブルされたクロックごとに反転するトグル・フリップフロップは、入力のちょうど半分の周波数の出力を生む。これを 4 段つなげば 2、4、8、16 分周が得られ、二進語として読めばアップカウンタになる。段間トリガに posedge ではなく negedge を選ぶことこそが上へ数える理由だ。ビット $i+1$ はビット $i$ が 1 から 0 になるとき反転しなければならず、それはまさに二進のキャリーが位置 $i$ から出ていく瞬間である。posedge でトリガすればダウンカウンタになる。
The toggle input is a count enable; the intermediate wires c0, c1, c2 exist only because the original Verilog-1995 rules do not let you put a bit-select of a reg directly in an edge qualifier in all tools, so the code aliases them to nets first. Note also that each stage has an incomplete if — when toggle is low nothing is assigned, so the bit holds, exactly as in Section 4.4.toggle 輸入是計數致能;中間的連線 c0、c1、c2 之所以存在,只是因為原本的 Verilog-1995 規則並不是在所有工具裡都允許你把 reg 的位元選擇直接放進邊緣修飾詞,所以程式碼先把它們別名成網路。另外注意每一級都有一個不完整的 if——當 toggle 為低時什麼都不指派,所以該位元保持,和 4.4 節完全一樣。toggle 入力はカウントイネーブルである。中間の配線 c0、c1、c2 が存在するのは、もともとの Verilog-1995 の規則では reg のビット選択をエッジ修飾子に直接書くことが全ツールで許されるわけではないからで、コードはまずそれらをネットに別名付けしている。また各段には不完全な if がある点にも注意——toggle が低のときは何も代入されないので、そのビットは保持される。4.4 節とまったく同じだ。
The synthesis result on the slide shows what you would expect: each toggle flip-flop realised as a plain D flip-flop with an XOR at its D pin computing toggle ^ Q, an inverter on the reset, and — the significant part — the clock pin of each stage driven by the previous stage's Q through an inverter.投影片上的合成結果正如你所預期:每顆 toggle 正反器被實現成一顆普通 D 正反器,D 腳上掛一顆算 toggle ^ Q 的 XOR,重置上一顆反相器,而——這是關鍵部分——每一級的時脈腳是由前一級的 Q 經過一顆反相器驅動的。スライドの合成結果は予想どおりである。各トグル・フリップフロップは、D ピンで toggle ^ Q を計算する XOR を伴う素の D フリップフロップとして実現され、リセットにはインバータが付く。そして——ここが肝心だが——各段のクロックピンは前段の Q がインバータを介して駆動している。
The circuit is small: four flip-flops, four XORs, three inverters, and no carry logic at all. A synchronous four-bit counter needs an incrementer, which for four bits is about the same size, but the gap widens quickly with width — a 32-bit ripple counter is still 32 toggle cells, while a 32-bit synchronous counter needs a real carry chain. So why does every ASIC design guide forbid ripple counters? Five reasons, in roughly descending order of how much trouble they cause.這個電路很小:四顆正反器、四顆 XOR、三顆反相器,完全沒有進位邏輯。一個同步四位元計數器需要一個遞增器,四位元時大小差不多,但差距會隨位元寬度迅速拉開——32 位元的漣波計數器仍然只是 32 顆 toggle 單元,而 32 位元的同步計數器需要一條真正的進位鏈。那為什麼每一份 ASIC 設計指南都禁止漣波計數器?五個理由,大致依照惹麻煩的程度由大到小排列。この回路は小さい。フリップフロップ 4 個、XOR 4 個、インバータ 3 個、キャリー論理はゼロ。同期 4 ビットカウンタにはインクリメンタが要り、4 ビットなら大きさはほぼ同じだが、幅が増えると差は急速に開く——32 ビットのリプルカウンタは依然トグルセル 32 個だが、32 ビットの同期カウンタには本物のキャリーチェーンが必要だ。ではなぜ、どの ASIC 設計指針もリプルカウンタを禁じるのか。理由は五つ、引き起こす面倒の大きい順におおむね並べる。
Each stage adds one clock-to-Q delay before the next stage even sees its clock edge. For a 4-bit counter the worst case — the transition from 0111 to 1000, where the carry ripples all the way — takes $4\,t_{cq}$ to settle. With $t_{cq} = 150$ ps that is 600 ps, which is a large fraction of a fast clock period. Any logic that reads count must wait for all of it, and the counter's maximum frequency is bounded by $n\,t_{cq}$ rather than by one stage.每一級都在下一級看到自己的時脈邊緣之前,先加上一個 clock-to-Q 延遲。對一個 4 位元計數器,最糟情況——從 0111 到 1000,進位一路傳到底——要花 $4\,t_{cq}$ 才安定。取 $t_{cq} = 150$ ps 就是 600 ps,那在快時脈下佔週期的一大部分。任何讀 count 的邏輯都必須等完這段時間,而計數器的最高頻率受限於 $n\,t_{cq}$ 而不是單一級。各段は、次の段が自分のクロックエッジを見るより前に clock-to-Q 遅延を一つ足す。4 ビットカウンタの最悪ケース——キャリーが端まで伝わる 0111 から 1000 への遷移——は整定に $4\,t_{cq}$ かかる。$t_{cq} = 150$ ps なら 600 ps で、高速クロックでは周期のかなりの割合を占める。count を読む論理はすべてその全部を待たねばならず、カウンタの最高周波数は 1 段ではなく $n\,t_{cq}$ で律速される。
During that settling window the counter passes through values that are not in its intended sequence. Going from 0111 to 1000, bit 0 falls first (0110), then bit 1 (0100), then bit 2 (0000), then bit 3 rises (1000). A comparator watching for count == 4'b0000 will fire a spurious pulse. A one-hot decoder will light up three wrong outputs in turn. In a synchronous design those spikes are filtered by the next register stage, but if the decode drives anything asynchronous — a write-enable, a reset, an off-chip strobe — it is a real failure.在那段安定窗口裡,計數器會經過一些不在它預定序列中的值。從 0111 走到 1000 時,位元 0 先落(0110),接著位元 1(0100),接著位元 2(0000),最後位元 3 升起(1000)。一個在等 count == 4'b0000 的比較器會送出一道假脈衝。一個 one-hot 解碼器會依序點亮三個錯誤的輸出。在同步設計裡那些尖波會被下一級暫存器濾掉,但如果那個解碼去驅動任何非同步的東西——寫入致能、重置、晶片外的 strobe——那就是真正的失效。その整定の窓のあいだ、カウンタは本来の計数列にない値を通過する。0111 から 1000 へ向かうとき、まずビット 0 が落ち(0110)、次にビット 1(0100)、次にビット 2(0000)、最後にビット 3 が立つ(1000)。count == 4'b0000 を監視する比較器は偽のパルスを出す。one-hot デコーダは誤った出力を順に三つ点灯させる。同期設計内ならこれらのスパイクは次段のレジスタが濾過するが、そのデコードが非同期の何か——ライトイネーブル、リセット、チップ外ストローブ——を駆動するなら、本物の故障である。
Every stage after the first is clocked by a net that the tool did not know was a clock. To analyse the design at all you must declare three create_generated_clock constraints, one per stage, each derived from the previous. Those generated clocks have no clock tree, so their skew relative to the main clock is whatever the placement happened to produce; cross-domain paths between stages become unconstrained or falsely violated; and every time the design is re-placed, the numbers move. Most teams simply ban the structure rather than maintain the constraints.第一級之後的每一級,都是由工具原本不知道是時脈的網路所驅動。光是要能分析這個設計,你就必須宣告三條 create_generated_clock 約束,每級一條,各自由前一級導出。那些衍生時脈沒有時脈樹,所以它們相對於主時脈的歪斜就是擺放結果碰巧給出的值;級與級之間的跨域路徑會變成未受約束或假違規;而且每次重新擺放,數字就會跑掉。多數團隊乾脆禁用這個結構,而不是去維護那些約束。初段より後のすべての段は、ツールがクロックだと知らなかったネットで駆動される。設計を解析できるようにするだけでも、段ごとに一本、前段から導出した create_generated_clock 制約を三本宣言しなければならない。それらの生成クロックにはクロックツリーがないので、主クロックに対するスキューは配置がたまたま生んだ値になる。段間のドメイン跨ぎ経路は制約なしか偽違反になり、再配置のたびに数値が動く。ほとんどのチームは制約を保守するより、この構造を禁止するほうを選ぶ。
Scan insertion stitches flip-flops into shift chains, and every flip-flop in a chain must share a clock. A ripple counter puts each bit in its own clock domain, so either you get four one-element chains (unacceptable overhead) or the stages are left out of scan entirely (unacceptable coverage). Fault coverage reports will show the counter as a hole.掃描插入會把正反器縫成移位鏈,而鏈中每一顆正反器都必須共用同一個時脈。漣波計數器把每一位元放進它自己的時脈域,所以你要嘛得到四條只有一個元素的鏈(開銷不可接受),要嘛那些級完全被排除在掃描之外(覆蓋率不可接受)。故障覆蓋率報告會把這個計數器顯示成一個洞。スキャン挿入はフリップフロップをシフトチェーンに縫い込むが、一本のチェーンのフリップフロップはすべて同じクロックを共有しなければならない。リプルカウンタは各ビットを固有のクロックドメインに置くので、要素 1 個のチェーンが 4 本できる(オーバーヘッドが許容不能)か、段がスキャンから完全に外される(カバレッジが許容不能)かのどちらかになる。故障カバレッジのレポートには、このカウンタが穴として現れる。
Because the stages have no common clock, there is no clock edge at which they all leave reset. Section 2.5's reset-release analysis has nothing to work with.因為各級沒有共同的時脈,所以根本不存在一個「大家一起離開重置」的時脈邊緣。2.5 節的重置釋放分析在這裡沒有任何著力點。各段に共通のクロックがないので、全員がリセットを抜ける単一のクロックエッジというものが存在しない。2.5 節のリセット解除の解析は、ここでは手がかりを持たない。
always @ (posedge clk or negedge rst_n)
if (!rst_n) count <= 4'b0;
else if (toggle) count <= count + 1'b1; // one clock, all bits
That version has all four flip-flops on the same clock, one carry chain of combinational logic in front of them, a single clock domain for STA and scan, and — because all four bits change at the same edge — a settling time of $t_{cq}$ plus the incrementer delay rather than $4\,t_{cq}$. It is bigger in gate count and smaller in every other sense. Ripple counters survive in two niches: very low-power always-on timers where the dynamic power of a synchronous carry chain matters more than anything else, and discrete logic, where the inter-stage delay is swamped by board parasitics anyway.那個版本讓四顆正反器都在同一個時脈上,前面擺一條組合邏輯的進位鏈,對 STA 與掃描而言只有單一時脈域,而且——因為四個位元都在同一個邊緣改變——安定時間是 $t_{cq}$ 加上遞增器延遲,而不是 $4\,t_{cq}$。它在閘數上比較大,在其他每一個意義上都比較小。漣波計數器只在兩個利基中存活:極低功耗的常開計時器,那裡同步進位鏈的動態功耗比什麼都重要;以及分立邏輯,那裡級間延遲反正會被板子的寄生效應淹沒。その版では 4 個のフリップフロップが同一クロック上にあり、その前に組合せ論理のキャリーチェーンが一本置かれ、STA にもスキャンにも単一のクロックドメインしかない。そして——4 ビットが同じエッジで変化するので——整定時間は $4\,t_{cq}$ ではなく $t_{cq}$ にインクリメンタの遅延を足したものになる。ゲート数では大きく、他のあらゆる意味では小さい。リプルカウンタが生き残るニッチは二つ。同期キャリーチェーンの動的電力が何より効く超低消費電力の常時オンタイマと、段間遅延がどうせ基板の寄生に埋もれるディスクリート論理である。
module ring_counter (count, enable, clock, reset);
output [7:0] count;
input enable, reset, clock;
reg [7:0] count;
always @ (posedge reset or posedge clock)
if (reset == 1'b1) count <= 8'b0000_0001;
else if (enable == 1'b1) count <= {count[6:0], count[7]};
endmodule
Structurally this is the rotate register of Section 4.3 with a different reset value, and that reset value is the whole point. Starting from 8'b0000_0001, rotating left gives 0000_0010, 0000_0100, …, 1000_0000, and then back to 0000_0001. A single 1 walks around the ring, taking eight clocks per lap. Eight flip-flops, eight states, and each state is already decoded: count[k] is high if and only if the machine is in state $k$. There is no decoder, so there are no decoding glitches, and the state outputs are flip-flop outputs so they are clean enough to drive anything.結構上這就是 4.3 節那個旋轉暫存器,只是換了重置值,而那個重置值正是整件事的重點。從 8'b0000_0001 開始,左旋轉得到 0000_0010、0000_0100、…、1000_0000,然後回到 0000_0001。一個孤單的 1 繞著環走,每一圈八個時脈。八顆正反器、八個狀態,而每個狀態都已經解碼好了:count[k] 為高,若且唯若機器處於狀態 $k$。沒有解碼器,所以沒有解碼毛刺,而且狀態輸出本身就是正反器輸出,乾淨到足以驅動任何東西。構造としてはこれは 4.3 節の回転レジスタそのもので、違うのはリセット値だけ——そしてそのリセット値こそが要点である。8'b0000_0001 から始めて左回転すると 0000_0010、0000_0100、…、1000_0000、そして 0000_0001 に戻る。たった一つの 1 が環を巡り、一周に 8 クロックかかる。フリップフロップ 8 個、状態 8 個、しかも各状態はすでにデコード済みだ。count[k] が高であるのは、機械が状態 $k$ にあるとき、かつそのときに限る。デコーダがないのでデコードのグリッチもなく、状態出力はフリップフロップ出力そのものなので何を駆動するにも十分きれいである。
That makes a ring counter the natural choice for a timing generator — the “gate generator”, “pulse generator” and “timing generator” entries in the Section 1.2 inventory are usually ring counters. It is also exactly a one-hot state machine with a fixed, cyclic transition graph, which is why Section 8 can treat one-hot encoding and ring counters as the same idea.這使環形計數器成為時序產生器的自然選擇——1.2 節清單裡的「閘控產生器」、「脈衝產生器」和「時序產生器」通常就是環形計數器。它同時也正是一台具有固定循環轉移圖的 one-hot 狀態機,這就是為什麼第 8 節可以把 one-hot 編碼和環形計數器當成同一個想法來談。そのためリングカウンタはタイミング生成器の自然な選択肢になる——1.2 節の一覧にある「ゲート生成器」「パルス生成器」「タイミング生成器」はたいていリングカウンタである。また、固定の巡回遷移グラフをもつ one-hot 状態機械そのものでもあり、第 8 節が one-hot 符号化とリングカウンタを同じ考えとして扱えるのはそのためだ。
Seven of the eight flip-flops reset to 0 and are implemented with asynchronously-clearable cells. Bit 0 resets to 1, which needs an asynchronously presettable cell — DFFS rather than DFFR, or in the slide's library dffspb_a next to seven dffqpb_a. If the library has no preset flip-flop, the tool has to invert the bit's polarity internally and add inverters at its inputs and outputs, which is why a one-hot reset value is marginally more expensive than an all-zeros one. Some designs dodge this by resetting to all zeros and treating that as an extra ninth “idle” state, seeding the 1 with a separate start pulse.八顆正反器中有七顆重置為 0,用可非同步清除的單元實現。位元 0 重置為 1,需要一顆可非同步預設的單元——DFFS 而不是 DFFR,在投影片的函式庫裡就是一顆 dffspb_a 旁邊七顆 dffqpb_a。如果函式庫沒有 preset 型正反器,工具就必須在內部把那一位元的極性反過來,並在它的輸入與輸出端加反相器,這就是為什麼 one-hot 的重置值會比全零的重置值稍微貴一點。有些設計會繞過這點:重置成全零,把那當成額外的第九個「閒置」狀態,再用一道獨立的啟動脈衝去種下那個 1。8 個のフリップフロップのうち 7 個は 0 にリセットされ、非同期クリア可能なセルで実装される。ビット 0 は 1 にリセットされるので、非同期プリセット可能なセルが要る——DFFR ではなく DFFS、スライドのライブラリでいえば dffqpb_a 7 個の隣に dffspb_a 1 個である。ライブラリにプリセット付きフリップフロップがなければ、ツールはそのビットの極性を内部で反転し、入力と出力にインバータを足さねばならない。one-hot のリセット値が全ゼロよりわずかに高くつくのはこのためだ。全ゼロにリセットしてそれを九つ目の「アイドル」状態と見なし、別途の起動パルスで 1 を植え付けることでこれを回避する設計もある。
This is the examinable weakness. The ring counter has $2^8 = 256$ possible flip-flop states but only 8 of them are legal. The rotate operation maps every state to another state with the same number of ones, so the 256 states decompose into closed orbits: the all-zeros state is a fixed point, the all-ones state is a fixed point, and every other population count forms one or more cycles. If noise, a reset-release skew, or a single-event upset puts two ones into the register, the pattern simply rotates forever with two ones in it, and every downstream decoder sees two states active simultaneously. Nothing in the circuit pulls it back; only reset recovers.這是會考的弱點。環形計數器有 $2^8 = 256$ 種可能的正反器狀態,但其中只有 8 種是合法的。旋轉運算把每個狀態對映到另一個具有相同 1 的個數的狀態,所以這 256 個狀態會分解成封閉的軌道:全零狀態是一個不動點,全一狀態是一個不動點,而其他每一種 1 的個數都形成一個或多個循環。如果雜訊、重置釋放的歪斜,或一次單事件翻轉把兩個 1 放進暫存器裡,那個圖樣就會帶著兩個 1 永遠旋轉下去,而每一個下游解碼器都會同時看到兩個狀態是作用中的。電路裡沒有任何東西會把它拉回來;只有 reset 能救。これが試験に出る弱点である。リングカウンタには $2^8 = 256$ 通りのフリップフロップ状態がありうるが、合法なのはそのうち 8 通りだけだ。回転操作は各状態を同じ 1 の個数をもつ別の状態へ写すので、256 状態は閉じた軌道に分解される。全ゼロ状態は不動点、全 1 状態も不動点、それ以外の各 1 個数は一つ以上の巡回をなす。ノイズやリセット解除のスキュー、あるいは単一事象アップセットがレジスタに 1 を二つ入れてしまうと、そのパターンは 1 を二つ抱えたまま永遠に回り続け、下流のデコーダはどれも二つの状態が同時に有効だと見る。回路の中にそれを引き戻すものは何もない。救えるのは reset だけである。
If that matters, make it self-correcting by feeding bit 0 with the NOR of the other seven bits instead of with bit 7:如果這件事要緊,就把位元 0 的輸入改成其他七個位元的 NOR,而不是位元 7,讓它能自我修正:それが問題になるなら、ビット 0 の入力をビット 7 ではなく他の 7 ビットの NOR にして、自己修正するようにすればよい。
always @ (posedge clk or negedge rst_n)
if (!rst_n) count <= 8'b0000_0001;
else if (enable) count <= {count[6:0], ~|count[7:1]};
Now any state with two or more ones loses one every lap until exactly one remains, and the all-zeros state injects a 1 at the bottom. The cost is one 7-input NOR in the path to bit 0, which also makes that one path slower than the other seven.現在任何有兩個以上 1 的狀態,每繞一圈就少掉一個,直到剛好剩下一個為止;而全零狀態會在最低位注入一個 1。代價是位元 0 的路徑上多了一顆 7 輸入 NOR,這也讓那條路徑比其他七條慢。こうすると、1 が二つ以上ある状態は一周ごとに一つずつ失い、ちょうど一つ残るまで減っていく。そして全ゼロ状態には最下位に 1 が注入される。代償はビット 0 への経路に入る 7 入力 NOR 1 個で、その経路だけが他の七本より遅くなる。
A Johnson (twisted-ring, Moebius) counter feeds back the complement of the top bit: count <= {count[6:0], ~count[7]}. Starting from all zeros it fills with ones from the bottom, then empties again, giving $2n$ states from $n$ flip-flops instead of $n$. Its outputs still change one bit at a time, so decoding is still glitch-free, and a Johnson state can be decoded with a two-input gate rather than the $n$-input gate a binary counter would need. The slide's encoding table shows the Johnson sequence for 16 states from 8 bits. The price is the same lack of self-correction and the same poor state-per-flip-flop efficiency.Johnson(扭環、Moebius)計數器回授的是最高位的補數:count <= {count[6:0], ~count[7]}。從全零開始,它會從最低位開始填滿 1,然後再排空,於是 $n$ 顆正反器給出 $2n$ 個狀態而不是 $n$ 個。它的輸出一次仍然只變動一個位元,所以解碼仍然無毛刺,而且一個 Johnson 狀態可以用兩輸入閘解碼,不像二進位計數器需要 $n$ 輸入閘。投影片的編碼表列出了 8 位元 16 狀態的 Johnson 序列。代價是同樣缺乏自我修正,以及同樣糟糕的「每顆正反器能表示幾個狀態」效率。ジョンソン(ツイストリング、メビウス)カウンタは最上位ビットの補数を帰還させる:count <= {count[6:0], ~count[7]}。全ゼロから始めると下位から 1 で埋まり、やがてまた空になるので、$n$ 個のフリップフロップから $n$ ではなく $2n$ 個の状態が得られる。出力は依然として一度に 1 ビットしか変化しないのでデコードはグリッチフリーのままで、しかもジョンソン状態は、二進カウンタが $n$ 入力ゲートを要するところを 2 入力ゲートでデコードできる。スライドの符号化表は 8 ビットで 16 状態のジョンソン系列を示している。代償は、同じく自己修正がないことと、同じくフリップフロップあたりの状態数の効率が悪いことである。
| Counter計數器カウンタ | FFs for $N$ states$N$ 個狀態所需正反器數$N$ 状態に要する FF 数 | Logic in the loop迴路中的邏輯ループ内の論理 | Decode for one state解碼單一狀態1 状態のデコード | Glitch-free outputs輸出無毛刺出力がグリッチフリー |
|---|---|---|---|---|
| Binary (synchronous)二進位(同步)二進(同期) | $\lceil\log_2 N\rceil$ | Incrementer / carry chain遞增器/進位鏈インクリメンタ/キャリーチェーン | $\lceil\log_2 N\rceil$-input AND$\lceil\log_2 N\rceil$ 輸入的 AND$\lceil\log_2 N\rceil$ 入力 AND | No (needs a register)否(需要再加一級暫存器)いいえ(レジスタが必要) |
| Ring環形リング | $N$ | None (a wire)無(一條連線)なし(ただの配線) | None — it is a flip-flop output無 — 它本來就是正反器輸出不要 — フリップフロップ出力そのもの | Yes是はい |
| Johnson | $N/2$ | One inverter一顆反相器インバータ 1 個 | 2-input gate兩輸入閘2 入力ゲート | Yes是はい |
| Ripple漣波リプル | $\lceil\log_2 N\rceil$ | None, but derived clocks無,但會產生衍生時脈なし、ただし派生クロックが生じる | $\lceil\log_2 N\rceil$-input AND$\lceil\log_2 N\rceil$ 輸入的 AND$\lceil\log_2 N\rceil$ 入力 AND | No — worst of all否 — 四者之中最差いいえ — 四者中もっとも悪い |
Problem: (a) The ripple counter holds 0111 and the next clock edge arrives with toggle = 1. List every intermediate value the count bus passes through before settling, and say how long settling takes if $t_{cq} = 150$ ps. (b) A comparator assign hit = (count == 4'd0); watches the bus and hit is used as a write enable for a RAM. What goes wrong and how would you fix it without abandoning the ripple counter? (c) An 8-state ring counter and a 3-bit binary counter both drive an 8-way one-hot decode. Compare flip-flop count, decode logic, and output glitching. (d) The ring counter is hit by a particle strike that flips count[3] from 0 to 1 while count[0] is the active bit. Describe the behaviour for the next twelve clocks, with and without the self-correcting feedback.題目:(a) 漣波計數器持有 0111,下一個時脈邊緣到來時 toggle = 1。列出 count 匯流排在安定前經過的每一個中間值,並說明若 $t_{cq} = 150$ ps 安定要花多久。(b) 一個比較器 assign hit = (count == 4'd0); 監看這條匯流排,而 hit 被用作某顆 RAM 的寫入致能。會出什麼問題?在不放棄漣波計數器的前提下你會怎麼修?(c) 一個 8 狀態環形計數器與一個 3 位元二進位計數器都要驅動一組 8 路 one-hot 解碼。比較正反器數量、解碼邏輯與輸出毛刺。(d) 環形計數器在 count[0] 是作用位元時被一次粒子撞擊,把 count[3] 從 0 翻成 1。請描述接下來十二個時脈的行為,分別就「有」與「沒有」自我修正回授兩種情況。問題:(a) リプルカウンタが 0111 を保持しており、次のクロックエッジが toggle = 1 で到来する。count バスが整定までに通過する中間値をすべて列挙し、$t_{cq} = 150$ ps のとき整定に要する時間を述べよ。(b) 比較器 assign hit = (count == 4'd0); がこのバスを監視し、hit が RAM のライトイネーブルに使われている。何が起きるか。リプルカウンタを捨てずにどう直すか。(c) 8 状態のリングカウンタと 3 ビット二進カウンタがともに 8 路の one-hot デコードを駆動する。フリップフロップ数、デコード論理、出力グリッチを比較せよ。(d) count[0] が有効ビットのときに粒子線がリングカウンタを直撃し、count[3] が 0 から 1 に反転した。自己修正帰還がある場合とない場合について、以後 12 クロックの振る舞いを述べよ。
(a) 0111 → 0110 (bit 0 falls at $t_{cq}$) → 0100 (bit 1 falls at $2t_{cq}$) → 0000 (bit 2 falls at $3t_{cq}$) → 1000 (bit 3 rises at $4t_{cq}$). Three values appear that are not part of the counting sequence, and settling takes $4 \times 150 = 600$ ps. On a 1 GHz clock that is 60 % of the period gone before the bus is even valid.(a) 0111 → 0110(位元 0 在 $t_{cq}$ 落下)→ 0100(位元 1 在 $2t_{cq}$ 落下)→ 0000(位元 2 在 $3t_{cq}$ 落下)→ 1000(位元 3 在 $4t_{cq}$ 升起)。有三個值出現,而它們都不屬於計數序列;安定要花 $4 \times 150 = 600$ ps。在 1 GHz 時脈下,匯流排都還沒有效,週期就已經去掉 60 %。(a) 0111 → 0110(ビット 0 が $t_{cq}$ で落ちる)→ 0100(ビット 1 が $2t_{cq}$)→ 0000(ビット 2 が $3t_{cq}$)→ 1000(ビット 3 が $4t_{cq}$ で立つ)。計数列に属さない値が三つ現れ、整定には $4 \times 150 = 600$ ps かかる。1 GHz のクロックでは、バスが有効になる前に周期の 60 % が消える。
(b) count passes through 0000 on its way from 0111 to 1000, so hit pulses high for one $t_{cq}$ at a moment when the counter is not actually at zero. Used directly as a RAM write enable, that spurious pulse writes a word that should not have been written. The fix is to register the decode: always @(posedge clk) hit <= (count == 4'd0);. The flip-flop samples only at the clock edge, long after the ripple has settled, so the glitch is invisible. This costs one cycle of latency and is the same medicine as registering the edge detector in Section 4.2. (The real fix is of course to use a synchronous counter.)(b) count 在從 0111 走到 1000 的途中經過 0000,所以 hit 會在計數器其實不在零的時刻拉高一個 $t_{cq}$ 的脈衝。直接拿去當 RAM 寫入致能,那道假脈衝就會寫進一個本來不該被寫的字。修法是把解碼暫存起來:always @(posedge clk) hit <= (count == 4'd0);。正反器只在時脈邊緣取樣,那時漣波早已安定,毛刺就看不見了。代價是一個週期的延遲,和 4.2 節把邊緣偵測器暫存起來是同一帖藥。(真正的修法當然是改用同步計數器。)(b) count は 0111 から 1000 へ向かう途中で 0000 を通過するので、カウンタが実際にはゼロでない瞬間に hit が $t_{cq}$ だけ高になる。これをそのまま RAM のライトイネーブルに使えば、その偽パルスが書かれるべきでない語を書き込む。直し方はデコードをレジスタで受けること:always @(posedge clk) hit <= (count == 4'd0);。フリップフロップはクロックエッジでしかサンプルせず、そのときリプルはとうに整定しているので、グリッチは見えない。代償はレイテンシ 1 サイクルで、4.2 節でエッジ検出器をレジスタで受けたのと同じ処方である。(本当の直し方はもちろん同期カウンタを使うことだ。)
(c) Ring: 8 flip-flops, zero decode gates, outputs are flip-flop Q pins so they are glitch-free by construction. Binary: 3 flip-flops plus an incrementer, plus a 3-to-8 decoder (eight 3-input AND gates with inverters) if one-hot outputs are needed; the decoder output will glitch whenever more than one state bit changes, e.g. 011→100, unless it too is registered — which brings the flip-flop count back to 3 + 8 = 11, worse than the ring counter. The ring counter wins for small $N$ and loses badly for large $N$: at $N = 256$ it needs 256 flip-flops against 8.(c) 環形:8 顆正反器、零顆解碼閘,輸出就是正反器的 Q 腳,所以從構造上就無毛刺。二進位:3 顆正反器加一個遞增器,若需要 one-hot 輸出還要加一個 3 對 8 解碼器(八顆三輸入 AND 加反相器);只要超過一個狀態位元改變,解碼輸出就會有毛刺,例如 011→100,除非把它也暫存起來——而那會讓正反器數回到 3 + 8 = 11,比環形計數器還糟。環形計數器在 $N$ 小時勝出,在 $N$ 大時慘敗:$N = 256$ 時它要 256 顆正反器,對手只要 8 顆。(c) リング:フリップフロップ 8 個、デコードゲートはゼロ、出力はフリップフロップの Q ピンなので構成上グリッチフリー。二進:フリップフロップ 3 個とインクリメンタ、one-hot 出力が要るならさらに 3-to-8 デコーダ(3 入力 AND 8 個とインバータ)。状態ビットが二つ以上変わるたびにデコード出力は必ずグリッチする(例:011→100)。これもレジスタで受ければ防げるが、そうするとフリップフロップ数は 3 + 8 = 11 に戻り、リングカウンタより悪くなる。リングカウンタは $N$ が小さければ勝ち、大きければ大敗する。$N = 256$ では 256 個に対して 8 個である。
(d) Without self-correction, the state is now 0000_1001: two ones. Rotation preserves the population count, so the pair circulates forever — 0001_0010, 0010_0100, and so on — and the decoded outputs show two simultaneously active states every cycle, for ever, until a reset. With the self-correcting feedback {count[6:0], ~|count[7:1]}, bit 0's new value is the NOR of bits 7..1. Starting from 0000_1001: bits 7..1 are 0000100, which is non-zero, so the injected bit is 0 and the state becomes 0001_0010 — still two ones, but the lower one was consumed. Each lap, whenever the lower of the two ones would wrap round from bit 7 to bit 0, the NOR sees the other one still present and refuses to re-inject it. Within at most eight clocks the extra one is gone and the counter is back to a legal single-one state. Twelve clocks is comfortably enough.(d) 沒有自我修正時,狀態現在是 0000_1001:兩個 1。旋轉保持 1 的個數,所以這一對會永遠循環下去——0001_0010、0010_0100,依此類推——而解碼輸出每個週期都顯示兩個狀態同時作用,永遠如此,直到重置為止。有自我修正回授 {count[6:0], ~|count[7:1]} 時,位元 0 的新值是位元 7..1 的 NOR。從 0000_1001 開始:位元 7..1 是 0000100,非零,所以注入的位元是 0,狀態變成 0001_0010——仍然兩個 1,但較低的那個被消耗掉了。每一圈,只要兩個 1 中較低的那個要從位元 7 繞回位元 0,NOR 就會看到另一個 1 還在而拒絕重新注入它。最多八個時脈之內多出來的那個 1 就會消失,計數器回到合法的單一 1 狀態。十二個時脈綽綽有餘。(d) 自己修正がない場合、状態はいま 0000_1001、1 が二つある。回転は 1 の個数を保つので、この対は永遠に巡り続け——0001_0010、0010_0100、というように——デコード出力は毎サイクル二つの状態が同時に有効だと示し続ける。リセットまで永久にだ。自己修正帰還 {count[6:0], ~|count[7:1]} がある場合、ビット 0 の新しい値はビット 7..1 の NOR である。0000_1001 から始めると、ビット 7..1 は 0000100 で非ゼロなので注入されるビットは 0 となり、状態は 0001_0010 になる——まだ 1 は二つだが、下側の 1 は消費された。一周ごとに、二つの 1 のうち下側がビット 7 からビット 0 へ回り込もうとするたび、NOR はもう一つの 1 がまだあるのを見て再注入を拒む。たかだか 8 クロック以内に余分な 1 は消え、カウンタは合法な 1 個だけの状態に戻る。12 クロックあれば十分すぎる。
Pipelining is the one architectural transformation in this lecture. Everything else maps a description onto hardware; pipelining changes the hardware deliberately, trading a resource you have (latency, registers) for one you want (clock frequency, throughput). The slide's vehicle is a 16-bit adder, which is small enough to analyse completely.管線化是本講中唯一一項架構層次的變換。其他所有東西都是把描述對映到硬體;管線化則是刻意改變硬體,拿你手上有的資源(延遲、暫存器)去換你想要的資源(時脈頻率、吞吐量)。投影片的載具是一個 16 位元加法器,小到足以被完整分析。パイプライン化は本講で唯一のアーキテクチャ変換である。他はすべて記述をハードウェアへ写すだけだが、パイプライン化はハードウェアそのものを意図的に変え、手持ちの資源(レイテンシ、レジスタ)を欲しい資源(クロック周波数、スループット)と交換する。スライドの題材は 16 ビット加算器で、完全に解析できるほど小さい。
The starting point is two 8-bit adders chained through the carry: the low adder takes a[7:0], b[7:0] and c_in and produces sum[7:0] plus a carry; that carry feeds the high adder's c_in, which produces sum[15:8] and c_out.起點是兩個 8 位元加法器以進位串接:低位加法器吃 a[7:0]、b[7:0] 與 c_in,產生 sum[7:0] 外加一個進位;那個進位餵給高位加法器的 c_in,產生 sum[15:8] 與 c_out。出発点は、キャリーで連結した二つの 8 ビット加算器である。下位の加算器は a[7:0]、b[7:0]、c_in を取り、sum[7:0] とキャリーを出す。そのキャリーが上位加算器の c_in に入り、sum[15:8] と c_out を生む。
The critical path is the carry. In a ripple-carry structure the carry has to walk from bit 0 to bit 15 before sum[15] is final, so the delay is roughly proportional to the word length. Writing the two 8-bit blocks as separate modules does not change that; it just makes the chain visible. With the setup equation from Section 3.3,關鍵路徑是進位。在漣波進位結構裡,進位必須從位元 0 一路走到位元 15,sum[15] 才算定案,所以延遲大致與字長成正比。把兩個 8 位元區塊寫成分開的模組並不會改變這件事;那只是讓鏈變得看得見而已。套用 3.3 節的 setup 方程式,クリティカルパスはキャリーである。リプルキャリー構造では、キャリーがビット 0 からビット 15 まで歩き切らないと sum[15] は確定しないので、遅延はおおむね語長に比例する。二つの 8 ビットブロックを別々のモジュールとして書いてもそれは変わらない。連鎖が目に見えるようになるだけだ。3.3 節のセットアップ式を当てはめると、
Take plausible numbers: $t_{cq} = 150$ ps, $t_{su} = 80$ ps, and an 8-bit carry chain $t_{\text{add8}} = 2.0$ ns. Then $T_{\text{flat}} = 0.15 + 4.0 + 0.08 = 4.23$ ns, a maximum frequency of about 236 MHz. If the specification says 400 MHz, the adder is the problem and something has to change.代入合理的數字:$t_{cq} = 150$ ps、$t_{su} = 80$ ps,8 位元進位鏈 $t_{\text{add8}} = 2.0$ ns。那麼 $T_{\text{flat}} = 0.15 + 4.0 + 0.08 = 4.23$ ns,最高頻率約 236 MHz。如果規格要求 400 MHz,加法器就是問題所在,必須有所改變。もっともらしい数値を入れてみる。$t_{cq} = 150$ ps、$t_{su} = 80$ ps、8 ビットのキャリーチェーンは $t_{\text{add8}} = 2.0$ ns。すると $T_{\text{flat}} = 0.15 + 4.0 + 0.08 = 4.23$ ns、最高周波数は約 236 MHz。仕様が 400 MHz を要求するなら、問題は加算器であり、何かを変えねばならない。
There are two ways to make a long combinational path fit in a shorter period. You can make the logic faster — replace ripple carry with carry-lookahead, carry-select or a parallel-prefix adder, which turns the $O(n)$ delay into $O(\log n)$ at the cost of area. Or you can cut the path with a register and accept that the answer now takes two cycles to come out. Pipelining is the second option, and its great virtue is that it works on any long path, not just on paths with a known fast algorithm.要讓一條長的組合路徑塞進較短的週期,有兩條路。你可以把邏輯變快——用超前進位、進位選擇或平行前綴加法器取代漣波進位,把 $O(n)$ 的延遲變成 $O(\log n)$,代價是面積。或者你可以用暫存器把路徑切開,接受答案現在要兩個週期才出得來。管線化是第二條路,它最大的優點是:它對任何長路徑都有效,不只是對那些已知有快速演算法的路徑。長い組合せ経路を短い周期に収める方法は二つある。論理を速くする——リプルキャリーをキャリールックアヘッド、キャリーセレクト、あるいは並列プレフィックス加算器に置き換え、$O(n)$ の遅延を面積と引き換えに $O(\log n)$ にする。もう一つは、レジスタで経路を切り、答えが出るのに 2 サイクルかかることを受け入れる。パイプライン化は後者であり、その大きな美点はどんな長い経路にも効くこと、速いアルゴリズムが知られている経路に限らないことである。
The slide's pipeline has three register banks:投影片的管線有三排暫存器:スライドのパイプラインにはレジスタ段が三つある。
c_in and all four bytes of a and b.IR,輸入暫存器,33 位元:它捕捉 c_in 以及 a 和 b 的全部四個位元組。IR、入力レジスタ、33 ビット:c_in と、a と b の 4 バイトすべてを取り込む。{c_out, sum[15:0]}.OR,輸出暫存器,17 位元:最終的 {c_out, sum[15:0]}。OR、出力レジスタ、17 ビット:最終的な {c_out, sum[15:0]}。Between IR and PR sits one 8-bit adder doing the low byte. Between PR and OR sits a second 8-bit adder doing the high byte with the stored carry. No single combinational path now contains more than one 8-bit carry chain, soIR 與 PR 之間坐著一個做低位元組的 8 位元加法器。PR 與 OR 之間坐著第二個 8 位元加法器,拿儲存起來的進位去做高位元組。現在沒有任何單一組合路徑包含超過一條 8 位元進位鏈,所以IR と PR のあいだには下位バイトを処理する 8 ビット加算器が、PR と OR のあいだには保存されたキャリーを使って上位バイトを処理する二つ目の 8 ビット加算器が座っている。これで単一の組合せ経路に 8 ビットのキャリーチェーンが二本入ることはなくなり、
The key structural idea — and the thing students most often get wrong when asked to pipeline something — is that data which is not used in stage 1 must still be registered through stage 1. The high bytes of a and b are not needed until stage 2, but they cannot simply be taken from the module inputs at stage 2, because by then the inputs have moved on to the next operand pair. They have to travel down the pipeline alongside the data that is being worked on. That is what PR[L3:R3] <= IR[L4:R4] and PR[L2:R2] <= IR[L3:R3] are doing: pure delay, no logic. Forget them and the adder computes the low byte of one operand pair with the high byte of another.關鍵的結構性想法——也是學生被要求把東西管線化時最常弄錯的一點——是:在第 1 級用不到的資料,仍然必須被暫存著穿過第 1 級。a 與 b 的高位元組要到第 2 級才用得到,但你不能在第 2 級直接從模組輸入去拿,因為到那時候輸入已經換成下一對運算元了。它們必須跟著正在被處理的資料一起沿管線往下走。這就是 PR[L3:R3] <= IR[L4:R4] 與 PR[L2:R2] <= IR[L3:R3] 在做的事:純延遲、沒有邏輯。忘了它們,加法器就會拿某一對運算元的低位元組去配另一對的高位元組。肝心な構造上の考え——そしてパイプライン化を課されたときに学生がもっとも間違える点——は、第 1 段で使わないデータも第 1 段をレジスタで通さねばならないことである。a と b の上位バイトは第 2 段まで不要だが、第 2 段でモジュール入力からそのまま取ることはできない。そのときには入力が次のオペランド対に移っているからだ。処理中のデータと並んでパイプラインを下っていく必要がある。PR[L3:R3] <= IR[L4:R4] と PR[L2:R2] <= IR[L3:R3] がしているのはそれだ。純粋な遅延であり、論理はない。これを忘れると、加算器はあるオペランド対の下位バイトを別の対の上位バイトと組み合わせて計算する。
The slide's code uses a wall of parameter declarations to name the bit ranges. That is good practice — change size and the whole thing re-sizes — but it makes the code hard to read until you substitute the numbers. Here it is with every parameter evaluated for size = 16.投影片的程式碼用一整面牆的 parameter 宣告去命名位元範圍。那是好習慣——改 size 整個東西就跟著改大小——但在你把數字代進去之前,程式碼很難讀。這裡是把每個參數都以 size = 16 算出來的版本。スライドのコードはビット範囲に名前を付けるために parameter 宣言の壁を使っている。よい習慣だ——size を変えれば全体がリサイズされる——が、数値を代入するまではコードが読みにくい。ここでは size = 16 として全パラメータを展開した版を示す。
module add_16_pipe (c_out, sum, a, b, c_in, clock); parameter size = 16; parameter half = size / 2; // 8 parameter double = 2 * size; // 32 parameter triple = 3 * half; // 24 parameter size1 = half - 1; // 7 -- top of a half word parameter size2 = size - 1; // 15 -- top of a full word parameter size3 = half + 1; // 9 parameter R1 = 1; // 1 IR field: a[7:0] = IR[8:1] parameter L1 = half; // 8 parameter R2 = size3; // 9 IR field: b[7:0] = IR[16:9] parameter L2 = size; // 16 parameter R3 = size + 1; // 17 IR field: a[15:8] = IR[24:17] parameter L3 = size + half; // 24 parameter R4 = double - half + 1; // 25 IR field: b[15:8] = IR[32:25] parameter L4 = double; // 32 input [size2:0] a, b; // [15:0] input c_in, clock; output [size2:0] sum; // [15:0] output c_out; reg [double:0] IR; // [32:0] = 1 + 8 + 8 + 8 + 8 reg [triple:0] PR; // [24:0] = 9 + 8 + 8 reg [size:0] OR; // [16:0] = 1 + 16 assign {c_out, sum} = OR; always @ (posedge clock) begin // ---- stage 0: load the input register ------------------- IR[0] <= c_in; // IR[0] = c_in IR[L1:R1] <= a[size1:0]; // IR[8:1] = a[7:0] IR[L2:R2] <= b[size1:0]; // IR[16:9] = b[7:0] IR[L3:R3] <= a[size2:half]; // IR[24:17] = a[15:8] IR[L4:R4] <= b[size2:half]; // IR[32:25] = b[15:8] // ---- stage 1: low byte adds, high bytes just wait ------- PR[L3:R3] <= IR[L4:R4]; // PR[24:17] = b[15:8], delayed PR[L2:R2] <= IR[L3:R3]; // PR[16:9] = a[15:8], delayed PR[half:0] <= IR[L2:R2] + IR[L1:R1] + IR[0]; // PR[8:0] = b[7:0] + a[7:0] + c_in // 9 bits: PR[8] is the carry, PR[7:0] the sum // ---- stage 2: high byte adds with the stored carry ------ OR <= {{1'b0, PR[L3:R3]} + {1'b0, PR[L2:R2]} + PR[half], PR[size1:0]}; end endmodule
The bit-field map is the part worth memorising, because once you see it the code is obvious:值得背下來的是這張位元欄位對照表,因為一旦你看懂它,程式碼就一目了然:暗記する価値があるのはこのビットフィールド対応表である。これが見えればコードは自明になる。
| Register暫存器レジスタ | Field欄位フィールド | Width寬度幅 | Contents內容内容 |
|---|---|---|---|
IR[32:0] | IR[0] | 1 | c_in |
IR[8:1] | 8 | a[7:0] | |
IR[16:9] | 8 | b[7:0] | |
IR[24:17] | 8 | a[15:8] | |
IR[32:25] | 8 | b[15:8] | |
PR[24:0] | PR[7:0] | 8 | final sum[7:0]最終的 sum[7:0]最終的な sum[7:0] |
PR[8] | 1 | carry out of the low byte低位元組的進位輸出下位バイトからのキャリー出力 | |
PR[24:9] | 16 | a[15:8] and b[15:8], delayed one cycle延遲一個週期的 a[15:8] 與 b[15:8]1 サイクル遅延させた a[15:8] と b[15:8] | |
OR[16:0] | OR[15:0] | 16 | sum[15:0] |
OR[16] | 1 | c_out |
Total register cost: $33 + 25 + 17 = 75$ flip-flops, where the flat version needed only the 17 output bits. That is the price of the pipeline, and it is a big relative increase for something as small as an adder. For a wide multiplier or an FFT butterfly, where the logic dwarfs the registers, the ratio is much more favourable — which is why pipelining is ubiquitous in communication datapaths and rare in small control logic.暫存器總成本:$33 + 25 + 17 = 75$ 顆正反器,而平坦版本只需要那 17 個輸出位元。這就是管線的代價,對加法器這麼小的東西來說,相對增幅非常大。對一個寬乘法器或一個 FFT 蝶形運算來說,邏輯遠遠大過暫存器,比例就好看得多——這就是為什麼管線化在通訊資料路徑中無所不在,在小型控制邏輯中卻很罕見。レジスタの総コストは $33 + 25 + 17 = 75$ 個のフリップフロップで、フラット版は出力の 17 ビットだけで済んでいた。これがパイプラインの代価であり、加算器のように小さな回路では相対的な増加が非常に大きい。幅の広い乗算器や FFT バタフライのように論理がレジスタを圧倒する回路なら比率ははるかに有利になる——パイプライン化が通信データパスで遍在し、小さな制御論理ではまれである理由だ。
All three register banks are loaded in one always @(posedge clock) behaviour. That is legal and efficient precisely because non-blocking assignment makes the statements order-independent: PR is computed from the old IR even though IR's new value is assigned a few lines above, and OR from the old PR. Rewrite the same code with = and the entire pipeline collapses into a single stage — every bank would take the value just assigned to the previous one, and the adder would produce a result in one cycle with the original long critical path. This is Practice 1(d) at architectural scale, and it is the standard way a pipeline silently stops being a pipeline.三排暫存器全部在同一個 always @(posedge clock) 行為裡被載入。這之所以合法又有效率,正是因為非阻斷式指派讓這些敘述與順序無關:PR 是從舊的 IR 算出來的,即使 IR 的新值在上面幾行就被指派了;OR 也是從舊的 PR 算出來的。把同一段程式碼改寫成 =,整條管線就塌成單一級——每一排都會拿到前一排剛被指派的值,加法器會在一個週期內產生結果,帶著原本那條長長的關鍵路徑。這是練習 1(d) 的架構級版本,也是管線悄悄不再是管線的標準方式。三つのレジスタ段はすべて一つの always @(posedge clock) ビヘイビアでロードされる。これが合法かつ効率的なのは、まさにノンブロッキング代入が文を順序に依存しなくするからだ。IR の新しい値が数行上で代入されていても PR は古い IR から計算され、OR は古い PR から計算される。同じコードを = で書き直すとパイプライン全体が 1 段に潰れる——各段は直前の段にいま代入された値を取り、加算器は元の長いクリティカルパスのまま 1 サイクルで結果を出す。これは練習 1(d) のアーキテクチャ規模版であり、パイプラインが黙ってパイプラインでなくなる定番の経路である。
1'b0 constants — the slide is wrong6.4 那兩個 1'b0 常數 — 投影片錯了6.4 二つの 1'b0 定数 — スライドは間違っているThe slide carries a handwritten annotation pointing at the two {1'b0, ...} concatenations in the last line, reading “Both 1'b0 should be removed.” Follow that instruction and the adder stops producing a carry out. Here is why, and the rule involved is one of the most frequently examined corners of Verilog.投影片上有一條手寫註記指著最後一行裡那兩個 {1'b0, ...} 串接,寫著「Both 1'b0 should be removed.」。照著那條指示做,加法器就不再產生進位輸出了。以下是原因,而牽涉到的那條規則是 Verilog 最常被考的角落之一。スライドには最終行の二つの {1'b0, ...} 連結を指して「Both 1'b0 should be removed.」という手書きの注記がある。その指示に従うと、加算器はキャリー出力を出さなくなる。以下がその理由であり、関わる規則は Verilog でもっとも頻繁に試験に出る隅の一つである。
IEEE 1364 divides expressions into context-determined and self-determined. In an ordinary assignment, the width of the right-hand side is extended to the width of the left-hand side before evaluation, so a carry out of an addition has somewhere to go. But the operands of a concatenation are self-determined: each is evaluated at its own natural width, with no knowledge of the target. For an addition, the natural width is the width of the widest operand.IEEE 1364 把運算式分成由上下文決定與自決兩類。在一般的指派裡,右手邊的寬度會在計算之前被延展到左手邊的寬度,所以加法的進位輸出有地方可去。但串接的運算元是自決的:每一個都以自己的自然寬度被計算,完全不知道目標是什麼。對加法而言,自然寬度就是最寬那個運算元的寬度。IEEE 1364 は式を文脈決定と自己決定に分ける。通常の代入では、右辺の幅は評価の前に左辺の幅まで拡張されるので、加算のキャリー出力には行き場がある。しかし連結のオペランドは自己決定である。各オペランドは代入先を知らないまま、自分の自然な幅で評価される。加算にとって自然な幅とは、もっとも広いオペランドの幅である。
Apply that here. PR[L3:R3] is PR[24:17], eight bits. PR[L2:R2] is PR[16:9], eight bits. PR[half] is PR[8], one bit.把這套規則用在這裡。PR[L3:R3] 是 PR[24:17],八位元。PR[L2:R2] 是 PR[16:9],八位元。PR[half] 是 PR[8],一位元。これをここに当てはめる。PR[L3:R3] は PR[24:17] で 8 ビット。PR[L2:R2] は PR[16:9] で 8 ビット。PR[half] は PR[8] で 1 ビットである。
As written, with the zero extensions: {1'b0, PR[24:17]} is nine bits and so is {1'b0, PR[16:9]}. The widest operand of the addition is nine bits, so the whole sum is evaluated at nine bits. The largest possible value is $0\text{-}11111111 + 0\text{-}11111111 + 1 = 255 + 255 + 1 = 511$, which is exactly $2^9 - 1$ — it fits in nine bits with nothing lost. Concatenated with the eight bits of PR[7:0], that gives a 17-bit value, which is precisely the width of OR. Bit 16 is the carry out. Correct.照原樣寫、含零延展時:{1'b0, PR[24:17]} 是九位元,{1'b0, PR[16:9]} 也是。加法中最寬的運算元是九位元,所以整個和都以九位元計算。可能的最大值是 $0\text{-}11111111 + 0\text{-}11111111 + 1 = 255 + 255 + 1 = 511$,恰好是 $2^9 - 1$——九位元剛好裝得下,什麼都沒掉。再與 PR[7:0] 的八位元串接,就得到一個 17 位元的值,正是 OR 的寬度。位元 16 就是進位輸出。正確。そのまま、ゼロ拡張ありの場合:{1'b0, PR[24:17]} は 9 ビット、{1'b0, PR[16:9]} も 9 ビットである。加算のもっとも広いオペランドは 9 ビットなので、和全体が 9 ビットで評価される。最大値は $0\text{-}11111111 + 0\text{-}11111111 + 1 = 255 + 255 + 1 = 511$ で、ちょうど $2^9 - 1$——9 ビットに収まり、失われるものはない。これを PR[7:0] の 8 ビットと連結すれば 17 ビット値になり、まさに OR の幅である。ビット 16 がキャリー出力だ。正しい。
With the zero extensions removed: the widest operand is eight bits, so the addition is evaluated at eight bits and the carry out of bit 7 is simply discarded. The concatenation is now $8 + 8 = 16$ bits, assigned to a 17-bit register, so it is zero-extended on the left and OR[16] — that is, c_out — is permanently 0. A 16-bit adder that can never report an overflow. Wrong.把零延展拿掉時:最寬的運算元是八位元,所以加法以八位元計算,位元 7 的進位輸出直接被丟掉。串接現在是 $8 + 8 = 16$ 位元,被指派給一個 17 位元的暫存器,於是左邊補零,OR[16]——也就是 c_out——永遠是 0。一個永遠無法回報溢位的 16 位元加法器。錯誤。ゼロ拡張を取り除いた場合:もっとも広いオペランドは 8 ビットなので加算は 8 ビットで評価され、ビット 7 のキャリー出力はそのまま捨てられる。連結はいまや $8 + 8 = 16$ ビットで、17 ビットのレジスタに代入されるため左側はゼロ拡張され、OR[16]——すなわち c_out——は永久に 0 になる。オーバーフローを決して報告できない 16 ビット加算器である。誤り。
Keep the two 1'b0 extensions. The annotation on page 22 of the slides is a mistake. It is worth understanding why the mistake is tempting: outside a concatenation, OR[16:8] <= PR[24:17] + PR[16:9] + PR[8]; really does not need them, because the 9-bit left-hand side makes the addition context-determined at nine bits. The zero extensions look redundant only if you forget that the expression is inside { }.請保留那兩個 1'b0 延展。投影片第 22 頁上的那條註記是錯的。值得理解的是這個錯誤為什麼很誘人:在串接之外,OR[16:8] <= PR[24:17] + PR[16:9] + PR[8]; 確實不需要它們,因為 9 位元的左手邊會讓加法以九位元由上下文決定。只有當你忘記這個運算式是在 { } 裡面的時候,零延展才會看起來多餘。二つの 1'b0 拡張は残すこと。スライド 22 ページの注記は誤りである。その誤りがなぜ魅力的に見えるかは理解しておく価値がある。連結の外では OR[16:8] <= PR[24:17] + PR[16:9] + PR[8]; は確かにそれらを必要としない。左辺が 9 ビットなので加算は文脈により 9 ビットで決まるからだ。ゼロ拡張が冗長に見えるのは、この式が { } の中にあることを忘れたときだけである。
IEEE 1364 settles the width of every expression in two passes. First it determines the size of the expression from the context; then it extends the operands to that size and evaluates.IEEE 1364 分兩趟決定每個運算式的寬度。第一趟由上下文決定運算式的大小;第二趟把運算元延展到那個大小再求值。IEEE 1364 は二つのパスで各式の幅を決める。第一パスで文脈から式の大きさを決め、第二パスでオペランドをその大きさに拡張して評価する。
+ - * / % & | ^ ~^ and of the conditional operator's two result arms take the width of the widest operand or of the assignment target, whichever is larger. This is why reg [8:0] s; s = a8 + b8; keeps the carry but reg [7:0] s; s = a8 + b8; does not.由上下文決定。指派的右手邊取左手邊的寬度。+ - * / % & | ^ ~^ 的運算元,以及條件運算子兩個結果分支的運算元,取「最寬運算元」或「指派目標」中較大者的寬度。這就是為什麼 reg [8:0] s; s = a8 + b8; 保得住進位,而 reg [7:0] s; s = a8 + b8; 保不住。文脈決定。代入の右辺は左辺の幅をとる。+ - * / % & | ^ ~^ のオペランドと、条件演算子の二つの結果項のオペランドは、もっとも広いオペランドまたは代入先のうち大きいほうの幅をとる。reg [8:0] s; s = a8 + b8; がキャリーを保ち、reg [7:0] s; s = a8 + b8; が保たないのはこのためだ。<< and >>; the operands of the comparison operators == != < > <= >=; the operand of a reduction operator; and the condition of ?:. These are evaluated at their own natural size and the surrounding context does not widen them.自決。串接或複製的運算元;<< 與 >> 的移位量;比較運算子 == != < > <= >= 的運算元;歸約運算子的運算元;以及 ?: 的條件。這些都以自己的自然大小求值,周圍的上下文不會把它們加寬。自己決定。連結や繰り返しのオペランド、<< と >> のシフト量、比較演算子 == != < > <= >= のオペランド、リダクション演算子のオペランド、そして ?: の条件。これらは自分の自然な大きさで評価され、周囲の文脈が幅を広げることはない。{a8 + b8} loses the carry. {1'b0, a8} + {1'b0, b8} inside a concatenation keeps it, because the operands are already nine bits. a8 << 1 is still eight bits, so the top bit falls off unless the target widens it — and a shift is not context-extended in the way an addition is, which is a separate trap. A comparison such as (a4 == b8) zero-extends the shorter operand to eight bits, which is usually what you want but is worth knowing.值得記住的後果。{a8 + b8} 會丟掉進位。串接裡的 {1'b0, a8} + {1'b0, b8} 保得住,因為運算元本身就已經是九位元了。a8 << 1 仍然是八位元,所以除非目標把它加寬,否則最高位會掉出去——而移位不會像加法那樣被上下文延展,這是另一個獨立的陷阱。像 (a4 == b8) 這樣的比較會把較短的運算元補零到八位元,通常那正是你要的,但值得知道。覚えておく価値のある帰結。{a8 + b8} はキャリーを失う。連結内の {1'b0, a8} + {1'b0, b8} は、オペランドがすでに 9 ビットなので保つ。a8 << 1 は依然 8 ビットなので、代入先が広げない限り最上位ビットは落ちる——しかもシフトは加算のようには文脈拡張されない。これは別個の落とし穴である。(a4 == b8) のような比較は短いほうのオペランドを 8 ビットにゼロ拡張する。たいていは望みどおりだが、知っておく価値はある。The practical rule: whenever an arithmetic result must be wider than its operands, make the extra width explicit, either by widening the operands or by assigning to a wide enough target in its own statement. Never rely on the reader spotting which bucket an expression falls into.實務規則:只要算術結果必須比它的運算元寬,就把多出來的寬度寫明,要嘛把運算元加寬,要嘛用一個獨立的敘述指派給一個夠寬的目標。千萬不要指望讀者能看出某個運算式落在哪一類。実務上の規則:算術結果がオペランドより広くなければならないときは、余分な幅をつねに明示すること。オペランドを広げるか、十分広い対象へ独立した文で代入するかだ。式がどちらの分類に入るかを読者が見抜いてくれることに頼ってはならない。
The cleanest repair, if you dislike relying on a subtle width rule, is to split the assignment so that the context does the work:如果你不喜歡依賴一條微妙的寬度規則,最乾淨的修法是把指派拆開,讓上下文去做這件事:微妙な幅の規則に頼るのが嫌なら、もっともきれいな対処は代入を分割して文脈に仕事をさせることである。
OR <= {{1'b0, PR[L3:R3]}
+ {1'b0, PR[L2:R2]}
+ PR[half], PR[size1:0]};OR[size:half] <= PR[L3:R3]
+ PR[L2:R2]
+ PR[half]; // 9-bit target
OR[size1:0] <= PR[size1:0]; // 8-bit targetBoth synthesise to the same hardware. The right-hand version cannot be misread, which is worth more than the one saved line.兩者合成出來的硬體相同。右邊那個版本不可能被誤讀,而那比省下來的那一行更有價值。どちらも同じハードウェアに合成される。右の版は誤読のしようがなく、その価値は節約できる 1 行よりも大きい。
Two numbers describe a pipeline and they move in opposite directions.有兩個數字描述一條管線,而它們往相反方向移動。パイプラインを記述する数は二つあり、両者は逆方向に動く。
For the slide's adder, $N_{\text{stages}} = 3$ (IR, PR, OR) and $T_{\text{pipe}} = 2.23$ ns, so latency is 6.69 ns while the flat adder's latency was 4.23 ns. Read that again: pipelining made each individual answer arrive later in absolute time. What it bought is that a new answer arrives every 2.23 ns instead of every 4.23 ns, so the sustained rate nearly doubled. For a communication datapath processing a continuous sample stream, that is exactly the trade you want. For a latency-critical control loop, it is exactly the trade you do not want.對投影片的加法器而言,$N_{\text{stages}} = 3$(IR、PR、OR)而 $T_{\text{pipe}} = 2.23$ ns,所以延遲是 6.69 ns,而平坦加法器的延遲只有 4.23 ns。再讀一次:管線化讓每一個個別的答案在絕對時間上更晚抵達。它買到的是:新答案每 2.23 ns 就來一個,而不是每 4.23 ns,所以持續速率幾乎翻倍。對一條處理連續取樣串流的通訊資料路徑來說,那正是你要的交換。對一個延遲吃緊的控制迴路來說,那正是你不要的交換。スライドの加算器では $N_{\text{stages}} = 3$(IR、PR、OR)、$T_{\text{pipe}} = 2.23$ ns なのでレイテンシは 6.69 ns、一方フラット加算器のレイテンシは 4.23 ns だった。もう一度読んでほしい。パイプライン化は個々の答えの到着を絶対時間では遅くした。得たのは、新しい答えが 4.23 ns ごとではなく 2.23 ns ごとに届くこと、つまり持続レートがほぼ倍になったことだ。連続サンプル列を処理する通信データパスにはまさに望ましい取引であり、レイテンシが致命的な制御ループにはまさに望ましくない取引である。
The speed-up from splitting one stage into $k$ equal stages is把一級切成 $k$ 個相等的級,加速比是1 段を $k$ 個の等しい段に分けたときの高速化率は
The register overhead $t_{cq} + t_{su}$ never goes away, so the speed-up saturates. With the numbers above, $t_{cq} + t_{su} = 230$ ps and $t_{\text{logic}} = 4.0$ ns, so the ceiling is $4.23/0.23 \approx 18.4\times$ — but you would need infinitely many stages to get there. Two stages give $4.23/2.23 = 1.90\times$; four stages give $4.23/1.23 = 3.44\times$; eight give $4.23/0.73 = 5.79\times$. Each doubling of stages buys less than the previous one, while the register area and the latency grow linearly. Real designs stop when the stage delay is somewhere around three to five times the register overhead, because past that point you are paying a full register bank for a few per cent of frequency.暫存器的開銷 $t_{cq} + t_{su}$ 永遠不會消失,所以加速比會飽和。用上面的數字,$t_{cq} + t_{su} = 230$ ps 而 $t_{\text{logic}} = 4.0$ ns,所以天花板是 $4.23/0.23 \approx 18.4\times$——但你需要無限多級才到得了。兩級給 $4.23/2.23 = 1.90\times$;四級給 $4.23/1.23 = 3.44\times$;八級給 $4.23/0.73 = 5.79\times$。級數每加倍一次,買到的都比前一次少,而暫存器面積與延遲卻線性成長。真實設計會在級延遲大約是暫存器開銷的三到五倍時停手,因為過了那一點,你是在用一整排暫存器換百分之幾的頻率。レジスタのオーバーヘッド $t_{cq} + t_{su}$ は決して消えないので、高速化率は飽和する。上の数値では $t_{cq} + t_{su} = 230$ ps、$t_{\text{logic}} = 4.0$ ns なので上限は $4.23/0.23 \approx 18.4\times$——だがそこへ達するには無限段が要る。2 段で $4.23/2.23 = 1.90\times$、4 段で $4.23/1.23 = 3.44\times$、8 段で $4.23/0.73 = 5.79\times$。段数を倍にするたび得られるものは前回より小さくなり、レジスタ面積とレイテンシは線形に増える。実際の設計は、段遅延がレジスタ・オーバーヘッドの 3〜5 倍あたりになったところで手を止める。それを越えると、数パーセントの周波数のためにレジスタ 1 段ぶんを丸ごと払うことになるからだ。
Two further realities temper the formula. First, you can rarely split a cloud into exactly equal parts; the clock period is set by the longest stage, so an unbalanced split wastes the slack in the short stages. Second, pipelining a feedback loop is not possible without changing the algorithm — an accumulator cannot simply be cut in half, because the next result depends on the current one. Techniques like carry-save accumulation or loop unrolling exist for exactly that reason, and they belong to Lecture 8's design examples.還有兩個現實因素會調和這個公式。第一,你很少能把一團邏輯雲切成完全相等的幾份;時脈週期由最長的那一級決定,所以不均勻的切法會浪費掉短級裡的餘裕。第二,不改演算法就無法把回授迴路管線化——累加器不能直接對半切,因為下一個結果取決於當前這一個。進位保留累加、迴圈展開之類的技巧正是為此而存在,而它們屬於第 8 講的設計範例。この式を和らげる現実がさらに二つある。第一に、論理の雲をきっかり等分できることはまれだ。クロック周期は最長の段で決まるので、不均等な分割は短い段のスラックを捨てることになる。第二に、アルゴリズムを変えずにフィードバックループをパイプライン化することはできない。アキュムレータは単純に半分に切れない。次の結果が今の結果に依存するからだ。キャリーセーブ加算やループ展開といった技法はまさにそのために存在し、それらは第 8 講の設計例に属する。
Problem: At rising edge 1 the inputs are $(a, b, c_{in}) = (\texttt{16'h00FF}, \texttt{16'h0001}, 0)$; at edge 2 they are $(\texttt{16'hFFFF}, \texttt{16'h0001}, 0)$; from edge 3 onwards the inputs are all zero. (a) Give the contents of IR after edge 1 and after edge 2. (b) Give PR after edge 2. (c) Give OR after edge 3 and after edge 4. (d) At which edge does the first result become visible on sum, and what is the pipeline's latency in cycles? (e) If the designer forgot the two “delay only” assignments PR[L3:R3] <= IR[L4:R4] and PR[L2:R2] <= IR[L3:R3] and instead wrote the high bytes straight from a and b, what would OR contain after edge 3?題目:在上升緣 1 時輸入是 $(a, b, c_{in}) = (\texttt{16'h00FF}, \texttt{16'h0001}, 0)$;在邊緣 2 時是 $(\texttt{16'hFFFF}, \texttt{16'h0001}, 0)$;從邊緣 3 起輸入全為零。(a) 給出邊緣 1 之後與邊緣 2 之後 IR 的內容。(b) 給出邊緣 2 之後的 PR。(c) 給出邊緣 3 之後與邊緣 4 之後的 OR。(d) 第一個結果在哪個邊緣才看得到於 sum 上?管線的延遲是幾個週期?(e) 如果設計者忘了那兩個「純延遲」的指派 PR[L3:R3] <= IR[L4:R4] 與 PR[L2:R2] <= IR[L3:R3],改成直接從 a 與 b 取高位元組,邊緣 3 之後 OR 會是什麼?問題:立ち上がりエッジ 1 で入力は $(a, b, c_{in}) = (\texttt{16'h00FF}, \texttt{16'h0001}, 0)$、エッジ 2 で $(\texttt{16'hFFFF}, \texttt{16'h0001}, 0)$、エッジ 3 以降は入力がすべてゼロである。(a) エッジ 1 の後とエッジ 2 の後の IR の内容を示せ。(b) エッジ 2 の後の PR を示せ。(c) エッジ 3 の後とエッジ 4 の後の OR を示せ。(d) 最初の結果が sum 上で見えるのはどのエッジか。またパイプラインのレイテンシは何サイクルか。(e) 設計者が「遅延のみ」の二つの代入 PR[L3:R3] <= IR[L4:R4] と PR[L2:R2] <= IR[L3:R3] を忘れ、上位バイトを a と b から直接取っていたら、エッジ 3 の後の OR は何になるか。
(a) After edge 1: IR[0] = 0, IR[8:1] = 8'hFF (a low), IR[16:9] = 8'h01 (b low), IR[24:17] = 8'h00 (a high), IR[32:25] = 8'h00 (b high). After edge 2: IR[0] = 0, IR[8:1] = 8'hFF (a low of the second operand), IR[16:9] = 8'h01, IR[24:17] = 8'hFF, IR[32:25] = 8'h00.(a) 邊緣 1 之後:IR[0] = 0、IR[8:1] = 8'hFF(a 的低位元組)、IR[16:9] = 8'h01(b 的低位元組)、IR[24:17] = 8'h00(a 的高位元組)、IR[32:25] = 8'h00(b 的高位元組)。邊緣 2 之後:IR[0] = 0、IR[8:1] = 8'hFF(第二對運算元 a 的低位元組)、IR[16:9] = 8'h01、IR[24:17] = 8'hFF、IR[32:25] = 8'h00。(a) エッジ 1 の後:IR[0] = 0、IR[8:1] = 8'hFF(a の下位)、IR[16:9] = 8'h01(b の下位)、IR[24:17] = 8'h00(a の上位)、IR[32:25] = 8'h00(b の上位)。エッジ 2 の後:IR[0] = 0、IR[8:1] = 8'hFF(二組目のオペランドの a 下位)、IR[16:9] = 8'h01、IR[24:17] = 8'hFF、IR[32:25] = 8'h00。
(b) PR after edge 2 is computed from IR as it stood before edge 2, i.e. from the first operand pair. Low add: $\texttt{8'h01} + \texttt{8'hFF} + 0 = \texttt{9'h100}$, so PR[8] = 1 and PR[7:0] = 8'h00. The delayed high bytes are PR[24:17] = 8'h00 and PR[16:9] = 8'h00.(b) 邊緣 2 之後的 PR,是用邊緣 2 之前的 IR 算出來的,也就是用第一對運算元。低位加法:$\texttt{8'h01} + \texttt{8'hFF} + 0 = \texttt{9'h100}$,所以 PR[8] = 1、PR[7:0] = 8'h00。被延遲的高位元組是 PR[24:17] = 8'h00 與 PR[16:9] = 8'h00。(b) エッジ 2 の後の PR は、エッジ 2 の直前の IR、すなわち一組目のオペランドから計算される。下位の加算は $\texttt{8'h01} + \texttt{8'hFF} + 0 = \texttt{9'h100}$ なので PR[8] = 1、PR[7:0] = 8'h00。遅延された上位バイトは PR[24:17] = 8'h00 と PR[16:9] = 8'h00 である。
(c) OR after edge 3 uses PR as it stood before edge 3: upper $= \texttt{8'h00} + \texttt{8'h00} + 1 = \texttt{9'h001}$, concatenated with PR[7:0] = 8'h00, giving OR = 17'h0_0100. So c_out = 0, sum = 16'h0100 — and indeed $\texttt{0x00FF} + \texttt{0x0001} = \texttt{0x0100}$. ✔(c) 邊緣 3 之後的 OR 用的是邊緣 3 之前的 PR:上半 $= \texttt{8'h00} + \texttt{8'h00} + 1 = \texttt{9'h001}$,與 PR[7:0] = 8'h00 串接,得到 OR = 17'h0_0100。所以 c_out = 0、sum = 16'h0100——而確實 $\texttt{0x00FF} + \texttt{0x0001} = \texttt{0x0100}$。✔(c) エッジ 3 の後の OR はエッジ 3 直前の PR を使う。上位は $= \texttt{8'h00} + \texttt{8'h00} + 1 = \texttt{9'h001}$ で、PR[7:0] = 8'h00 と連結して OR = 17'h0_0100。すなわち c_out = 0、sum = 16'h0100——確かに $\texttt{0x00FF} + \texttt{0x0001} = \texttt{0x0100}$ である。✔
OR after edge 4 corresponds to the second operand pair: low add $\texttt{0x01} + \texttt{0xFF} = \texttt{9'h100}$ so carry 1, sum low 8'h00; upper $= \texttt{8'h00} + \texttt{8'hFF} + 1 = \texttt{9'h100}$. Hence OR = {9'h100, 8'h00} = 17'h1_0000, i.e. c_out = 1, sum = 16'h0000. And $\texttt{0xFFFF} + \texttt{0x0001} = \texttt{0x10000}$. ✔邊緣 4 之後的 OR 對應第二對運算元:低位加法 $\texttt{0x01} + \texttt{0xFF} = \texttt{9'h100}$,所以進位 1、低位和 8'h00;上半 $= \texttt{8'h00} + \texttt{8'hFF} + 1 = \texttt{9'h100}$。因此 OR = {9'h100, 8'h00} = 17'h1_0000,也就是 c_out = 1、sum = 16'h0000。而 $\texttt{0xFFFF} + \texttt{0x0001} = \texttt{0x10000}$。✔エッジ 4 の後の OR は二組目のオペランドに対応する。下位の加算は $\texttt{0x01} + \texttt{0xFF} = \texttt{9'h100}$ でキャリー 1、下位の和は 8'h00。上位は $= \texttt{8'h00} + \texttt{8'hFF} + 1 = \texttt{9'h100}$。したがって OR = {9'h100, 8'h00} = 17'h1_0000、すなわち c_out = 1、sum = 16'h0000。そして $\texttt{0xFFFF} + \texttt{0x0001} = \texttt{0x10000}$ である。✔
(d) The first result is in OR after edge 3, so it is readable on sum during cycle 3–4. The latency is three clock cycles from presenting the operands to having the result registered.(d) 第一個結果在邊緣 3 之後進入 OR,所以在週期 3–4 之間可以在 sum 上讀到。從送入運算元到結果被暫存起來,延遲是三個時脈週期。(d) 最初の結果はエッジ 3 の後に OR に入るので、サイクル 3–4 のあいだ sum 上で読める。オペランドを与えてから結果がレジスタに収まるまでのレイテンシはクロック 3 サイクルである。
(e) Stage 2 would then add the high bytes of whatever is on the module inputs now to the carry from the operand pair presented two cycles ago. Before edge 3 the inputs are the second pair (a high = 8'hFF), while PR[8] carries the first pair's carry. OR would become $\{\texttt{8'h00} + \texttt{8'hFF} + 1, \texttt{8'h00}\} = \texttt{17'h1\_0000}$ — the right-hand half of the first operand pair glued to the left-hand half of the second. The bug is silent for constant inputs and produces nonsense for varying ones, which is why it is so dangerous. Pipelines fail by misalignment, not by arithmetic.(e) 第 2 級會把此刻模組輸入上的高位元組,加上兩個週期前那對運算元所產生的進位。邊緣 3 之前,輸入是第二對(a 的高位元組 = 8'hFF),而 PR[8] 帶的是第一對的進位。OR 會變成 $\{\texttt{8'h00} + \texttt{8'hFF} + 1, \texttt{8'h00}\} = \texttt{17'h1\_0000}$——第一對運算元的右半被黏到第二對的左半上。這個錯誤在輸入固定時悄無聲息,在輸入變動時產生垃圾,這正是它危險的原因。管線是因為對不齊而失敗,不是因為算術。(e) 第 2 段はいまモジュール入力に出ている上位バイトを、2 サイクル前のオペランド対から来たキャリーに加算してしまう。エッジ 3 の直前、入力は二組目(a の上位 = 8'hFF)だが、PR[8] が運んでいるのは一組目のキャリーである。OR は $\{\texttt{8'h00} + \texttt{8'hFF} + 1, \texttt{8'h00}\} = \texttt{17'h1\_0000}$ になる——一組目の右半分に二組目の左半分が貼り付いた値だ。このバグは入力が一定なら静かで、変動すると無意味な値を出す。だからこそ危険である。パイプラインは算術ではなく位置ずれで壊れる。
Problem: A 64-bit ripple-carry adder has a combinational delay of 16 ns. The flip-flops have $t_{cq} = 150$ ps and $t_{su} = 80$ ps; assume the carry chain can be cut at any bit boundary and that the resulting stages are exactly equal. (a) Write $T(k)$ and $f(k)$ for a $k$-stage pipeline. (b) Compute $f$ for $k = 1, 2, 4, 8, 16, 64$. (c) At which $k$ does an extra stage buy less than 10 % more frequency? (d) How many extra flip-flops does $k = 8$ cost, roughly, and what is the latency in nanoseconds compared with $k = 1$? (e) Why is a carry-select adder often a better answer than a deep pipeline here?題目:一個 64 位元漣波進位加法器的組合延遲是 16 ns。正反器的 $t_{cq} = 150$ ps、$t_{su} = 80$ ps;假設進位鏈可以在任何位元邊界被切開,且切出來的各級完全相等。(a) 寫出 $k$ 級管線的 $T(k)$ 與 $f(k)$。(b) 計算 $k = 1, 2, 4, 8, 16, 64$ 時的 $f$。(c) 在哪個 $k$ 之後,多加一級買到的頻率提升不到 10 %?(d) $k = 8$ 大約多花幾顆正反器?與 $k = 1$ 相比延遲是幾奈秒?(e) 為什麼在這裡進位選擇加法器往往是比深管線更好的答案?問題:64 ビットのリプルキャリー加算器の組合せ遅延は 16 ns である。フリップフロップは $t_{cq} = 150$ ps、$t_{su} = 80$ ps。キャリーチェーンは任意のビット境界で切れ、できた段はきっかり等しいと仮定する。(a) $k$ 段パイプラインの $T(k)$ と $f(k)$ を書け。(b) $k = 1, 2, 4, 8, 16, 64$ について $f$ を計算せよ。(c) 段を一つ増やしても周波数が 10 % 未満しか上がらなくなるのはどの $k$ か。(d) $k = 8$ はおよそ何個のフリップフロップを余分に要し、$k = 1$ と比べてレイテンシは何ナノ秒か。(e) ここでキャリーセレクト加算器が深いパイプラインより良い答えであることが多いのはなぜか。
(a) $T(k) = t_{cq} + t_{\text{logic}}/k + t_{su} = 0.23 + 16/k$ ns, and $f(k) = 1/T(k)$.(a) $T(k) = t_{cq} + t_{\text{logic}}/k + t_{su} = 0.23 + 16/k$ ns,而 $f(k) = 1/T(k)$。(a) $T(k) = t_{cq} + t_{\text{logic}}/k + t_{su} = 0.23 + 16/k$ ns、$f(k) = 1/T(k)$。
(b)
| $k$ | 1 | 2 | 4 | 8 | 16 | 64 |
|---|---|---|---|---|---|---|
| $T(k)$ / ns | 16.23 | 8.23 | 4.23 | 2.23 | 1.23 | 0.48 |
| $f(k)$ / MHz | 61.6 | 121.5 | 236 | 448 | 813 | 2083 |
| Speed-up加速比高速化率 | 1.00 | 1.97 | 3.84 | 7.28 | 13.2 | 33.8 |
(c) Going from $k$ to $k+1$ improves frequency by $T(k)/T(k+1)$. At $k = 16 \to 17$: $1.23 / (0.23 + 16/17) = 1.23/1.171 = 1.050$, only 5 %. At $k = 8 \to 9$: $2.23/(0.23+1.778) = 2.23/2.008 = 1.111$, 11 %. So the 10 % threshold is crossed between $k = 9$ and $k = 10$. Beyond about ten stages you are buying single-digit percentages for a full register bank each time.(c) 從 $k$ 走到 $k+1$,頻率改善的倍率是 $T(k)/T(k+1)$。在 $k = 16 \to 17$:$1.23 / (0.23 + 16/17) = 1.23/1.171 = 1.050$,只有 5 %。在 $k = 8 \to 9$:$2.23/(0.23+1.778) = 2.23/2.008 = 1.111$,11 %。所以 10 % 的門檻落在 $k = 9$ 與 $k = 10$ 之間。超過大約十級之後,你每次都是用一整排暫存器去買個位數的百分比。(c) $k$ から $k+1$ への周波数改善率は $T(k)/T(k+1)$ である。$k = 16 \to 17$ では $1.23 / (0.23 + 16/17) = 1.23/1.171 = 1.050$ でわずか 5 %。$k = 8 \to 9$ では $2.23/(0.23+1.778) = 2.23/2.008 = 1.111$ で 11 %。したがって 10 % の境目は $k = 9$ と $k = 10$ のあいだにある。おおよそ 10 段を越えると、毎回レジスタ 1 段ぶんを払って一桁パーセントを買っていることになる。
(d) Each cut needs a register holding everything that still has to travel: the finished sum bits plus the un-processed operand bits plus the carry. Summed over the pipeline this is roughly $k$ times the data width, so eight stages on a 64-bit adder costs on the order of 8 × 130 ≈ 1000 flip-flops against the 65 of the flat version. Latency rises from 16.23 ns (one cycle) to $8 \times 2.23 = 17.8$ ns (eight cycles) — marginally worse in time, dramatically better in rate.(d) 每切一刀都需要一個暫存器來裝所有還得繼續走的東西:已經算完的和位元,加上還沒處理的運算元位元,加上進位。沿著整條管線加總起來,大約是 $k$ 乘上資料寬度,所以 64 位元加法器切成八級大約要 8 × 130 ≈ 1000 顆正反器,而平坦版本只要 65 顆。延遲從 16.23 ns(一個週期)升到 $8 \times 2.23 = 17.8$ ns(八個週期)——在時間上稍差,在速率上大幅變好。(d) 切るたびに、まだ先へ運ぶ必要のあるものすべて——完成した和のビット、未処理のオペランドのビット、キャリー——を保持するレジスタが要る。パイプライン全体で合計するとおよそ $k$ かけるデータ幅になるので、64 ビット加算器を 8 段にすると 8 × 130 ≈ 1000 個程度のフリップフロップが要る。フラット版の 65 個に対してである。レイテンシは 16.23 ns(1 サイクル)から $8 \times 2.23 = 17.8$ ns(8 サイクル)へ——時間ではわずかに悪化し、レートでは劇的に改善する。
(e) Because the 16 ns is an artefact of a bad adder topology, not a law of nature. A carry-select or parallel-prefix 64-bit adder has delay $O(\log n)$ — perhaps 2–3 ns instead of 16 — for maybe twice the combinational area and no extra registers and no extra latency. The rule is: fix the algorithm first, pipeline second. Pipelining is for paths that are already as fast as you know how to make them.(e) 因為那 16 ns 是糟糕的加法器拓撲造成的人為產物,不是自然律。一個進位選擇或平行前綴的 64 位元加法器延遲是 $O(\log n)$——大概 2–3 ns 而不是 16——代價大約是兩倍的組合面積,而且不需要額外的暫存器、不需要額外的延遲。規則是:先修演算法,再管線化。管線化是給那些已經快到你不知道還能怎麼更快的路徑用的。(e) その 16 ns は自然法則ではなく、まずい加算器トポロジが生んだ人工物だからである。キャリーセレクトや並列プレフィックスの 64 ビット加算器は遅延が $O(\log n)$——16 ではなく 2–3 ns 程度——で、組合せ面積はおよそ 2 倍、しかし追加のレジスタも追加のレイテンシも不要である。原則は「まずアルゴリズムを直し、次にパイプライン化せよ」。パイプライン化は、すでに自分の知る限り最速になっている経路のためのものだ。
A finite state machine is the general form of everything in this lecture. A shift register is an FSM whose state graph is a line; a ring counter is an FSM whose graph is a cycle; a pipeline is an FSM whose state is the data in flight. What makes the general case worth a section of its own is that the designer, rather than the structure, chooses the state graph — and that two different but equally valid choices of where to put the output produce circuits with measurably different behaviour.有限狀態機是本講中所有東西的一般形式。移位暫存器是狀態圖為一條線的 FSM;環形計數器是狀態圖為一個環的 FSM;管線是以「在途資料」為狀態的 FSM。一般情形之所以值得獨立成節,是因為狀態圖是由設計者而不是由結構決定的——而「把輸出放在哪裡」這兩種不同但同樣有效的選擇,會產生行為上可量測地不同的電路。有限状態機械は、本講のすべてを包含する一般形である。シフトレジスタは状態グラフが一直線の FSM、リングカウンタは状態グラフが巡回の FSM、パイプラインは飛行中のデータを状態とする FSM である。一般の場合が独立した節に値するのは、状態グラフを構造ではなく設計者が選ぶからであり、そして「出力をどこに置くか」という異なるが等しく妥当な二つの選択が、測定可能なほど振る舞いの異なる回路を生むからである。
The slide gives one requirement, a list of options and three recommendations. Taken in order:投影片給了一條要求、一份選項清單和三條建議。依序來看:スライドは要求を一つ、選択肢の一覧、そして推奨を三つ挙げている。順に見ていこう。
Make sure the description is complete. Complete means: every state in the enumeration appears in the case; every state assigns next_state for every value of every input; and there is a default. The hardware reason is the one from Lecture 5. The next-state block is combinational, so any path through it that does not assign next_state means “hold”, which means a latch. A latch in the next-state path of an FSM is not merely wasteful — it creates a transparent window during which the next-state value can follow the inputs, so the state register can capture something that was never a legal next state.確保描述是完整的。完整的意思是:列舉中的每一個狀態都出現在 case 裡;每一個狀態對每一個輸入的每一種值都指派 next_state;而且有 default。硬體上的理由就是第 5 講那一條。次態區塊是組合的,所以任何一條沒有指派 next_state 的路徑都意味著「保持」,也就意味著閂鎖。FSM 次態路徑上的閂鎖不只是浪費——它製造出一個透通窗口,在那段期間次態值可以跟著輸入跑,於是狀態暫存器可能捕捉到一個從來不是合法次態的值。記述が完全であることを確かめる。完全とは、列挙したすべての状態が case に現れ、各状態がすべての入力のすべての値に対して next_state を代入し、かつ default があることをいう。ハードウェア上の理由は第 5 講と同じだ。次状態ブロックは組合せなので、そこを通るどの経路でも next_state を代入しなければ「保持」を意味し、それはラッチを意味する。FSM の次状態経路にあるラッチは単に無駄なだけではない。次状態の値が入力に追従できる透過窓を作り、その結果、状態レジスタが合法な次状態ではなかった値を取り込みうる。
Possible modelling options. The slide lists continuous assignments, a behaviour whose event control is an event-or of the state and the inputs, functions, and an external module. These are four ways of expressing the same combinational function, and the choice is one of taste except for one thing: whatever you choose, its sensitivity must be complete. A continuous assignment and a function call inside an always @* are automatically complete. A hand-written sensitivity list, always @(state_reg or in_bit), is complete only as long as nobody adds an input and forgets to update it — which is why always @* (Verilog-2001) exists and why you should use it.可能的建模選項。投影片列出了連續指派、事件控制為「狀態與輸入的 event-or」的行為、函式,以及外部模組。這四種是表達同一個組合函數的四種方式,選哪一種是品味問題,只有一件事例外:不論你選哪一種,它的敏感性都必須完整。連續指派與放在 always @* 裡的函式呼叫自動就是完整的。手寫的感應列 always @(state_reg or in_bit) 只有在沒人新增輸入卻忘了更新它的前提下才完整——這就是 always @*(Verilog-2001)存在的原因,也是你應該用它的原因。とりうるモデリングの選択肢。スライドは連続代入、状態と入力の event-or をイベント制御とするビヘイビア、関数、外部モジュールを挙げる。これらは同一の組合せ関数を表す四つの書き方であり、どれを選ぶかは好みの問題だ。ただし一点だけ例外がある。何を選ぶにせよ、その感度は完全でなければならない。連続代入と always @* 内の関数呼び出しは自動的に完全である。手書きの感度リスト always @(state_reg or in_bit) が完全なのは、誰も入力を追加して更新を忘れないあいだだけだ——always @*(Verilog-2001)が存在する理由であり、それを使うべき理由でもある。
If you write always @(state_reg) and forget in_bit, the simulation will not re-evaluate the next-state logic when in_bit changes, so the machine appears to ignore its input between state changes. The synthesiser, however, builds the combinational function the body describes and wires in_bit into it, because hardware has no sensitivity lists. The result is an RTL simulation that passes and a gate-level simulation that fails, or vice versa — the hardest class of bug to find, because the two models you are comparing both look correct in isolation. Most tools now emit a warning; take it seriously.如果你寫 always @(state_reg) 而忘了 in_bit,模擬就不會在 in_bit 改變時重新計算次態邏輯,於是機器看起來像是在狀態轉換之間忽略了它的輸入。然而合成器會按照本體所描述的組合函數去蓋電路,並把 in_bit 接進去,因為硬體沒有感應列這種東西。結果就是 RTL 模擬通過而閘級模擬失敗,或者反過來——這是最難找的一類錯誤,因為你正在比較的兩個模型各自單獨看起來都正確。現在多數工具會發出警告;請認真看待。always @(state_reg) と書いて in_bit を忘れると、シミュレーションは in_bit が変化しても次状態論理を再評価しないので、機械は状態遷移のあいだ入力を無視しているように見える。ところが合成器は本体が記述する組合せ関数を構築し、そこに in_bit を配線する。ハードウェアに感度リストは存在しないからだ。結果として RTL シミュレーションは通りゲートレベルが落ちる、あるいはその逆が起きる——比較している二つのモデルがどちらも単独では正しく見えるため、もっとも見つけにくい類のバグである。いまはほとんどのツールが警告を出す。真に受けること。
Use separate behaviours for the state register and the next-state logic. This is the two-block template. The reason is that the two pieces are different kinds of hardware with different templates: one is edge-sensitive and becomes flip-flops, the other is level-sensitive and becomes gates. Writing them together forces the tool to disentangle them and forces you to reason about blocking versus non-blocking in the same breath as about state transitions. Separating them makes each block match exactly one template.狀態暫存器與次態邏輯請用分開的行為。這就是兩區塊樣板。理由是這兩塊是種類不同的硬體,對應不同的樣板:一個是邊緣敏感、會變成正反器,另一個是位準敏感、會變成閘。把它們寫在一起,會逼工具去把它們拆開,也逼你在思考狀態轉移的同時還要思考阻斷與非阻斷。分開寫,每個區塊就剛好對應一個樣板。状態レジスタと次状態論理は別々のビヘイビアに分ける。これが二ブロック・テンプレートである。理由は、この二つが異なる種類のハードウェアであり、対応するテンプレートも違うからだ。片方はエッジ感応でフリップフロップになり、もう片方はレベル感応でゲートになる。一緒に書けばツールに両者をほどく手間をかけさせ、あなたには状態遷移を考えるのと同じ息でブロッキングか否かを考えさせることになる。分ければ各ブロックがちょうど一つのテンプレートに一致する。
Use non-blocking assignments for the state register, because they are order-independent (Section 1.3), and never mix blocking and non-blocking assignments to the same variable in the same behaviour. That last rule is not a style preference: the IEEE standard leaves the result unspecified, different simulators resolve it differently, and synthesis tools generally refuse.狀態暫存器請用非阻斷式指派,因為它們與順序無關(1.3 節),而且絕不要在同一個行為裡對同一個變數混用阻斷式與非阻斷式指派。最後那條不是風格偏好:IEEE 標準把結果定為未規定,不同模擬器的解法不同,而合成工具通常會直接拒絕。状態レジスタにはノンブロッキング代入を使う。順序に依存しないからだ(1.3 節)。そして同一ビヘイビア内で同じ変数にブロッキングとノンブロッキングを混ぜてはならない。最後の規則は文体の好みではない。IEEE 標準は結果を未規定とし、シミュレータごとに解決が異なり、合成ツールはたいてい拒否する。
The specification on the slide:投影片上的規格:スライドにある仕様:
That last point deserves a moment. Changing stimulus on the opposite edge from the one the design uses is the standard way to write a testbench for a synchronous design, and Lecture 2 makes the same point. It gives the design half a clock period of setup margin and half a period of hold margin in simulation, so you never have to reason about race conditions between the testbench and the device under test. It also means that in any waveform you look at, the value of in_bit during a clock period is exactly the value that will be sampled at the end of that period.最後這一點值得停一下。在與設計所使用的邊緣相反的那個邊緣上改變激勵,是為同步設計撰寫測試平台的標準做法,第 2 講也講了同一件事。它在模擬中給了設計半個時脈週期的 setup 餘裕和半個週期的 hold 餘裕,所以你永遠不必去推敲測試平台與待測裝置之間的競爭條件。它也意味著:在你看的任何波形裡,某個時脈週期內 in_bit 的值,正好就是那個週期結束時會被取樣到的值。最後の点は少し立ち止まる価値がある。設計が使うのとは反対のエッジで刺激を変えるのは、同期設計のテストベンチを書く標準的なやり方で、第 2 講も同じことを述べている。シミュレーション上、設計にクロック半周期ぶんのセットアップ余裕とホールド余裕を与えるので、テストベンチと被試験回路のあいだの競合条件を考えなくて済む。また、どの波形を見ても、あるクロック周期中の in_bit の値が、その周期の終わりにサンプルされる値そのものだということも意味する。
Now the two machine types:接著是兩種機器類型:続いて二つの機械の型:
| Type類型型 | Output is a function of輸出是什麼的函數出力は何の関数か | Where the output label goes輸出標籤寫在哪裡出力ラベルを書く場所 | Output changes when輸出何時改變出力が変化するとき |
|---|---|---|---|
| Mealy | current state and current input當前狀態與當前輸入現在の状態と現在の入力 | on each arc: input / output寫在每條弧上:input / output各アーク上に:input / output | the state changes, or the input changes狀態改變時,或輸入改變時状態が変わったとき、または入力が変わったとき |
| Moore | current state only只有當前狀態現在の状態のみ | inside each node: state / output寫在每個節點裡:state / output各ノードの中に:state / output | only when the state changes只有在狀態改變時状態が変わったときだけ |
Before any Verilog, derive the states. The machine has to remember enough of the past to decide whether the next bit completes a pair. “Enough of the past” is: what was the last bit, and have I already seen a run of two? That gives five states:在寫任何 Verilog 之前,先導出狀態。這台機器必須記住足夠多的過去,才能判斷下一個位元是否湊成一對。「足夠多的過去」就是:上一個位元是什麼,以及我是不是已經看到一段長度二的連續。這給出五個狀態:Verilog を書く前に、まず状態を導く。この機械は、次のビットが対を完成させるかどうかを判断できるだけの過去を覚えていなければならない。「十分な過去」とは、直前のビットが何だったか、そしてすでに長さ 2 の連続を見たかどうかである。これで五つの状態が得られる。
| State狀態状態 | Binary code二進位編碼二進符号 | Meaning意義意味 | Moore outputMoore 輸出ムーア出力 |
|---|---|---|---|
start_state | 3'b000 | Just reset; no bit has been read yet剛重置完;還沒讀到任何位元リセット直後。まだ 1 ビットも読んでいない | 0 |
read_1_zero | 3'b001 | The last bit was a 0, and it was the first of its run上一個位元是 0,而且是它那段連續的第一個直前のビットは 0 で、その連続の最初のものだった | 0 |
read_1_one | 3'b010 | The last bit was a 1, and it was the first of its run上一個位元是 1,而且是它那段連續的第一個直前のビットは 1 で、その連続の最初のものだった | 0 |
read_2_zero | 3'b011 | The last two or more bits were 0最後兩個或更多位元都是 0直近 2 ビット以上が 0 だった | 1 |
read_2_one | 3'b100 | The last two or more bits were 1最後兩個或更多位元都是 1直近 2 ビット以上が 1 だった | 1 |
A common first attempt uses only four states, dropping start_state and resetting into read_1_zero. That is wrong in a detectable way: after reset the machine would behave as though it had already seen a 0, so a single 0 as the very first bit would be reported as a pair. The fifth state exists precisely to encode “I have no history yet”.常見的第一次嘗試只用四個狀態,去掉 start_state 而把重置落在 read_1_zero。那是以一種可偵測的方式出錯:重置後機器的行為會像是它已經看過一個 0,於是最前面那一個單獨的 0 就會被報成一對。第五個狀態存在的目的,正是為了編碼「我還沒有任何歷史」。よくある最初の試みは状態を四つだけにし、start_state を省いて read_1_zero にリセットするというものだ。これは検出可能な形で誤っている。リセット後、機械はすでに 0 を一つ見たかのように振る舞うので、先頭の単独の 0 が対として報告されてしまう。五つ目の状態は、まさに「まだ履歴がない」を符号化するために存在する。
Note also that read_2_zero means “two or more”, not “exactly two”. Its self-loop on input 0 keeps asserting the output, so a run of five zeros produces four consecutive assertions. That is the specification the slides implement; if you wanted “exactly two, then stop”, the self-loop would go to a sixth state with output 0.另外注意 read_2_zero 的意思是「兩個或更多」,不是「剛好兩個」。它在輸入 0 上的自迴圈會持續確立輸出,所以連續五個零會產生四次連續的確立。這就是投影片所實作的規格;如果你要的是「剛好兩個然後停」,那個自迴圈就該指向第六個輸出為 0 的狀態。また read_2_zero が意味するのは「2 個以上」であって「ちょうど 2 個」ではない点に注意。入力 0 での自己ループが出力をアサートし続けるので、0 が 5 個続けば 4 回連続でアサートされる。これがスライドの実装する仕様である。「ちょうど 2 個で止める」が欲しければ、その自己ループは出力 0 の六つ目の状態へ向かうべきだ。
The complete transition table. The next-state column is identical for Mealy and Moore — the machines differ only in the output column.完整的轉移表。次態那一欄對 Mealy 與 Moore 是相同的——兩台機器只在輸出欄上有差別。完全な遷移表。次状態の列はミーリとムーアで同一である——二つの機械が異なるのは出力の列だけだ。
| Current state當前狀態現在の状態 | in_bit | Next state次態次状態 | Mealy output (this period)Mealy 輸出(本週期)ミーリ出力(この周期) | Moore output (in the next state)Moore 輸出(在次態時)ムーア出力(次状態において) |
|---|---|---|---|---|
start_state | 0 | read_1_zero | 0 | 0 |
start_state | 1 | read_1_one | 0 | 0 |
read_1_zero | 0 | read_2_zero | 1 | 1 |
read_1_zero | 1 | read_1_one | 0 | 0 |
read_1_one | 0 | read_1_zero | 0 | 0 |
read_1_one | 1 | read_2_one | 1 | 1 |
read_2_zero | 0 | read_2_zero | 1 | 1 |
read_2_zero | 1 | read_1_one | 0 | 0 |
read_2_one | 0 | read_1_zero | 0 | 0 |
read_2_one | 1 | read_2_one | 1 | 1 |
Read the last two columns carefully. They contain the same pattern of ones, but they refer to different instants: the Mealy column says “during the clock period in which this row's input is presented”, while the Moore column says “during the clock period after that input has been absorbed”. That one-period shift is the entire difference between the two machines, and Section 7.6 draws it.請仔細讀最後兩欄。它們含有相同的 1 的圖樣,但指的是不同的時刻:Mealy 那一欄說的是「在這一列的輸入被送進來的那個時脈週期內」,而 Moore 那一欄說的是「在那個輸入被吸收之後的那個時脈週期內」。那一個週期的位移就是兩台機器全部的差別,而 7.6 節會把它畫出來。最後の二列は注意深く読んでほしい。1 の並びは同じだが、指している瞬間が違う。ミーリの列は「この行の入力が与えられているクロック周期のあいだ」を、ムーアの列は「その入力が取り込まれた後のクロック周期のあいだ」を言っている。この 1 周期のずれが二つの機械の差のすべてであり、7.6 節でそれを図にする。
module seq_det_mealy_1exp (clock, reset, in_bit, out_bit); input clock, reset, in_bit; output out_bit; reg [2:0] state_reg, next_state; parameter start_state = 3'b000; parameter read_1_zero = 3'b001; parameter read_1_one = 3'b010; parameter read_2_zero = 3'b011; parameter read_2_one = 3'b100; // ---- state register: clocked, non-blocking, async reset ---- always @ (posedge clock or posedge reset) if (reset == 1) state_reg <= start_state; else state_reg <= next_state; // ---- next-state logic: combinational, blocking ---- always @ (state_reg or in_bit) case (state_reg) start_state: if (in_bit == 0) next_state = read_1_zero; else if (in_bit == 1) next_state = read_1_one; else next_state = start_state; read_1_zero: if (in_bit == 0) next_state = read_2_zero; else if (in_bit == 1) next_state = read_1_one; else next_state = start_state; read_2_zero: if (in_bit == 0) next_state = read_2_zero; else if (in_bit == 1) next_state = read_1_one; else next_state = start_state; read_1_one: if (in_bit == 0) next_state = read_1_zero; else if (in_bit == 1) next_state = read_2_one; else next_state = start_state; read_2_one: if (in_bit == 0) next_state = read_1_zero; else if (in_bit == 1) next_state = read_2_one; else next_state = start_state; default: next_state = start_state; endcase // ---- output logic: Mealy, depends on state AND input ---- assign out_bit = ((((state_reg == read_1_zero) || (state_reg == read_2_zero)) && (in_bit == 0)) || (((state_reg == read_1_one) || (state_reg == read_2_one)) && (in_bit == 1))) ? 1 : 0; endmodule
Four things in this code are worth naming explicitly, because they are the habits the slide's recommendations are trying to instil.這段程式碼裡有四件事值得明確點名,因為它們正是投影片那些建議想要養成的習慣。このコードには明示的に名指しする価値のある点が四つある。スライドの推奨が身につけさせようとしている習慣そのものだからだ。
The triple if. Each state tests in_bit == 0, then in_bit == 1, then falls through to a final else. For a one-bit signal that looks redundant — surely it is either 0 or 1? In simulation it need not be: in_bit can be x or z, and in_bit == 0 and in_bit == 1 are both false in that case, because == returns x for an unknown operand and if treats x as false. So the final else catches the unknown and parks the machine in start_state. In synthesis the branch is unreachable and costs nothing. This is a deliberate, cheap, defensive idiom and it is the reason the code looks more verbose than it needs to be.三重 if。每個狀態都先測 in_bit == 0,再測 in_bit == 1,然後落到最後的 else。對一個一位元訊號來說這看起來多餘——它不是 0 就是 1 吧?在模擬裡未必:in_bit 可以是 x 或 z,而那種情況下 in_bit == 0 與 in_bit == 1 兩者都為假,因為 == 對未知運算元回傳 x,而 if 把 x 當成假。所以最後的 else 接住了未知值,把機器停在 start_state。在合成時那個分支不可達,不花任何成本。這是一個刻意的、便宜的防禦性慣用寫法,也是這段程式碼看起來比必要更囉嗦的原因。三重の if。各状態はまず in_bit == 0 を、次に in_bit == 1 を判定し、最後の else に落ちる。1 ビット信号には冗長に見える——0 か 1 のどちらかに決まっているのでは? シミュレーションではそうとは限らない。in_bit は x や z になりうるし、その場合 in_bit == 0 と in_bit == 1 はどちらも偽である。== は不定オペランドに対して x を返し、if は x を偽として扱うからだ。したがって最後の else が不定値を受け止め、機械を start_state に留める。合成時この分岐は到達不能でコストはゼロ。これは意図的で安価な防御的定型であり、このコードが必要以上に冗長に見える理由である。
Both blocks target different variables. state_reg is written only in the clocked block; next_state only in the combinational one. Neither variable has two drivers, and no variable is written with both = and <=. This is exactly the slide's third recommendation.兩個區塊各自指派不同的變數。state_reg 只在時脈區塊裡被寫;next_state 只在組合區塊裡被寫。沒有任何變數有兩個驅動源,也沒有任何變數同時用 = 與 <= 被寫。這正是投影片的第三條建議。二つのブロックは別々の変数を対象にしている。state_reg はクロック同期ブロックでのみ、next_state は組合せブロックでのみ書かれる。ドライバが二つある変数はなく、= と <= の両方で書かれる変数もない。これはスライドの三つ目の推奨そのものである。
The default. The state variable is three bits, so there are eight codes and only five are used. Without the default, the three unused codes would leave next_state unassigned, and the tool would build a latch. With it, the machine is guaranteed to return to start_state from any illegal code within one clock — which, as Section 2.5 noted, is exactly the insurance you want against reset-release skew.那個 default。狀態變數是三位元,所以有八種碼而只用了五種。沒有 default 的話,那三種沒用到的碼會讓 next_state 沒被指派,工具就會蓋出閂鎖。有了它,機器保證能在一個時脈內從任何非法碼回到 start_state——而如 2.5 節所述,那正是你想要用來防範重置釋放歪斜的保險。その default。状態変数は 3 ビットなので符号は八つあり、使うのは五つだけだ。default がなければ、未使用の三つの符号で next_state が代入されず、ツールはラッチを作る。あれば、機械はどんな不正符号からも 1 クロック以内に start_state へ戻ることが保証される——2.5 節で述べたとおり、リセット解除のスキューに対して欲しい保険そのものだ。
out_bit is a wire. It is declared only as output, not as reg, because it is driven by a continuous assignment. Mixing that up — declaring it reg and then using assign — is a compile error.out_bit 是一條連線。它只被宣告成 output,沒有宣告成 reg,因為它是由連續指派驅動的。把這點搞混——宣告成 reg 之後又用 assign——是編譯錯誤。out_bit は配線である。連続代入で駆動されるので、output としてのみ宣言され、reg としては宣言されない。これを取り違えて reg と宣言したうえで assign を使うのはコンパイルエラーである。
Apply the slide's stimulus, in_bit = 0011_1000_1 with the leftmost bit first, and reset released before the first edge. Remember the convention: the input shown for period $k$ is the one present throughout period $k$ and sampled at the edge that ends it.套用投影片的激勵 in_bit = 0011_1000_1,最左邊的位元先進,並在第一個邊緣之前解除重置。記住那個慣例:顯示在週期 $k$ 的輸入,就是整個週期 $k$ 都存在、並在結束該週期的那個邊緣被取樣的值。スライドの刺激 in_bit = 0011_1000_1 を、左端のビットから順に与え、最初のエッジの前にリセットを解除する。慣例を思い出すこと。周期 $k$ に示される入力は、周期 $k$ のあいだずっと存在し、その周期を終わらせるエッジでサンプルされる値である。
| Clock period時脈週期クロック周期 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|
in_bit | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 1 |
state_reg during該週期內的 state_regその周期中の state_reg | 0 | 1 | 3 | 2 | 4 | 4 | 1 | 3 | 3 |
next_state during該週期內的 next_stateその周期中の next_state | 1 | 3 | 2 | 4 | 4 | 1 | 3 | 3 | 2 |
out_bit (Mealy) | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 | 0 |
out_bit (Moore) | 0 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 1 |
Check the assertions against the stream by hand. The pairs of adjacent equal bits are at positions (1,2), (3,4), (4,5), (6,7) and (7,8) — five pairs. The Mealy row has ones in periods 2, 4, 5, 7, 8: the period in which the second member of each pair is present at the input. The Moore row has ones in periods 3, 5, 6, 8, 9: exactly one period later in every case. Five detections, five assertions, both machines correct, both machines different.用手把這些確立對照著串流檢查一遍。相鄰相等位元的配對出現在位置 (1,2)、(3,4)、(4,5)、(6,7) 與 (7,8)——五對。Mealy 那一列在週期 2、4、5、7、8 為 1:也就是每一對的第二個成員出現在輸入端的那個週期。Moore 那一列在週期 3、5、6、8、9 為 1:每一次都恰好晚一個週期。五次偵測、五次確立,兩台機器都正確,兩台機器都不一樣。アサートを入力列と突き合わせて手で確認してみよう。隣り合う等しいビットの対は位置 (1,2)、(3,4)、(4,5)、(6,7)、(7,8) の五組である。ミーリの行は周期 2、4、5、7、8 で 1 になる。各対の二番目の要素が入力に現れている周期だ。ムーアの行は周期 3、5、6、8、9 で 1 になる。どの場合もちょうど 1 周期遅い。検出 5 回、アサート 5 回、どちらの機械も正しく、どちらの機械も違う。
Take periods 4, 5 and 6 of the trace above and follow every signal through the hardware. Let the rising edges be at $t = 40, 50, 60, 70$ ns with a 10 ns period, so the testbench moves in_bit at 45, 55 and 65 ns.取上面軌跡的週期 4、5、6,把每一個訊號在硬體裡走一遍。設上升緣在 $t = 40, 50, 60, 70$ ns、週期 10 ns,所以測試平台在 45、55 與 65 ns 搬動 in_bit。上のトレースの周期 4、5、6 を取り、各信号をハードウェア上で追ってみる。立ち上がりエッジを $t = 40, 50, 60, 70$ ns、周期を 10 ns とすると、テストベンチは 45、55、65 ns に in_bit を動かす。
state_reg was read_2_zero (3) and next_state was read_1_one (2), computed from state 3 and the input 1 that had been present since 35 ns. So state_reg becomes 2. A few hundred picoseconds later the combinational block re-evaluates with state 2 and the same input 1, giving next_state = read_2_one (4). The Mealy output becomes (state == read_1_one) && (in_bit == 1) = 1. The Moore output becomes 0, because state 2 is not a detect state.t = 40 ns,上升緣。state_reg 原本是 read_2_zero(3),而 next_state 是 read_1_one(2),由狀態 3 和自 35 ns 起就存在的輸入 1 算出。所以 state_reg 變成 2。幾百皮秒之後,組合區塊以狀態 2 和同樣的輸入 1 重新計算,得到 next_state = read_2_one(4)。Mealy 輸出變成 (state == read_1_one) && (in_bit == 1) = 1。Moore 輸出變成 0,因為狀態 2 不是偵測狀態。t = 40 ns、立ち上がりエッジ。state_reg は read_2_zero(3)で、next_state は状態 3 と 35 ns から存在する入力 1 から計算された read_1_one(2)だった。したがって state_reg は 2 になる。数百ピコ秒後、組合せブロックが状態 2 と同じ入力 1 で再評価し、next_state = read_2_one(4)を得る。ミーリ出力は (state == read_1_one) && (in_bit == 1) = 1 になる。ムーア出力は 0 になる。状態 2 は検出状態ではないからだ。next_state stays 4, and the Mealy output stays 1 — this time because the new input also matches. Had the new bit been 0, the Mealy output would have fallen here, in the middle of a clock period. That mid-period change is the Mealy glitch exposure of Section 7.6(b), made visible.t = 45 ns,下降緣。測試平台送出下一個位元,也是 1。狀態完全沒變,但兩個組合邏輯錐都重新計算。next_state 維持 4,Mealy 輸出也維持 1——這一次是因為新的輸入也匹配。如果新位元是 0,Mealy 輸出就會在這裡、在時脈週期的中間落下。那種週期中途的變化就是 7.6(b) 所說的 Mealy 毛刺暴露,在這裡具體現形。t = 45 ns、立ち下がりエッジ。テストベンチが次のビットを与える。これも 1 である。状態は何も変わらないが、二つの組合せコーンは再評価される。next_state は 4 のまま、ミーリ出力も 1 のまま——今回は新しい入力も一致するからだ。新しいビットが 0 だったら、ミーリ出力はここで、クロック周期の途中で落ちていた。この周期中途の変化こそ 7.6(b) でいうミーリのグリッチ露出であり、ここで目に見える形になっている。state_reg takes next_state = 4 (read_2_one). The Moore output now rises, one full period after the Mealy output did, and it will stay high for exactly one period. The Mealy output stays high because the input is still 1 and state 4 is a one-detect state.t = 50 ns,上升緣。state_reg 取得 next_state = 4(read_2_one)。Moore 輸出現在升起,比 Mealy 輸出整整晚一個週期,而且它會剛好維持高一個週期。Mealy 輸出維持高,因為輸入仍是 1 而狀態 4 是「偵測到一對 1」的狀態。t = 50 ns、立ち上がりエッジ。state_reg が next_state = 4(read_2_one)を取り込む。ムーア出力がここで立ち上がる。ミーリ出力より丸 1 周期遅く、そしてちょうど 1 周期だけ高を保つ。ミーリ出力は、入力がまだ 1 で状態 4 が 1 の対を検出した状態なので、高のままである。next_state becomes read_1_zero (1).t = 55 ns,下降緣。測試平台送出 0。Mealy 輸出立刻落下:狀態 4 配上輸入 0 不成立。Moore 輸出不動——它動不了,因為沒有任何東西被時脈推動。next_state 變成 read_1_zero(1)。t = 55 ns、立ち下がりエッジ。テストベンチが 0 を与える。ミーリ出力は即座に落ちる。状態 4 と入力 0 では一致しないからだ。ムーア出力は動かない——動きようがない。クロックされたものが何もないからだ。next_state は read_1_zero(1)になる。state_reg becomes 1 and the Moore output falls. Net result over these three periods: one Mealy pulse running from 40 to 55 ns (one and a half periods, because the run of ones lasted three bits) and one Moore pulse running from 50 to 60 ns plus a second from 60 onwards — that is, the Moore output was high for periods 5 and 6 as the table says.t = 60 ns,上升緣。state_reg 變成 1,Moore 輸出落下。這三個週期的淨結果是:一道從 40 到 55 ns 的 Mealy 脈衝(一個半週期,因為那串 1 持續了三個位元),以及一道從 50 到 60 ns 的 Moore 脈衝再加上一道從 60 起的——也就是說 Moore 輸出在週期 5 與 6 為高,正如表上所寫。t = 60 ns、立ち上がりエッジ。state_reg が 1 になり、ムーア出力が落ちる。この 3 周期の正味の結果は、40 から 55 ns まで続くミーリパルス 1 本(1 の連続が 3 ビット続いたので 1.5 周期ぶん)と、50 から 60 ns のムーアパルス 1 本に 60 以降のもう 1 本——すなわち表にあるとおり、ムーア出力は周期 5 と 6 で高だったことになる。The lesson to carry away: the Moore output only ever moves at a rising edge, and its pulses are always an integer number of clock periods long. The Mealy output moves at rising and falling edges, and its pulse widths depend on the data.要帶走的教訓是:Moore 輸出永遠只在上升緣動,而且它的脈衝寬度永遠是時脈週期的整數倍。Mealy 輸出在上升緣和下降緣都會動,而且它的脈衝寬度取決於資料。持ち帰るべき教訓はこうだ。ムーア出力は立ち上がりエッジでしか動かず、そのパルス幅は必ずクロック周期の整数倍になる。ミーリ出力は立ち上がりにも立ち下がりにも動き、パルス幅はデータ次第である。
The simulation screenshot on the slides shows state_reg as 0, 1, 3, 2, 4, 1, 3, 2 — only eight values for nine periods. That is not an inconsistency: a waveform viewer merges adjacent identical values into one wide segment, and in this trace the state sits at 4 for two periods (5 and 6) and at 3 for two periods (8 and 9). Expanding the merged segments gives the nine-column table above. Being able to do that expansion mentally is a useful waveform-reading skill.投影片上的模擬截圖把 state_reg 顯示成 0, 1, 3, 2, 4, 1, 3, 2——九個週期卻只有八個值。那不是不一致:波形檢視器會把相鄰的相同值併成一段較寬的區塊,而在這段軌跡裡狀態在 4 待了兩個週期(5 和 6),在 3 也待了兩個週期(8 和 9)。把合併的區段展開就得到上面那張九欄的表。能在腦中做這個展開,是一項有用的讀波形技能。スライドのシミュレーション画面では state_reg が 0, 1, 3, 2, 4, 1, 3, 2 と、9 周期に対して 8 個の値しか示されていない。これは矛盾ではない。波形ビューアは隣接する同一値を一つの幅広い区間にまとめるからで、このトレースでは状態が 4 に 2 周期(5 と 6)、3 に 2 周期(8 と 9)留まっている。まとめられた区間を展開すれば上の 9 列の表になる。この展開を頭の中でできることは、波形を読むうえで有用な技能である。
The canonical Moore structure, which the slide draws as three boxes, is worth internalising because it is the picture every synchronous controller fits into:投影片畫成三個方塊的那個典型 Moore 結構,值得內化,因為它是每一個同步控制器都能套進去的圖:スライドが三つの箱として描く標準的なムーア構造は、内面化する価値がある。あらゆる同期コントローラが収まる図だからだ。
In Verilog, the Moore machine is byte-for-byte the Mealy machine with one line changed.在 Verilog 裡,Moore 機就是把 Mealy 機改掉一行,其餘一個位元組都不差。Verilog では、ムーア機械はミーリ機械の 1 行を変えただけで、あとは 1 バイトも違わない。
module seq_det_moore_1exp (clock, reset, in_bit, out_bit); ... // declarations identical to seq_det_mealy_1exp always @ (posedge clock or posedge reset) // identical if (reset == 1) state_reg <= start_state; else state_reg <= next_state; always @ (state_reg or in_bit) // identical case (state_reg) ... // exactly the same ten transitions endcase // ---- the only difference: no in_bit in the output ---- assign out_bit = ((state_reg == read_2_zero) || (state_reg == read_2_one)) ? 1 : 0; endmodule
Everything that makes the two machines different fits in that one expression. The Mealy output names in_bit; the Moore output does not. In gates, the Mealy output is a six-term function of four variables (three state bits and the input); the Moore output is a two-term function of three state bits — and with the right encoding, as Section 8 shows, it can be a single wire.讓兩台機器不同的一切,都塞在那一個運算式裡。Mealy 的輸出提到 in_bit;Moore 的輸出沒有。在閘的層次上,Mealy 輸出是四個變數(三個狀態位元加輸入)的六項函數;Moore 輸出是三個狀態位元的兩項函數——而用對編碼的話,如第 8 節所示,它可以只是一條線。二つの機械を違えるものはすべて、その一つの式に収まっている。ミーリ出力は in_bit を名指しし、ムーア出力はしない。ゲートのレベルでは、ミーリ出力は 4 変数(状態 3 ビットと入力)の 6 項関数、ムーア出力は状態 3 ビットの 2 項関数である——そして第 8 節で示すように、符号化を選べばただの配線 1 本にもなりうる。
It follows that the Moore version's state sequence is identical to the Mealy version's, cycle for cycle. The machines agree about everything except when they say so. That is what the slide's comparison waveform shows: state_reg is drawn twice with the same values, while out_bit_mealy and out_bit_moore are offset.由此可知 Moore 版本的狀態序列與 Mealy 版本完全相同,一個週期都不差。兩台機器對每件事都同意,除了「什麼時候說出來」。這就是投影片那張比較波形所顯示的:state_reg 被畫了兩次、值相同,而 out_bit_mealy 與 out_bit_moore 彼此錯開。したがってムーア版の状態系列はミーリ版と完全に同一で、1 サイクルの違いもない。二つの機械はいつそれを告げるかを除けば、すべてについて一致している。スライドの比較波形が示しているのはそれだ。state_reg は同じ値で二度描かれ、out_bit_mealy と out_bit_moore だけがずれている。
Now the engineering consequences, which are what the slide means by calling Moore “better”.接著是工程上的後果,也就是投影片說 Moore「比較好」時所指的東西。続いて工学上の帰結、すなわちスライドがムーアを「より良い」と呼ぶときに意味していることを見る。
A Moore output is a decode of flip-flop outputs. Its launch point is a clock edge, so static timing analysis can constrain it like any other path: launch at the state register, propagate through G, capture wherever it goes. A Mealy output has a path that starts at in_bit — which may be a chip pin — and ends at the output, with no register anywhere in between. At a module boundary that path is an input-to-output combinational arc, and constraining it requires knowing the external arrival time and the external required time. At a chip boundary it is an impossible-to-constrain pad-to-pad path. This is the single most practical reason to prefer Moore.Moore 輸出是正反器輸出的解碼。它的發射點是一個時脈邊緣,所以靜態時序分析可以像約束任何其他路徑那樣約束它:從狀態暫存器發射,穿過 G,在它去的地方被捕捉。Mealy 輸出有一條從 in_bit 開始的路徑——而 in_bit 可能是一支晶片接腳——一路到輸出,中間沒有任何暫存器。在模組邊界上,那是一條 input 到 output 的組合弧,要約束它就得知道外部的到達時間與外部的要求時間。在晶片邊界上,那是一條不可能約束的 pad 到 pad 路徑。這是偏好 Moore 最實際的一個理由。ムーア出力はフリップフロップ出力のデコードである。発射点がクロックエッジなので、スタティックタイミング解析は他の経路と同様に制約できる。状態レジスタから発射し、G を通り、行き先で捕捉される。ミーリ出力には in_bit——これはチップピンかもしれない——から始まり、途中にレジスタを一つも挟まずに出力へ至る経路がある。モジュール境界ではそれは input から output への組合せアークであり、制約するには外部の到着時刻と要求時刻を知らねばならない。チップ境界では制約不能なパッド間経路になる。これがムーアを選ぶもっとも実務的な理由である。
A Moore output changes only in response to a state change, which happens once per clock edge; between edges it is static. A Mealy output changes whenever in_bit changes, and in_bit can change at any time — including in the middle of a period, and including transiently if it is itself the output of some other combinational logic. A glitch on in_bit becomes a glitch on out_bit.Moore 輸出只在狀態改變時才改變,而狀態改變每個時脈邊緣發生一次;在邊緣之間它是靜止的。Mealy 輸出只要 in_bit 改變就會改變,而 in_bit 可以在任何時候改變——包括週期中途,也包括在它本身是某段組合邏輯輸出時那種短暫的改變。in_bit 上的毛刺會變成 out_bit 上的毛刺。ムーア出力は状態変化に応じてのみ変化し、状態変化はクロックエッジごとに 1 回起こる。エッジとエッジのあいだでは静止している。ミーリ出力は in_bit が変わるたびに変化し、in_bit はいつでも変わりうる——周期の途中でも、またそれ自体が別の組合せ論理の出力であれば一過性にも。in_bit 上のグリッチは out_bit 上のグリッチになる。
Whether that matters depends on the consumer. If out_bit feeds a flip-flop clocked by the same clock, the glitch is sampled only at the edge, by which time it has settled, and nothing is wrong. If out_bit drives an asynchronous input — a clear pin, a RAM write strobe, an off-chip enable — the glitch is a real pulse and can do real damage. The symptom is a signal that shows a narrow spike on a scope but nothing in simulation, because RTL simulation with zero delays does not produce the glitch at all.這要不要緊,取決於消費者是誰。如果 out_bit 餵給一顆由同一時脈驅動的正反器,毛刺只在邊緣被取樣,那時它早已安定,沒有任何問題。如果 out_bit 驅動一個非同步輸入——清除腳、RAM 寫入 strobe、晶片外的致能——那道毛刺就是一個真實的脈衝,可以造成真實的傷害。症狀是:示波器上看得到一根窄窄的尖波,模擬裡卻什麼都沒有,因為零延遲的 RTL 模擬根本不會產生那個毛刺。それが問題になるかどうかは受け手次第である。out_bit が同じクロックのフリップフロップに入るなら、グリッチはエッジでしかサンプルされず、その頃には収まっているので何も問題はない。out_bit が非同期入力——クリアピン、RAM のライトストローブ、チップ外のイネーブル——を駆動するなら、そのグリッチは実在のパルスであり、実害を与えうる。症状は、オシロでは細いスパイクが見えるのにシミュレーションには何も出ないことだ。遅延ゼロの RTL シミュレーションはそもそもグリッチを生まないからである。
The Mealy machine responds one cycle earlier. In a protocol where a response must be asserted in the same cycle as the request, that cycle is not negotiable. Mealy machines also often need fewer states than the equivalent Moore machine, because information that a Moore machine must encode in a state can be read directly from the input. For this particular detector the state counts happen to be equal; for others — a machine that outputs a function of a wide input word, say — a Moore implementation would need a state per output value.Mealy 機早一個週期回應。在某些協定裡,回應必須與請求在同一個週期內被確立,那一個週期是沒得商量的。Mealy 機往往也比等價的 Moore 機需要更少的狀態,因為 Moore 機必須編進狀態裡的資訊,Mealy 機可以直接從輸入讀。對這個特定的偵測器來說,兩者的狀態數恰好相等;對別的機器——比方說一台輸出是寬輸入字之函數的機器——Moore 實作會需要每個輸出值一個狀態。ミーリ機械は 1 サイクル早く応答する。要求と同じサイクルで応答をアサートしなければならないプロトコルでは、その 1 サイクルに交渉の余地はない。ミーリ機械は等価なムーア機械より状態数が少なくて済むことも多い。ムーア機械が状態に符号化せねばならない情報を、ミーリ機械は入力から直接読めるからだ。この検出器ではたまたま状態数が等しいが、別の機械——たとえば幅の広い入力語の関数を出力する機械——ではムーア実装は出力値ごとに状態を要する。
You can have both. Compute the Mealy output function, then register it:你可以兩者兼得。先算出 Mealy 輸出函數,再把它暫存:両取りはできる。ミーリ出力関数を計算し、それをレジスタで受ければよい。
always @ (posedge clock or posedge reset) if (reset) out_bit <= 1'b0; else out_bit <= mealy_function(next_state_decision, in_bit);
The output is now a flip-flop output: glitch-free, cleanly constrained, launched by a clock edge. But because the function is computed from the current input and registered immediately, the result appears one cycle after the input — the same timing as a Moore output. For this detector the registered-Mealy machine and the Moore machine produce identical waveforms, which is not a coincidence: a registered Mealy machine is a Moore machine whose state has been augmented with the output bit. The general lesson is that “Mealy versus Moore” is really a question about where you put the register, and you get to choose.輸出現在是一個正反器輸出:無毛刺、乾淨地被約束、由時脈邊緣發射。但因為那個函數是從當前輸入算出來並立刻被暫存的,結果會在輸入之後一個週期才出現——時序和 Moore 輸出一樣。對這個偵測器而言,暫存化 Mealy 機與 Moore 機產生完全相同的波形,這不是巧合:一台暫存化的 Mealy 機就是一台把輸出位元併進狀態裡的 Moore 機。一般性的教訓是:「Mealy 對 Moore」其實是在問你把暫存器放在哪裡,而那是你可以選的。出力はこれでフリップフロップ出力になる。グリッチがなく、きれいに制約でき、クロックエッジで発射される。ただし関数は現在の入力から計算されて直ちにレジスタに入るので、結果は入力の 1 サイクル後に現れる——ムーア出力と同じタイミングだ。この検出器ではレジスタ付きミーリ機械とムーア機械は同一の波形を出すが、これは偶然ではない。レジスタ付きミーリ機械とは、状態に出力ビットを足したムーア機械なのである。一般的な教訓は、「ミーリ対ムーア」とは結局レジスタをどこに置くかという問いであり、それは自分で選べるということだ。
| Mealy | Moore | Registered Mealy暫存化 Mealyレジスタ付きミーリ | |
|---|---|---|---|
| Output function of輸出是什麼的函數出力は何の関数か | state, input狀態、輸入状態、入力 | state狀態状態 | state, input (registered)狀態、輸入(經暫存)状態、入力(レジスタ経由) |
| Response latency回應延遲応答レイテンシ | same cycle同一週期同じサイクル | next cycle下一週期次のサイクル | next cycle下一週期次のサイクル |
| Glitch-free無毛刺グリッチフリー | no否いいえ | yes是はい | yes是はい |
| Input-to-output comb. path輸入到輸出的組合路徑入力から出力への組合せ経路 | yes是はい | no否いいえ | no否いいえ |
| Extra flip-flops額外的正反器追加のフリップフロップ | 0 | 0 | one per output bit每個輸出位元一顆出力ビットごとに 1 個 |
| Typical state count典型狀態數典型的な状態数 | fewer較少少ない | more較多多い | fewer較少少ない |
Industry practice has converged on a small number of FSM templates, usually named by how many always blocks they use. All of them produce correct hardware; they differ in readability, in how easy it is to change the machine later, and in how many ways they can be got wrong.業界實務已經收斂到少數幾種 FSM 樣板,通常以它們用了幾個 always 區塊來命名。它們全都產生正確的硬體;差別在可讀性、在之後要改機器時有多容易,以及有多少種把它寫錯的方式。業界の実務は少数の FSM テンプレートに収束しており、たいてい always ブロックをいくつ使うかで呼ばれる。どれも正しいハードウェアを生む。違うのは可読性、後から機械を変更する容易さ、そして間違え方の種類の多さである。
reg [2:0] state_reg, next_state; always @ (posedge clock or posedge reset) // 1: state register (sequential) if (reset) state_reg <= start_state; else state_reg <= next_state; always @* // 2: next state + output (combinational) begin next_state = state_reg; // default: hold — kills latch inference out_bit = 1'b0; // default: deasserted case (state_reg) read_1_zero: begin if (!in_bit) begin next_state = read_2_zero; out_bit = 1'b1; end else next_state = read_1_one; end ... default: next_state = start_state; endcase end
The crucial habit here is the pair of default assignments at the top of the combinational block. Because every variable is assigned before the case runs, no path through the block can leave anything unassigned, so no latch can be inferred no matter how incomplete the case becomes during later editing. It also makes the code shorter, because branches only mention what differs from the default. This single idiom eliminates the most common FSM bug class.這裡關鍵的習慣是組合區塊最上面那一組預設指派。因為每個變數在 case 跑之前就已經被指派,所以通過這個區塊的任何路徑都不可能留下未指派的東西,於是不論之後編輯把 case 弄得多不完整,都不會推論出閂鎖。它也讓程式碼變短,因為各分支只需要提到與預設不同的部分。光是這一個慣用寫法,就消滅了最常見的那一類 FSM 錯誤。ここで肝心な習慣は組合せブロックの先頭に置く既定代入の組である。case が走る前にすべての変数が代入されるので、このブロックを通るどの経路も未代入のものを残しえない。したがって後の編集で case がどれほど不完全になってもラッチは推論されない。分岐は既定と異なる部分だけ書けばよいのでコードも短くなる。この定型一つで、FSM のバグのうちもっとも多い一群が消える。
Split the combinational block into next-state logic and output logic:把組合區塊拆成次態邏輯與輸出邏輯:組合せブロックを次状態論理と出力論理に分ける。
always @ (posedge clock or posedge reset) // 1: state register if (reset) state_reg <= start_state; else state_reg <= next_state; always @* // 2: next-state logic only begin next_state = state_reg; case (state_reg) ... endcase end always @* // 3: output logic only out_bit = (state_reg == read_2_zero) || (state_reg == read_2_one);
This is the style that makes the Mealy/Moore distinction visible at a glance: block 3's sensitivity tells you immediately which kind of machine you have. If it reads only state_reg the machine is Moore; if it also reads in_bit it is Mealy. Changing the machine from one to the other means editing one block. For anything beyond a toy FSM this is the template to use.這種風格讓 Mealy/Moore 的區別一眼可辨:第 3 個區塊的感應列立刻告訴你手上是哪一種機器。如果它只讀 state_reg,機器是 Moore;如果它也讀 in_bit,就是 Mealy。要把機器從一種改成另一種,只需要編輯一個區塊。對任何超出玩具規模的 FSM,這就是該用的樣板。この書き方ならミーリとムーアの違いは一目でわかる。第 3 ブロックの感度リストがどちらの機械かを即座に教えてくれる。state_reg しか読まなければムーア、in_bit も読めばミーリだ。一方から他方へ変えるには 1 ブロックを編集すればよい。おもちゃ規模を超える FSM なら、使うべきはこのテンプレートである。
Add a fourth, clocked block that registers the output, giving the registered-Mealy behaviour of Section 7.6(d). Common in designs where the FSM drives something timing-critical or off-chip.加上第四個、時脈驅動的區塊去暫存輸出,得到 7.6(d) 的暫存化 Mealy 行為。在 FSM 要驅動某個時序吃緊或晶片外的東西時很常見。出力をレジスタで受ける 4 つ目のクロック同期ブロックを加えると、7.6(d) のレジスタ付きミーリの振る舞いになる。FSM がタイミングに厳しいものやチップ外のものを駆動する設計でよく使われる。
always @ (posedge clock or posedge reset) // everything in one clocked block
if (reset) begin state_reg <= start_state; out_bit <= 1'b0; end
else case (state_reg)
read_1_zero: if (!in_bit) begin state_reg <= read_2_zero; out_bit <= 1'b1; end
else begin state_reg <= read_1_one; out_bit <= 1'b0; end
...
endcase
There is no next_state variable; the machine writes state_reg directly. This style is compact, it cannot infer a latch (the block is edge-sensitive, so an unassigned path simply holds), and the output is automatically registered. Its drawbacks are that the output is always one cycle late — you cannot write a plain Mealy machine this way — and that it is easy to forget to assign out_bit in some branch and get a sticky output. It is popular for small controllers and for anyone who has been burned by latches.這裡沒有 next_state 變數;機器直接寫 state_reg。這種風格很精簡,它不可能推論出閂鎖(區塊是邊緣敏感的,所以未指派的路徑單純就是保持),而且輸出自動被暫存。它的缺點是輸出永遠晚一個週期——你沒辦法用這種方式寫出一台純粹的 Mealy 機——而且很容易在某個分支忘了指派 out_bit,結果得到一個黏住的輸出。它在小型控制器中很受歡迎,也受到所有被閂鎖燙過手的人歡迎。ここには next_state 変数がなく、機械は state_reg を直接書く。この書き方はコンパクトで、ラッチを推論しえない(ブロックがエッジ感応なので、未代入の経路は単に保持になる)。出力も自動的にレジスタで受けられる。欠点は、出力が常に 1 サイクル遅れること——この書き方では素のミーリ機械は書けない——と、どこかの分岐で out_bit の代入を忘れて出力が貼り付いてしまいやすいことだ。小規模なコントローラで、そしてラッチで痛い目を見た人たちに人気がある。
case branch that does not assign next_state, or a missing default. Symptom: latches in the area report, plus possibly a combinational-loop warning. Fix: default assignments at the top of the block.次態區塊裡的閂鎖。某個 case 分支沒有指派 next_state,或是少了 default。症狀:面積報告裡出現閂鎖,可能還附帶一則組合迴路警告。修法:在區塊最上面放預設指派。次状態ブロック内のラッチ。next_state を代入しない case 分岐、あるいは default の欠落。症状:面積レポートにラッチが現れ、場合によっては組合せループ警告も伴う。対処:ブロック先頭に既定代入を置く。state_reg. Writing state_reg = next_state; inside the clocked block creates a race with any other block that reads state_reg at the same edge. Symptom: simulator-dependent behaviour; the machine appears to skip a state on one tool and not on another. Fix: <=.對 state_reg 用阻斷式指派。在時脈區塊裡寫 state_reg = next_state;,會和任何在同一個邊緣讀 state_reg 的其他區塊形成競爭。症狀:行為隨模擬器而異;機器在某套工具上看起來跳過了一個狀態,在另一套上卻沒有。修法:用 <=。state_reg へのブロッキング代入。クロック同期ブロックで state_reg = next_state; と書くと、同じエッジで state_reg を読む他のブロックとレースになる。症状:シミュレータ依存の振る舞い。あるツールでは状態を飛ばしたように見え、別のツールでは見えない。対処:<= を使う。full_case / parallel_case pragmas. These tell the synthesiser to assume the case is complete and mutually exclusive even if it cannot prove it. They remove logic and they remove the default's recovery behaviour, so an illegal state code becomes a permanent lock-up — and because the simulator ignores the pragma, the gate-level netlist and the RTL no longer agree. Avoid both; write a real default instead.full_case/parallel_case 編譯指示。它們告訴合成器:就算證明不了,也要假設這個 case 是完整且互斥的。它們拿掉邏輯,也拿掉 default 的復原行為,於是一個非法狀態碼就變成永久鎖死——而且因為模擬器忽略這個指示,閘級 netlist 與 RTL 就不再一致。兩個都不要用;請改寫一個真正的 default。full_case/parallel_case プラグマ。これらは合成器に対し、証明できなくても case が完全かつ排他的だと仮定せよと告げる。論理を削るとともに default による復帰動作も削るので、不正な状態符号は恒久的なロックアップになる。しかもシミュレータはプラグマを無視するため、ゲートレベル・ネットリストと RTL が一致しなくなる。どちらも使わないこと。本物の default を書くべきだ。The slides finish the FSM material with a completely different way of writing the same detector. Instead of enumerating states and transitions, the code describes the sequence of operations and lets the position in the code be the state.投影片用一種完全不同的方式寫出同一個偵測器,作為 FSM 內容的收尾。程式碼不去列舉狀態與轉移,而是描述操作的順序,讓程式碼中的位置本身成為狀態。スライドは FSM の話題を、同じ検出器をまったく別の書き方で示して締めくくる。状態と遷移を列挙する代わりに、コードは操作の順序を記述し、コード中の位置そのものを状態とする。
module seq_det_mealy_1imp (clock, reset, in_bit, out_bit);
input clock, reset, in_bit;
output out_bit;
reg last_bit, this_bit, flag;
wire out_bit;
always begin: wrapper_for_synthesis
@ (posedge clock /* or posedge reset */) begin: machine
if (reset == 1) begin
last_bit = 0;
this_bit = 0;
flag = 0;
disable machine; end
else begin // 1
last_bit = this_bit;
this_bit = in_bit;
forever
@ (posedge clock /* or posedge reset */) begin // 2
if (reset == 1) begin
last_bit = 0;
this_bit = 0;
flag = 0;
disable machine; end
else begin
last_bit = this_bit;
this_bit = in_bit;
flag = 1; end
end // 2
end // 1
end // machine
end // wrapper_for_synthesis
assign out_bit = (flag && (this_bit == last_bit) && (this_bit == in_bit));
endmodule
How it works. The outer always has no event control of its own; it is an infinite loop whose body is the named block machine. The first statement inside is an event control, so execution blocks there until a rising clock edge. On that edge, if reset is asserted the three registers are cleared and disable machine aborts the named block, which returns control to the top of the always and parks it at the first @ again. Otherwise the code records the first bit and enters a forever loop that waits for every subsequent clock edge, shifting this_bit into last_bit and setting flag.它怎麼運作。外層的 always 自己沒有事件控制;它是一個無窮迴圈,本體是具名區塊 machine。裡面的第一個敘述就是一個事件控制,所以執行會卡在那裡,直到時脈上升緣到來。在那個邊緣上,如果重置被確立,三個暫存器就被清除,而 disable machine 中止這個具名區塊,把控制交還到 always 的最上面,再次停在第一個 @。否則程式碼記下第一個位元,進入一個 forever 迴圈,等待其後每一個時脈邊緣,把 this_bit 移進 last_bit 並設定 flag。動作の仕組み。外側の always はそれ自体のイベント制御をもたない。本体が名前付きブロック machine である無限ループだ。その内側の最初の文がイベント制御なので、実行はクロックの立ち上がりエッジまでそこでブロックする。そのエッジでリセットがアサートされていれば三つのレジスタがクリアされ、disable machine が名前付きブロックを中断して制御を always の先頭へ返し、再び最初の @ で待機させる。そうでなければコードは最初のビットを記録し、以降のすべてのクロックエッジを待つ forever ループに入って、this_bit を last_bit へ送り flag を立てる。
Where the state lives. Three places. Two bits of it are explicit, in last_bit and this_bit. One more bit is flag, which says “at least two bits have been read” and suppresses a false detection on the very first bit — it plays exactly the role of start_state in the explicit machine. And a fourth bit of state is implicit: whether the behaviour is waiting at the first @ or at the @ inside the forever. That implicit bit is why the synthesis result on the slide contains a flip-flop labelled multiple_wait_state — the tool had to materialise the program counter.狀態住在哪裡。三個地方。其中兩個位元是明確的,在 last_bit 與 this_bit 裡。再一個位元是 flag,它表示「至少已經讀了兩個位元」,用來壓掉第一個位元上的假偵測——它扮演的角色正好就是明確機器裡的 start_state。而第四個狀態位元是隱含的:這個行為是停在第一個 @,還是停在 forever 裡面那個 @。那個隱含位元就是投影片上的合成結果裡有一顆標著 multiple_wait_state 的正反器的原因——工具必須把程式計數器具體化出來。状態はどこにあるか。三か所である。うち 2 ビットは明示的で、last_bit と this_bit にある。もう 1 ビットは flag で、「少なくとも 2 ビット読んだ」ことを表し、最初のビットでの誤検出を抑える——明示的な機械における start_state とまったく同じ役割だ。そして 4 ビット目の状態は暗黙である。すなわち、このビヘイビアが最初の @ で待っているのか、forever 内の @ で待っているのか。この暗黙のビットこそ、スライドの合成結果に multiple_wait_state というラベルのフリップフロップが含まれる理由である——ツールはプログラムカウンタを実体化せざるを得なかったのだ。
What the output expression really does. This is more subtle than it looks, and it explains the narrow pulses in the slide's waveform. At any moment after an edge, this_bit holds the bit sampled at that edge and last_bit the one before. But in_bit in the output expression is the live input, and the testbench moves it on the falling edge. So within one clock period:那個輸出運算式真正在做什麼。這比看起來微妙,而且它解釋了投影片波形裡那些窄脈衝。在邊緣之後的任何時刻,this_bit 保存的是那個邊緣取樣到的位元,last_bit 保存的是前一個。但輸出運算式裡的 in_bit 是即時的輸入,而測試平台在下降緣搬動它。所以在一個時脈週期內:その出力式が実際に何をしているか。見た目より微妙であり、スライドの波形にある細いパルスの説明にもなる。エッジ後の任意の時点で、this_bit はそのエッジでサンプルしたビットを、last_bit はその一つ前を保持する。だが出力式中の in_bit はいま現在の入力であり、テストベンチはそれを立ち下がりエッジで動かす。したがって 1 クロック周期の中では:
in_bit still equals the value just sampled, so this_bit == in_bit is trivially true and out_bit reduces to flag && (this_bit == last_bit) — the last two sampled bits were equal. This is the genuine detection.前半(上升緣之後、下降緣之前):in_bit 仍然等於剛剛被取樣的值,所以 this_bit == in_bit 自明為真,out_bit 退化成 flag && (this_bit == last_bit)——也就是最後兩個被取樣的位元相等。這是真正的偵測。前半(立ち上がりエッジの後、立ち下がりエッジの前):in_bit はまだサンプルしたばかりの値に等しいので this_bit == in_bit は自明に真となり、out_bit は flag && (this_bit == last_bit) に帰着する——直近 2 つのサンプル済みビットが等しいということだ。これが本来の検出である。in_bit has become the next bit, so out_bit additionally requires that bit to match too — effectively testing for three equal bits in a row.後半(下降緣之後):in_bit 已經變成下一個位元,所以 out_bit 額外要求那個位元也要相符——實際上變成在測試「連續三個相等的位元」。後半(立ち下がりエッジの後):in_bit は次のビットになっているので、out_bit はそのビットの一致も追加で要求する——事実上、等しいビットが 3 つ続くことを判定している。The consequence is that a detection produces a half-period pulse when the run stops at two, and a full-period pulse when the run continues to three. That is exactly the irregular pulse width visible on the slide's waveform, and it is a different output specification from either the explicit Mealy or the explicit Moore machine. If this were a real design, the extra (this_bit == in_bit) term would be a bug.結果就是:當連續只停在兩個時,偵測產生一道半週期的脈衝;當連續延伸到三個時,產生一道整週期的脈衝。這正是投影片波形上看得到的不規則脈寬,而它是一套與明確 Mealy 機或明確 Moore 機都不同的輸出規格。如果這是真實設計,多出來的 (this_bit == in_bit) 項就是一個錯誤。その結果、連続が 2 個で止まるときの検出は半周期のパルスを、連続が 3 個まで続くときは 1 周期ぶんのパルスを生む。スライドの波形に見える不揃いなパルス幅はまさにこれであり、明示的なミーリ機械ともムーア機械とも異なる出力仕様になっている。これが実際の設計なら、余分な (this_bit == in_bit) の項はバグである。
The slide shows /* or posedge reset */ commented out in both event controls and labels the page “Un-synthesized”. The reason is structural: an implicit state machine has many event controls in one behaviour, and the rules of Section 2.1 — if first, one asynchronous control, last branch is the clocked activity — cannot be applied to a behaviour with more than one place to wait. A tool that supports implicit FSMs can only offer synchronous reset, which is what the if (reset == 1) tests inside the clocked blocks provide.投影片在兩個事件控制裡都把 /* or posedge reset */ 註解掉,並把那一頁標成「Un-synthesized」。理由是結構性的:隱含式狀態機在同一個行為裡有很多個事件控制,而 2.1 節的規則——if 放第一、一個非同步控制、最後一個分支是時脈活動——沒辦法套用在一個有超過一處可以等待的行為上。支援隱含式 FSM 的工具只能提供同步重置,也就是那些時脈區塊裡的 if (reset == 1) 測試所提供的東西。スライドは二つのイベント制御の /* or posedge reset */ をコメントアウトし、そのページを「Un-synthesized」と記している。理由は構造的なものだ。暗黙の状態機械は一つのビヘイビアに多数のイベント制御をもち、2.1 節の規則——if が最初、非同期制御は一つ、最後の分岐がクロック動作——は待機箇所が複数あるビヘイビアには適用できない。暗黙 FSM に対応するツールが提供できるのは同期リセットだけで、それはクロック同期ブロック内の if (reset == 1) 判定が担っている。
More generally: multiple event controls in one behaviour, forever, and disable are all outside the RTL synthesisable subset of IEEE 1364.1. Some tools — behavioural-synthesis products and a few FPGA flows — accept them; most RTL synthesisers reject the module outright. The slide's own netlist shows that the tool used did accept it, mapping the three variables onto scan-style dffrmqb cells and adding the multiple_wait_state flip-flop. Treat the construct as a curiosity: it is useful for understanding what a state is, and it is not code to ship.更一般地說:一個行為裡有多個事件控制、forever、以及 disable,全都落在 IEEE 1364.1 的 RTL 可合成子集之外。有些工具——行為合成產品和少數 FPGA 流程——接受它們;多數 RTL 合成器會直接拒絕這個模組。投影片自己的 netlist 顯示所用的工具確實接受了,把三個變數對映到掃描式的 dffrmqb 單元上,並加了那顆 multiple_wait_state 正反器。請把這個寫法當成一樁奇聞:它有助於理解狀態是什麼,但不是拿去出貨的程式碼。より一般には、一つのビヘイビア内の複数のイベント制御、forever、disable はいずれも IEEE 1364.1 の RTL 合成可能サブセットの外にある。一部のツール——ビヘイビア合成製品といくつかの FPGA フロー——は受理するが、ほとんどの RTL 合成器はこのモジュールを丸ごと拒否する。スライド自身のネットリストは、使われたツールが確かに受理し、三つの変数をスキャン型の dffrmqb セルに写像し、multiple_wait_state フリップフロップを足したことを示している。この構文は珍品として扱うこと。状態とは何かを理解する助けにはなるが、出荷するコードではない。
There is one honest argument in its favour. For a machine whose job is a long, mostly linear sequence — a bus protocol, a memory initialisation routine, a test pattern generator — the implicit style is far more readable than twenty explicit states whose only purpose is to count. Behavioural-synthesis tools and high-level-synthesis flows exist precisely to exploit that. But in the RTL world you write the explicit machine, and if the sequence is long you write it as a counter plus a small FSM rather than as twenty states.有一個誠實的論點支持它。對於一台工作是跑一段長而幾乎線性之序列的機器——匯流排協定、記憶體初始化常式、測試圖樣產生器——隱含式風格遠比二十個唯一用途是計數的明確狀態好讀。行為合成工具與高階合成流程的存在,正是為了利用這一點。但在 RTL 的世界裡你要寫明確的機器,而如果序列很長,你會把它寫成一個計數器加一台小 FSM,而不是二十個狀態。これを支持するまっとうな論拠が一つある。長くほぼ直線的な系列をこなすのが仕事の機械——バスプロトコル、メモリ初期化ルーチン、テストパターン生成器——では、暗黙のスタイルは、数えるためだけに存在する 20 個の明示的状態よりはるかに読みやすい。ビヘイビア合成ツールや高位合成フローはまさにそれを活かすために存在する。だが RTL の世界では明示的な機械を書く。系列が長いなら、20 状態ではなくカウンタ+小さな FSM として書くのだ。
Problem: After reset, in_bit takes the values 1, 0, 1, 1, 0, 0, 0, 1 during eight successive clock periods (each value applied half a period before the edge that samples it). (a) Give state_reg during each period. (b) In which periods is the Mealy output high? (c) In which periods is the Moore output high? (d) The machine is changed so that read_2_zero on input 0 goes to a new state read_3_zero with output 0, and read_3_zero behaves like read_2_zero except that its self-loop output is 0. What does the machine now detect, and what changes in the trace?題目:重置之後,in_bit 在連續八個時脈週期中依序取值 1, 0, 1, 1, 0, 0, 0, 1(每個值都在取樣它的邊緣前半個週期被施加)。(a) 給出每個週期內的 state_reg。(b) Mealy 輸出在哪些週期為高?(c) Moore 輸出在哪些週期為高?(d) 把機器改成:read_2_zero 在輸入 0 時轉到一個新狀態 read_3_zero,其輸出為 0;而 read_3_zero 的行為與 read_2_zero 相同,只是它的自迴圈輸出是 0。現在這台機器偵測的是什麼?軌跡有什麼變化?問題:リセット後、in_bit は連続する 8 クロック周期で 1, 0, 1, 1, 0, 0, 0, 1 の値をとる(各値はそれをサンプルするエッジの半周期前に与えられる)。(a) 各周期の state_reg を示せ。(b) ミーリ出力が高になるのはどの周期か。(c) ムーア出力が高になるのはどの周期か。(d) read_2_zero が入力 0 で出力 0 の新状態 read_3_zero へ移り、read_3_zero は自己ループの出力が 0 である点を除いて read_2_zero と同じ振る舞いをするよう機械を変更する。この機械は何を検出するようになるか。またトレースはどう変わるか。
(a) Start in start_state and apply the table of Section 7.3. Writing states by their codes (0 = start, 1 = read_1_zero, 2 = read_1_one, 3 = read_2_zero, 4 = read_2_one):(a) 從 start_state 開始,套用 7.3 節的表。用編碼來寫狀態(0 = start、1 = read_1_zero、2 = read_1_one、3 = read_2_zero、4 = read_2_one):(a) start_state から始めて 7.3 節の表を適用する。状態は符号で書く(0 = start、1 = read_1_zero、2 = read_1_one、3 = read_2_zero、4 = read_2_one)。
| Period週期周期 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|
in_bit | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 1 |
state_reg during該週期內的 state_regその周期中の state_reg | 0 | 2 | 1 | 2 | 4 | 1 | 3 | 3 |
| state name狀態名稱状態名 | start | r1_one | r1_zero | r1_one | r2_one | r1_zero | r2_zero | r2_zero |
Check a couple of steps: period 1, state start, input 1 → read_1_one, which is the state during period 2. Period 4, state read_1_one, input 1 → read_2_one, the state during period 5. ✔檢查幾步:週期 1,狀態 start,輸入 1 → read_1_one,那正是週期 2 的狀態。週期 4,狀態 read_1_one,輸入 1 → read_2_one,那是週期 5 的狀態。✔いくつか確かめてみる。周期 1、状態 start、入力 1 → read_1_one、これが周期 2 の状態である。周期 4、状態 read_1_one、入力 1 → read_2_one、これが周期 5 の状態だ。✔
(b) Mealy high when (state is read_1_zero or read_2_zero and input is 0) or (state is read_1_one or read_2_one and input is 1). Period by period: 1 no, 2 no (state r1_one, input 0), 3 no (state r1_zero, input 1), 4 yes, 5 no (state r2_one, input 0), 6 yes, 7 yes, 8 no. So periods 4, 6, 7.(b) Mealy 為高的條件是:(狀態是 read_1_zero 或 read_2_zero 且輸入為 0)或(狀態是 read_1_one 或 read_2_one 且輸入為 1)。逐週期來看:1 否,2 否(狀態 r1_one、輸入 0),3 否(狀態 r1_zero、輸入 1),4 是,5 否(狀態 r2_one、輸入 0),6 是,7 是,8 否。所以是週期 4、6、7。(b) ミーリが高になるのは、(状態が read_1_zero か read_2_zero で入力が 0)または(状態が read_1_one か read_2_one で入力が 1)のとき。周期ごとに見ると:1 いいえ、2 いいえ(状態 r1_one、入力 0)、3 いいえ(状態 r1_zero、入力 1)、4 はい、5 いいえ(状態 r2_one、入力 0)、6 はい、7 はい、8 いいえ。したがって周期 4、6、7。
Sanity check against the stream 1,0,1,1,0,0,0,1: adjacent equal pairs are at (3,4), (5,6) and (6,7). The second member of each pair sits in periods 4, 6 and 7. ✔對照串流 1,0,1,1,0,0,0,1 做個合理性檢查:相鄰相等的配對在 (3,4)、(5,6) 與 (6,7)。每一對的第二個成員落在週期 4、6 與 7。✔入力列 1,0,1,1,0,0,0,1 と突き合わせて健全性を確認する。隣接する等しい対は (3,4)、(5,6)、(6,7) にある。各対の二番目の要素は周期 4、6、7 に位置する。✔
(c) Moore high when the state is read_2_zero or read_2_one: periods 5, 7, 8 — each Mealy period plus one, as expected.(c) Moore 為高的條件是狀態為 read_2_zero 或 read_2_one:週期 5、7、8——如預期,每個 Mealy 週期加一。(c) ムーアが高になるのは状態が read_2_zero か read_2_one のとき:周期 5、7、8——予想どおり、各ミーリ周期に 1 を足したものである。
(d) The modified machine asserts only on exactly two successive equal zeros, not on longer runs: entering read_2_zero still asserts, but a third 0 moves to read_3_zero whose output is 0 and whose self-loop also outputs 0. In the trace, the Mealy assertion in period 7 (the third 0 of the run 0,0,0 in periods 5–7) would disappear, leaving periods 4 and 6; the Moore assertions become 5 and 7. Note how little code this costs — one extra state and one changed arc — and how completely it changes the specification. Exam questions about sequence detectors nearly always hinge on whether overlapping or extended runs count.(d) 改過的機器只在剛好兩個連續相等的零時確立,較長的連續不算:進入 read_2_zero 仍然確立,但第三個 0 會轉到 read_3_zero,它的輸出是 0,它的自迴圈輸出也是 0。在軌跡上,週期 7 的 Mealy 確立(週期 5–7 那串 0,0,0 的第三個 0)會消失,只剩週期 4 與 6;Moore 的確立變成 5 與 7。注意這花的程式碼多麼少——多一個狀態、改一條弧——而它把規格改變得多麼徹底。關於序列偵測器的考題,幾乎都繫於「重疊或延長的連續算不算」。(d) 変更後の機械はちょうど 2 個連続した等しいゼロのときだけアサートし、それより長い連続では出さない。read_2_zero に入る時点では依然アサートするが、三つ目の 0 で read_3_zero に移り、その出力も自己ループの出力も 0 である。トレース上では、周期 7 のミーリのアサート(周期 5–7 の 0,0,0 の三つ目の 0)が消え、周期 4 と 6 だけが残る。ムーアのアサートは 5 と 7 になる。コードの変更がいかに小さく——状態を一つ増やしアークを一本変えるだけ——仕様がいかに大きく変わるかに注目してほしい。シーケンス検出器の試験問題は、ほぼ必ず「重なりや延びた連続を数えるか」で決まる。
Problem: A student submits the following. Find every defect, say what each one does to the hardware or the simulation, and rewrite it correctly.題目:某位學生交出了下面這段程式。找出每一個缺陷,說明它各自對硬體或模擬造成什麼影響,並正確地重寫它。問題:ある学生が次のコードを提出した。欠陥をすべて指摘し、それぞれがハードウェアまたはシミュレーションに何をもたらすかを述べ、正しく書き直せ。
module det (clock, reset, in_bit, out_bit);
input clock, reset, in_bit;
output out_bit;
reg [1:0] state, next;
reg out_bit;
parameter S0 = 2'b00, S1 = 2'b01, S2 = 2'b10;
always @ (posedge clock)
state = next;
always @ (state)
case (state)
S0: if (in_bit) next = S1;
S1: if (in_bit) next = S2; else next = S0;
S2: begin next = S2; out_bit = 1; end
endcase
endmodule
1. No reset. The state register has no reset at all, so the machine powers up in an unknown state and in gate-level simulation stays x forever. Add an asynchronous reset to S0.1. 沒有重置。狀態暫存器根本沒有重置,所以機器會在未知狀態下上電,而在閘級模擬裡會永遠停在 x。請加上一個回到 S0 的非同步重置。1. リセットがない。状態レジスタにリセットがまったくないので、機械は未知の状態で起動し、ゲートレベル・シミュレーションでは永久に x のままになる。S0 へ戻す非同期リセットを加えること。
2. Blocking assignment to state. state = next; in a clocked block races with the combinational block that reads state at the same edge. Use <=.2. 對 state 用阻斷式指派。時脈區塊裡的 state = next; 會與任何在同一個邊緣讀 state 的組合區塊形成競爭。請用 <=。2. state へのブロッキング代入。クロック同期ブロック内の state = next; は、同じエッジで state を読む組合せブロックとレースになる。<= を使うこと。
3. Incomplete sensitivity list. always @(state) omits in_bit, so simulation will not re-evaluate when the input changes but synthesis will wire it in — a guaranteed RTL/gate mismatch. Use always @*.3. 感應列不完整。always @(state) 漏了 in_bit,所以模擬在輸入改變時不會重新計算,而合成卻會把它接進去——保證的 RTL/閘級不一致。請用 always @*。3. 感度リストが不完全。always @(state) は in_bit を落としているので、シミュレーションは入力変化で再評価せず、合成はそれを配線する——RTL とゲートの不一致が確実に起きる。always @* を使うこと。
4. Latch on next. In S0 the if has no else, and the case has no default (the code 2'b11 is unhandled). Both leave next unassigned on some path, so the tool infers a latch.4. next 上的閂鎖。在 S0 裡那個 if 沒有 else,而 case 也沒有 default(編碼 2'b11 沒被處理)。兩者都在某些路徑上讓 next 未被指派,於是工具推論出閂鎖。4. next のラッチ。S0 の if に else がなく、case にも default がない(符号 2'b11 が未処理)。どちらもある経路で next を未代入のままにするので、ツールはラッチを推論する。
5. Latch on out_bit. out_bit is assigned only in S2, so it latches — and worse, once set it is never cleared, so the output sticks high for ever after the first detection.5. out_bit 上的閂鎖。out_bit 只在 S2 裡被指派,所以它會閂鎖——更糟的是,一旦被設起來就永遠不會被清掉,所以第一次偵測之後輸出就永遠卡在高位準。5. out_bit のラッチ。out_bit は S2 でしか代入されないのでラッチする——さらに悪いことに、いったん立つと決してクリアされないので、最初の検出以降は出力が高に貼り付く。
6. No recovery from the illegal code. With two bits there are four codes and only three states. Without a default, 2'b11 is a trap state.6. 無法從非法編碼復原。兩位元有四種編碼而只有三個狀態。沒有 default 的話,2'b11 就是一個陷阱狀態。6. 不正符号から復帰できない。2 ビットなら符号は四つあり、状態は三つしかない。default がなければ 2'b11 はトラップ状態になる。
Corrected version:更正後的版本:訂正後の版:
module det (clock, reset_n, in_bit, out_bit); input clock, reset_n, in_bit; output reg out_bit; reg [1:0] state, next; parameter S0 = 2'b00, S1 = 2'b01, S2 = 2'b10; always @ (posedge clock or negedge reset_n) // sequential if (!reset_n) state <= S0; else state <= next; always @* // combinational begin next = state; // defaults first out_bit = 1'b0; case (state) S0: if (in_bit) next = S1; S1: if (in_bit) next = S2; else next = S0; S2: begin out_bit = 1'b1; next = in_bit ? S2 : S0; end default: next = S0; endcase end endmodule
Note that out_bit is now a Moore output (it depends only on state) and that the two default assignments at the top of the combinational block make latch inference impossible regardless of how the case evolves later.注意現在 out_bit 是 Moore 輸出(它只取決於 state),而且組合區塊最上面那兩個預設指派讓閂鎖推論不可能發生,不論之後 case 怎麼演化。いまや out_bit はムーア出力であり(state だけに依存する)、組合せブロック先頭の二つの既定代入により、以後 case がどう変わろうとラッチ推論は起こりえない。
The state diagram says what the machine does; the encoding says what bit patterns the flip-flops hold. The two are independent — any encoding implements any diagram — and the choice changes flip-flop count, logic depth, power and robustness without changing the behaviour at all. In the sequence detector the encoding was fixed by five parameter declarations, and changing those five lines is the entire exercise.狀態圖說的是這台機器做什麼;編碼說的是正反器裡裝著什麼位元圖樣。兩者是獨立的——任何編碼都能實現任何狀態圖——而這個選擇會改變正反器數量、邏輯深度、功耗與強健性,卻完全不改變行為。在序列偵測器裡,編碼是由五條 parameter 宣告定死的,而改那五行就是這整件事的全部。状態図は機械が何をするかを述べ、符号化はフリップフロップがどんなビットパターンを保持するかを述べる。両者は独立で——どの符号化でもどの状態図も実装できる——その選択はフリップフロップ数、論理深度、電力、堅牢性を変えるが、振る舞いはまったく変えない。シーケンス検出器では符号化は五つの parameter 宣言で固定されており、その 5 行を変えることが作業のすべてである。
| # | Binary二進位二進 | One-hotOne-hotワンホット | Gray格雷碼グレイ | Johnson |
|---|---|---|---|---|
| 0 | 0000 | 0000000000000001 | 0000 | 00000000 |
| 1 | 0001 | 0000000000000010 | 0001 | 00000001 |
| 2 | 0010 | 0000000000000100 | 0011 | 00000011 |
| 3 | 0011 | 0000000000001000 | 0010 | 00000111 |
| 4 | 0100 | 0000000000010000 | 0110 | 00001111 |
| 5 | 0101 | 0000000000100000 | 0111 | 00011111 |
| 6 | 0110 | 0000000001000000 | 0101 | 00111111 |
| 7 | 0111 | 0000000010000000 | 0100 | 01111111 |
| 8 | 1000 | 0000000100000000 | 1100 | 11111111 |
| 9 | 1001 | 0000001000000000 | 1101 | 11111110 |
| 10 | 1010 | 0000010000000000 | 1111 | 11111100 |
| 11 | 1011 | 0000100000000000 | 1110 | 11111000 |
| 12 | 1100 | 0001000000000000 | 1010 | 11110000 |
| 13 | 1101 | 0010000000000000 | 1011 | 11100000 |
| 14 | 1110 | 0100000000000000 | 1001 | 11000000 |
| 15 | 1111 | 1000000000000000 | 1000 | 10000000 |
Each column is generated by a different rule.每一欄都由一條不同的規則產生。各列は異なる規則で生成されている。
Binary is just the state index written in base two. It uses the theoretical minimum number of flip-flops, $\lceil\log_2 N\rceil$, and nothing else about it is special.二進位就是把狀態索引寫成二進位。它用掉理論上最少的正反器數 $\lceil\log_2 N\rceil$,除此之外沒有什麼特別的。二進は状態の添字を 2 進で書いただけである。理論上最小のフリップフロップ数 $\lceil\log_2 N\rceil$ を使い、それ以外に特筆すべき点はない。
One-hot gives each state its own flip-flop and guarantees that exactly one is set. It uses $N$ flip-flops, which is the maximum any sensible encoding uses, and in exchange the logic becomes trivial: testing “am I in state $k$?” is reading one wire, and the next-state equation for bit $k$ is simply the OR of the conditions on all arcs entering state $k$.One-hot 給每個狀態一顆自己的正反器,並保證恰好只有一顆被設起來。它用掉 $N$ 顆正反器,那是任何合理編碼會用到的上限,而交換到的是邏輯變得極其簡單:測試「我在不在狀態 $k$?」就是讀一條線,而位元 $k$ 的次態方程式就只是所有進入狀態 $k$ 之弧的條件的 OR。ワンホットは各状態に専用のフリップフロップを与え、ちょうど一つだけが立つことを保証する。使うフリップフロップは $N$ 個で、まともな符号化が使う最大値だが、引き換えに論理が極めて単純になる。「状態 $k$ にいるか」を調べるのは配線 1 本を読むことであり、ビット $k$ の次状態方程式は状態 $k$ に入るすべてのアークの条件の OR にすぎない。
Gray code arranges the $2^n$ patterns so that consecutive codes differ in exactly one bit. The standard construction is the reflected binary code, obtained from the binary value $b$ as $g = b \oplus (b \gg 1)$ — check it against the table: for $b = 7 = 0111$, $b \gg 1 = 0011$ and the XOR is $0100$, which is the table's entry. It uses the same $\lceil\log_2 N\rceil$ flip-flops as binary.格雷碼把 $2^n$ 個圖樣排成使連續兩個碼恰好只差一個位元。標準做法是反射二進碼,由二進位值 $b$ 算出 $g = b \oplus (b \gg 1)$——拿表來對照看看:$b = 7 = 0111$ 時 $b \gg 1 = 0011$,XOR 得到 $0100$,正是表上的那一項。它用掉和二進位一樣的 $\lceil\log_2 N\rceil$ 顆正反器。グレイ符号は $2^n$ 個のパターンを、連続する符号がちょうど 1 ビットだけ異なるように並べる。標準的な構成は反射二進符号で、二進値 $b$ から $g = b \oplus (b \gg 1)$ として得られる——表で確かめてほしい。$b = 7 = 0111$ なら $b \gg 1 = 0011$、XOR は $0100$ で、表の値と一致する。フリップフロップ数は二進と同じ $\lceil\log_2 N\rceil$ である。
Johnson (twisted ring) shifts the complement of the top bit into the bottom, producing $2n$ codes from $n$ flip-flops: the register fills with ones from the bottom and then empties from the bottom. Like Gray, consecutive codes differ in one bit; unlike Gray, the code is not dense — the table's 16 states need 8 bits where binary would need 4.Johnson(扭環)把最高位的補數移進最低位,用 $n$ 顆正反器產生 $2n$ 個碼:暫存器從底部開始填滿 1,然後再從底部開始排空。和格雷碼一樣,連續兩個碼只差一位;和格雷碼不同的是,這個碼並不稠密——表上的 16 個狀態要用 8 個位元,而二進位只要 4 個。ジョンソン(ツイストリング)は最上位ビットの補数を最下位へシフトし、$n$ 個のフリップフロップから $2n$ 通りの符号を作る。レジスタは下から 1 で埋まり、また下から空になる。グレイと同じく連続する符号は 1 ビットしか違わないが、グレイと違って符号は密ではない——表の 16 状態には 8 ビットを要し、二進なら 4 ビットで済む。
| Encoding編碼符号化 | FFs for $N$ states$N$ 個狀態所需正反器數$N$ 状態に要する FF 数 | Next-state logic次態邏輯次状態論理 | Decode one state解碼單一狀態1 状態のデコード | Bits changing per step每步改變的位元數1 ステップで変化するビット数 | Natural home天然的歸宿本来の活躍の場 |
|---|---|---|---|---|---|
| Binary二進位二進 | $\lceil\log_2 N\rceil$ | Deepest — each bit is a function of all bits最深 — 每個位元都是所有位元的函數もっとも深い — 各ビットが全ビットの関数 | $\lceil\log_2 N\rceil$-input AND$\lceil\log_2 N\rceil$ 輸入的 AND$\lceil\log_2 N\rceil$ 入力 AND | up to all of them最多可能全部最大で全ビット | Area-limited ASIC blocks, large $N$面積受限的 ASIC 區塊、$N$ 很大時面積制約のある ASIC ブロック、$N$ が大きい場合 |
| One-hotOne-hotワンホット | $N$ | Shallowest — one OR per state最淺 — 每個狀態一顆 ORもっとも浅い — 状態ごとに OR 1 個 | a single wire一條線就夠配線 1 本 | 2 | FPGA, speed-critical controlFPGA、速度吃緊的控制邏輯FPGA、速度が厳しい制御 |
| Gray格雷碼グレイ | $\lceil\log_2 N\rceil$ | Similar to binary與二進位相近二進と同程度 | $\lceil\log_2 N\rceil$-input AND$\lceil\log_2 N\rceil$ 輸入的 AND$\lceil\log_2 N\rceil$ 入力 AND | 1 (for sequential transitions)1(對依序轉移而言)1(順次遷移の場合) | Counters crossing clock domains, low power跨時脈域的計數器、低功耗クロックドメインを跨ぐカウンタ、低消費電力 |
| Johnson | $\lceil N/2\rceil$ | Shallow — one inverter in the ring淺 — 環上一顆反相器浅い — リングにインバータ 1 個 | 2-input gate兩輸入閘2 入力ゲート | 1 | Timing generators, small cyclic machines時序產生器、小型循環機器タイミング生成器、小さな巡回機械 |
The headline trade is between sequential area and combinational area. For the five-state detector, binary needs 3 flip-flops and one-hot needs 5 — two extra flip-flops, perhaps 50 µm² in a mature process. In exchange, the Moore output goes from a comparison against two 3-bit codes to state_reg[3] | state_reg[4], a single OR gate. For five states that is roughly a wash. For twenty states it is not: binary needs 5 flip-flops and a next-state cone that is a function of all five bits plus the inputs, with a logic depth of three or four levels; one-hot needs 20 flip-flops but each next-state bit is an OR of a handful of arc conditions, depth one or two. One-hot wins on speed almost always and loses on area once $N$ gets large.最主要的取捨是循序面積對組合面積。對五狀態的偵測器,二進位需要 3 顆正反器,one-hot 需要 5 顆——多兩顆,在成熟製程裡大概 50 µm²。交換到的是:Moore 輸出從「比對兩個 3 位元碼」變成 state_reg[3] | state_reg[4],一顆 OR 閘。對五個狀態來說大致打平。對二十個狀態就不是了:二進位要 5 顆正反器,而次態邏輯錐是那五個位元加上輸入的函數,邏輯深度三到四層;one-hot 要 20 顆正反器,但每個次態位元只是少數幾個弧條件的 OR,深度一到兩層。One-hot 幾乎永遠在速度上勝出,而一旦 $N$ 變大就在面積上落敗。主たるトレードオフは順序回路面積と組合せ回路面積のあいだにある。5 状態の検出器では二進は 3 個、ワンホットは 5 個のフリップフロップを要する——2 個増で、成熟プロセスならおよそ 50 µm²。引き換えに、ムーア出力は 2 つの 3 ビット符号との比較から state_reg[3] | state_reg[4]、すなわち OR ゲート 1 個になる。5 状態ではほぼ互角だ。20 状態ではそうはいかない。二進はフリップフロップ 5 個に加え、次状態コーンが 5 ビット全部と入力の関数となり論理深度は 3〜4 段。ワンホットはフリップフロップ 20 個だが、各次状態ビットはごく少数のアーク条件の OR で深さ 1〜2 段である。ワンホットは速度ではほぼ常に勝ち、$N$ が大きくなると面積で負ける。
On an FPGA the decision is nearly automatic. Flip-flops are free — every logic cell contains one whether you use it or not — while routing and LUT levels are the scarce resource. One-hot minimises both, so FPGA synthesis tools default to it for machines up to a few dozen states. On an ASIC, flip-flops cost real area and real leakage power, so the default is binary and one-hot is used selectively for the critical machine.在 FPGA 上這個決定幾乎是自動的。正反器是免費的——每個邏輯單元裡都有一顆,你用不用都在——而稀缺資源是繞線與 LUT 層數。One-hot 把兩者都最小化,所以 FPGA 合成工具對幾十個狀態以內的機器預設就用它。在 ASIC 上,正反器要花真實的面積與真實的漏電功耗,所以預設是二進位,而 one-hot 只被選擇性地用在關鍵的那台機器上。FPGA ではこの判断はほぼ自動である。フリップフロップは無料だ——各ロジックセルに使っても使わなくても一つ入っている——一方で希少資源は配線と LUT の段数である。ワンホットは両方を最小化するので、FPGA の合成ツールは数十状態までの機械には既定でこれを選ぶ。ASIC ではフリップフロップが実面積と実リーク電力を消費するので、既定は二進で、ワンホットは重要な機械に選択的に使う。
Dynamic power is proportional to the number of nodes that toggle. One-hot toggles exactly two flip-flops per transition regardless of $N$. Binary toggles between one and $\lceil\log_2 N\rceil$, averaging about two for a random walk but spiking on carry-like transitions such as 0111 → 1000. Gray toggles exactly one, which is why Gray-coded counters are standard in always-on, power-critical blocks. But in a machine with many flip-flops, one-hot's advantage in switching is offset by its disadvantage in leakage, so the honest answer again depends on $N$ and on the duty cycle.動態功耗與翻轉的節點數成正比。One-hot 每次轉移恰好翻轉兩顆正反器,與 $N$ 無關。二進位翻轉的數量在 1 到 $\lceil\log_2 N\rceil$ 之間,隨機遊走下平均大約兩個,但在像 0111 → 1000 這種進位式轉移時會爆衝。格雷碼恰好翻轉一個,這就是為什麼格雷編碼的計數器在常開、功耗吃緊的區塊裡是標準做法。但在正反器很多的機器裡,one-hot 在切換上的優勢會被它在漏電上的劣勢抵銷,所以誠實的答案又一次取決於 $N$ 和工作週期。動的電力はトグルするノード数に比例する。ワンホットは $N$ によらず遷移ごとにちょうど 2 個のフリップフロップをトグルする。二進は 1 から $\lceil\log_2 N\rceil$ のあいだでトグルし、ランダムウォークなら平均 2 程度だが、0111 → 1000 のようなキャリー的遷移では跳ね上がる。グレイはちょうど 1 なので、グレイ符号カウンタは常時オンで電力に厳しいブロックの定番である。ただしフリップフロップの多い機械では、ワンホットのスイッチング上の利点がリーク上の不利に相殺されるので、正直な答えはまた $N$ とデューティ次第になる。
If a state vector is read by logic in another clock domain, the fact that only one bit changes per step becomes a correctness property rather than a power optimisation. With binary coding, a receiver sampling 0111 → 1000 asynchronously can latch any of 0000, 1111 or anything in between, because the four bits do not settle simultaneously. With Gray coding only one bit is in flight, so the receiver sees either the old value or the new one — never a third. This is exactly why FIFO read and write pointers are Gray coded, and it is the main reason Gray appears in this table at all.如果狀態向量會被另一個時脈域的邏輯讀取,「每一步只有一個位元改變」就從功耗最佳化變成一項正確性性質。用二進位編碼時,一個非同步取樣 0111 → 1000 的接收端可能鎖到 0000、1111 或介於其間的任何值,因為四個位元不會同時安定。用格雷碼時只有一個位元在飛,所以接收端看到的不是舊值就是新值——絕不會是第三個值。這正是為什麼 FIFO 的讀寫指標要用格雷編碼,也是格雷碼之所以會出現在這張表裡的主要理由。状態ベクタが別のクロックドメインの論理に読まれる場合、「1 ステップで 1 ビットしか変わらない」ことは電力最適化ではなく正しさの性質になる。二進符号では、0111 → 1000 を非同期にサンプルする受信側は 0000 や 1111、あるいはその中間の何かをラッチしうる。4 ビットが同時に落ち着かないからだ。グレイ符号なら飛んでいるビットは 1 本だけなので、受信側は古い値か新しい値のどちらかしか見ない——第三の値は決して現れない。FIFO の読み書きポインタがグレイ符号である理由がこれであり、グレイがそもそもこの表に載っている主な理由でもある。
You can impose an encoding by writing the parameter values yourself, as the slides do, or you can declare the states symbolically and let synthesis choose with an attribute such as // synopsys state_vector or an fsm_encoding directive. Tools can also re-encode a machine you wrote in binary into one-hot if that meets timing better. Be aware of the consequence: if the tool re-encodes, the state codes in the netlist no longer match your RTL, so a gate-level waveform will not show the values you expect and any testbench that pokes at state_reg will break. For a machine whose encoding matters — one that crosses domains, or whose outputs must be a particular decode — pin the encoding explicitly and disable re-encoding.你可以像投影片那樣自己寫 parameter 的值來強制編碼,也可以用符號方式宣告狀態,再用 // synopsys state_vector 這類屬性或 fsm_encoding 指示詞讓合成去選。工具甚至可以把你用二進位寫的機器重新編碼成 one-hot,如果那樣比較容易收時序。要注意後果:如果工具重新編碼,netlist 裡的狀態碼就不再和你的 RTL 一致,閘級波形不會顯示你預期的值,任何會去戳 state_reg 的測試平台都會壞掉。對於編碼本身很重要的機器——會跨域的,或輸出必須是某個特定解碼的——請明確釘死編碼並停用重新編碼。スライドのように parameter の値を自分で書いて符号化を強制してもよいし、状態を記号的に宣言して // synopsys state_vector のような属性や fsm_encoding ディレクティブで合成に選ばせてもよい。ツールは、そのほうがタイミングを満たすなら、二進で書いた機械をワンホットへ再符号化することさえできる。その帰結に注意すること。ツールが再符号化すると、ネットリスト中の状態符号は RTL と一致しなくなり、ゲートレベル波形は期待した値を示さず、state_reg を突くテストベンチはすべて壊れる。符号化そのものが重要な機械——ドメインを跨ぐもの、出力が特定のデコードでなければならないもの——では、符号化を明示的に固定し再符号化を禁止すること。
Every encoding except a perfectly dense binary one leaves unused codes, and the sparser the encoding the more of them there are.除了完全稠密的二進位之外,每一種編碼都會留下沒用到的碼,而編碼越稀疏,剩下的就越多。完全に密な二進を除けば、どの符号化も未使用の符号を残し、符号化が疎であるほどその数は増える。
| Encoding of 5 states5 個狀態的編碼5 状態の符号化 | Flip-flops正反器數フリップフロップ数 | Total codes總碼數符号の総数 | Legal合法合法 | Illegal非法不正 |
|---|---|---|---|---|
| Binary / Gray二進位/格雷碼二進/グレイ | 3 | 8 | 5 | 3 |
| Johnson | 3 | 8 | 5 (of 6 available)5(可用的 6 個之中)5(使える 6 個のうち) | 3 |
| One-hotOne-hotワンホット | 5 | 32 | 5 | 27 |
A machine can land in an illegal code three ways: reset-release skew (Section 2.5), a single-event upset from a cosmic ray or alpha particle, or a setup violation on a marginal path. Once there, what happens depends entirely on your default.機器會落進非法碼有三種途徑:重置釋放的歪斜(2.5 節)、宇宙射線或 α 粒子造成的單事件翻轉,或是某條邊緣路徑上的 setup 違規。一旦到了那裡,接下來會怎樣完全取決於你的 default。機械が不正符号に落ちる道は三つある。リセット解除のスキュー(2.5 節)、宇宙線やアルファ粒子による単一事象アップセット、そして際どい経路でのセットアップ違反である。いったんそこへ落ちたあと何が起きるかは、あなたの default 次第で完全に決まる。
With default: next_state = start_state; every illegal code maps to the reset state, so the machine recovers within one clock and the damage is bounded to whatever it did while confused. Without a default, the next-state logic for an illegal code is whatever the minimisation happened to produce — possibly a legal state, possibly a self-loop that locks the machine up until the next system reset. The cost of the default is a few gates; the cost of omitting it is a field failure that requires a power cycle. There is no reason not to write it.有 default: next_state = start_state; 時,每一個非法碼都對映到重置狀態,所以機器在一個時脈內復原,損害被限制在它神智不清期間所做的那些事。沒有 default 時,非法碼的次態邏輯就是邏輯最小化剛好產生出來的東西——可能是某個合法狀態,也可能是一個自迴圈,把機器鎖死到下一次系統重置為止。寫那個 default 的代價是幾顆閘;不寫的代價是一次需要斷電重開的現場失效。沒有理由不寫。default: next_state = start_state; があれば、どの不正符号もリセット状態へ写るので機械は 1 クロック以内に復帰し、被害は混乱していたあいだにしたことに限られる。default がなければ、不正符号に対する次状態論理は論理最小化がたまたま生んだものになる——合法な状態かもしれないし、次のシステムリセットまで機械をロックする自己ループかもしれない。default を書く代価はゲート数個、書かない代価は電源再投入を要する現場故障である。書かない理由はない。
With one-hot encoding, 27 of 32 codes are illegal, and a single bit flip always lands in one of them — either two bits set or none. A case on a one-hot vector written as case (state_reg) 5'b00001: ... 5'b00010: ... will fall straight into the default, which is good. But a common one-hot idiom uses case (1'b1) state_reg[0]: ... state_reg[1]: ... (a “reverse case”), and that construct silently picks the first matching bit when two are set, hiding the error instead of recovering from it. If one-hot robustness matters, add an explicit legality check: if (state_reg != (state_reg & -state_reg) || state_reg == 0) next_state = start_state; or simply compare against the enumerated codes.用 one-hot 編碼時,32 個碼中有 27 個是非法的,而單一位元翻轉必然落進其中之一——不是兩個位元被設起來,就是一個都沒有。寫成 case (state_reg) 5'b00001: ... 5'b00010: ... 的 one-hot case 會直接落進 default,那很好。但一種常見的 one-hot 慣用寫法用的是 case (1'b1) state_reg[0]: ... state_reg[1]: ...(所謂「反向 case」),而那個構造在兩個位元同時被設起來時會默默挑第一個相符的位元,把錯誤藏起來而不是從中復原。如果 one-hot 的強健性很重要,就加一個明確的合法性檢查:if (state_reg != (state_reg & -state_reg) || state_reg == 0) next_state = start_state;,或者乾脆去比對那些列舉出來的碼。ワンホット符号化では 32 符号のうち 27 が不正であり、1 ビットの反転は必ずそのどれかに落ちる——2 ビットが立つか、1 ビットも立たないかだ。case (state_reg) 5'b00001: ... 5'b00010: ... と書いたワンホットの case はまっすぐ default に落ちる。これはよい。だがよく使われるワンホットの定型 case (1'b1) state_reg[0]: ... state_reg[1]: ...(いわゆる「逆 case」)は、2 ビットが立っているとき黙って最初に一致したビットを選び、誤りから復帰する代わりにそれを隠してしまう。ワンホットの堅牢性が重要なら、明示的な合法性検査を加えること:if (state_reg != (state_reg & -state_reg) || state_reg == 0) next_state = start_state;、あるいは単に列挙した符号と比較すればよい。
Problem: The sequence detector has five states. (a) How many flip-flops does each of the four encodings need? (b) Write the one-hot parameter list and the resulting Moore output expression. (c) With one-hot, write the next-state equation for the read_2_zero bit directly from the state diagram. (d) A 24-state controller sits on the critical path of a 1 GHz design and currently uses binary encoding with a next-state cone four levels deep. Estimate the effect of switching to one-hot on flip-flop count, logic depth and the Moore output decode. (e) Why would you not use one-hot for a 1024-entry FIFO pointer?題目:序列偵測器有五個狀態。(a) 四種編碼各需要幾顆正反器?(b) 寫出 one-hot 的 parameter 清單,以及由此得到的 Moore 輸出運算式。(c) 用 one-hot,直接從狀態圖寫出 read_2_zero 那個位元的次態方程式。(d) 一台 24 狀態的控制器位於某個 1 GHz 設計的關鍵路徑上,目前用二進位編碼,次態邏輯錐四層深。估計改用 one-hot 對正反器數量、邏輯深度與 Moore 輸出解碼的影響。(e) 為什麼你不會把 one-hot 用在一個 1024 項的 FIFO 指標上?問題:シーケンス検出器には状態が五つある。(a) 四つの符号化はそれぞれ何個のフリップフロップを要するか。(b) ワンホットの parameter 一覧と、そこから得られるムーア出力の式を書け。(c) ワンホットで、状態図から直接 read_2_zero ビットの次状態方程式を書け。(d) 24 状態のコントローラが 1 GHz 設計のクリティカルパス上にあり、現在は二進符号化で次状態コーンは 4 段の深さである。ワンホットに切り替えたときのフリップフロップ数、論理深度、ムーア出力デコードへの影響を見積もれ。(e) 1024 エントリの FIFO ポインタにワンホットを使わないのはなぜか。
(a) Binary: $\lceil\log_2 5\rceil = 3$. Gray: 3 (same density). Johnson: $\lceil 5/2 \rceil = 3$, since 3 flip-flops give 6 Johnson states and 5 are needed. One-hot: 5.(a) 二進位:$\lceil\log_2 5\rceil = 3$。格雷碼:3(密度相同)。Johnson:$\lceil 5/2 \rceil = 3$,因為 3 顆正反器給出 6 個 Johnson 狀態,而我們需要 5 個。One-hot:5。(a) 二進:$\lceil\log_2 5\rceil = 3$。グレイ:3(密度は同じ)。ジョンソン:$\lceil 5/2 \rceil = 3$。フリップフロップ 3 個でジョンソン状態が 6 つ得られ、必要なのは 5 つだからである。ワンホット:5。
(b)
parameter start_state = 5'b00001;
parameter read_1_zero = 5'b00010;
parameter read_1_one = 5'b00100;
parameter read_2_zero = 5'b01000;
parameter read_2_one = 5'b10000;
assign out_bit = state_reg[3] | state_reg[4]; // Moore: one OR gate
Compare with the binary version, which needs two 3-bit equality comparisons and an OR — roughly eight gates instead of one.與二進位版本相比,後者需要兩次 3 位元相等比較再加一顆 OR——大約八顆閘,而不是一顆。二進版と比べてみよう。そちらは 3 ビットの等値比較が 2 回と OR が 1 個——ゲートにしておよそ 8 個であり、1 個ではない。
(c) From the diagram, read_2_zero is entered from read_1_zero on input 0 and from itself on input 0. With one-hot, the next-state bit is the OR of those two arc conditions:(c) 從狀態圖來看,read_2_zero 是在輸入 0 時由 read_1_zero 進入,以及在輸入 0 時由它自己進入。用 one-hot,次態位元就是那兩個弧條件的 OR:(c) 状態図より、read_2_zero へは入力 0 のとき read_1_zero から、および入力 0 のとき自分自身から入る。ワンホットなら次状態ビットはその二つのアーク条件の OR である。
assign next_state[3] = (state_reg[1] & ~in_bit) // from read_1_zero on 0 | (state_reg[3] & ~in_bit); // self loop on 0 // = ~in_bit & (state_reg[1] | state_reg[3]) -- two gates, one level
Notice that no decoding is needed: the state bits are the decode. This is the structural reason one-hot logic is shallow.注意完全不需要解碼:狀態位元就是解碼結果。這就是 one-hot 邏輯很淺的結構性原因。デコードがまったく不要である点に注目してほしい。状態ビットそのものがデコード結果なのだ。これがワンホットの論理が浅い構造上の理由である。
(d) Flip-flops go from $\lceil\log_2 24\rceil = 5$ to 24, an increase of 19 — perhaps 400 µm² and a little leakage. Logic depth typically drops from four levels to one or two, because every next-state bit becomes an OR of arc terms with no state decoding in front. On a 1 ns period, saving two levels of logic is on the order of 150–250 ps, which is a large fraction of the budget. The Moore output decode collapses from a comparison against a 5-bit code to a single wire or a small OR. For a critical machine this is an easy trade: it is the standard reason ASIC designers reach for one-hot despite preferring binary by default.(d) 正反器從 $\lceil\log_2 24\rceil = 5$ 變成 24,增加 19 顆——大概 400 µm² 再加上一點漏電。邏輯深度通常從四層掉到一到兩層,因為每個次態位元都變成弧項的 OR,前面不再有狀態解碼。在 1 ns 的週期下,省下兩層邏輯大約是 150–250 ps,那是預算中很大的一塊。Moore 輸出解碼從「比對一個 5 位元碼」塌縮成一條線或一顆小 OR。對關鍵機器而言這是很划算的交換:這正是 ASIC 設計者雖然預設偏好二進位、卻仍會伸手去拿 one-hot 的標準理由。(d) フリップフロップは $\lceil\log_2 24\rceil = 5$ から 24 へ、19 個増える——およそ 400 µm² と若干のリークだ。論理深度はたいてい 4 段から 1〜2 段へ下がる。各次状態ビットが、前段に状態デコードを持たないアーク項の OR になるからである。1 ns の周期では論理 2 段ぶんの節約は 150–250 ps のオーダーで、予算の大きな部分を占める。ムーア出力のデコードは 5 ビット符号との比較から配線 1 本か小さな OR に縮む。重要な機械にとってこれは易しい取引であり、ASIC 設計者が既定では二進を好みつつワンホットに手を伸ばす標準的な理由である。
(e) Because a 1024-entry FIFO pointer has 1024 states, and one-hot would need 1024 flip-flops where binary needs 10. More importantly, a FIFO pointer has to be compared (full/empty detection) and usually has to cross a clock domain, and a one-hot vector 1024 bits wide is both expensive to compare and expensive to synchronise. The right answer for a FIFO pointer is a Gray-coded binary counter: 10 flip-flops, one bit changing per increment so it can be synchronised safely into the other domain, and a cheap comparison.(e) 因為 1024 項的 FIFO 指標有 1024 個狀態,而 one-hot 會需要 1024 顆正反器,二進位只要 10 顆。更重要的是,FIFO 指標必須被比較(滿/空的偵測),而且通常必須跨時脈域,而一個 1024 位元寬的 one-hot 向量既昂貴於比較,也昂貴於同步。FIFO 指標的正確答案是格雷編碼的二進位計數器:10 顆正反器、每次遞增只改一個位元因此可以安全地同步進另一個域,而且比較很便宜。(e) 1024 エントリの FIFO ポインタは 1024 状態をもち、ワンホットなら 1024 個のフリップフロップを要する。二進なら 10 個だ。さらに重要なのは、FIFO ポインタは比較(フル/エンプティ検出)されねばならず、たいていクロックドメインも跨ぐことである。1024 ビット幅のワンホットベクタは比較も同期化も高くつく。FIFO ポインタの正解はグレイ符号の二進カウンタである。フリップフロップ 10 個、インクリメントごとに 1 ビットしか変わらないので他ドメインへ安全に同期でき、比較も安い。
The questions that catch people are nearly always one of these.會坑到人的題目幾乎永遠是下面這幾種之一。引っかかる問題はほぼ必ず次のどれかである。
else. In a clocked block it is an enable; in a combinational block it is a latch. Always say which kind of block you are looking at before answering.缺少的 else。在時脈區塊裡它是致能;在組合區塊裡它是閂鎖。回答之前,一定要先說出你看的是哪一種區塊。欠けた else。クロック同期ブロックではイネーブル、組合せブロックではラッチになる。答える前に、見ているのがどちらの種類のブロックかを必ず言うこと。negedge reset must pair with if (reset == 1'b0). The Shift_reg4 example uses active-low reset while every other module in the lecture uses active-high; questions love to mix them.重置極性與邊緣。negedge reset 必須搭配 if (reset == 1'b0)。Shift_reg4 那個例子用的是低位準作用的重置,而本講其他每個模組用的都是高位準作用;考題最愛把它們混在一起。リセットの極性とエッジ。negedge reset は if (reset == 1'b0) と組にしなければならない。Shift_reg4 の例は負論理リセットを使い、本講の他のモジュールはすべて正論理である。試験問題はこれを混ぜるのが大好きだ。{Data_in, Data_reg[3:1]} shifts towards the LSB; {Data_out[6:0], Data_out[7]} rotates towards the MSB.移位方向。把串接一位元一位元解開。{Data_in, Data_reg[3:1]} 往 LSB 方向移;{Data_out[6:0], Data_out[7]} 往 MSB 方向旋轉。シフトの向き。連結をビットごとに読み解くこと。{Data_in, Data_reg[3:1]} は LSB 方向へシフトし、{Data_out[6:0], Data_out[7]} は MSB 方向へ回転する。0111 to 1000 it passes through 0110, 0100, 0000. Any decode of those bits glitches.漣波計數器的中間值。從 0111 走到 1000 時,它會經過 0110、0100、0000。任何對那些位元的解碼都會出毛刺。リプルカウンタの中間値。0111 から 1000 へ向かう途中、0110、0100、0000 を通過する。それらのビットをデコードすれば必ずグリッチする。{ } are evaluated at their own width. An addition inside a concatenation loses its carry unless you widen the operands — which is why the two 1'b0 in the pipelined adder must stay.自決寬度。{ } 裡面的運算元以自己的寬度求值。串接裡的加法會丟掉進位,除非你把運算元加寬——這就是管線化加法器裡那兩個 1'b0 必須留下的原因。自己決定の幅。{ } の中のオペランドは自分の幅で評価される。連結内の加算は、オペランドを広げない限りキャリーを失う——パイプライン加算器の二つの 1'b0 を残さねばならない理由である。<=, combinational uses =.時脈邏輯用 <=,組合邏輯用 =。クロック同期論理は <=、組合せ論理は =。always block, one assignment operator.一個變數、一個 always 區塊、一種指派運算子。変数一つに always ブロック一つ、代入演算子一種。if first; last branch is the clocked activity.if 放第一;最後一個分支是時脈活動。if を最初に。最後の分岐がクロック同期の動作。{in, r[n-1:1]}. Rotate: {r[6:0], r[7]}.移位:{in, r[n-1:1]}。旋轉:{r[6:0], r[7]}。シフト:{in, r[n-1:1]}。回転:{r[6:0], r[7]}。else in a clocked block = enable, not latch.時脈區塊裡缺 else = 致能,不是閂鎖。クロック同期ブロックで else が欠けている=イネーブルであってラッチではない。count <= count + 1 by default.預設就用同步的 count <= count + 1。既定では同期の count <= count + 1 を使う。default; never use full_case.永遠寫一個真正的 default;絕不要用 full_case。必ず本物の default を書く。full_case は使わない。reg [2:0] state_reg, next_state; always @ (posedge clock or negedge reset_n) // 1. state register if (!reset_n) state_reg <= S0; else state_reg <= next_state; always @* // 2. next-state logic begin next_state = state_reg; // default: hold case (state_reg) S0: if (go) next_state = S1; S1: next_state = S2; S2: if (done) next_state = S0; default: next_state = S0; // illegal-state recovery endcase end assign out = (state_reg == S2); // 3. Moore output
always blocks; this lecture turns those semantics into flip-flops.第 4 講確立了阻斷式與非阻斷式指派的區別以及 always 區塊的語義;這一講把那些語義變成正反器。第 4 講はブロッキング代入とノンブロッキング代入の違い、および always ブロックの意味論を確立した。本講はその意味論をフリップフロップに変える。forever, disable and multiple event controls are outside it — the formal version of Section 7.8.第 7 講逐一走過可合成子集的每個語法構造,並解釋為什麼 forever、disable 與多個事件控制落在它之外——那是 7.8 節的正式版本。第 7 講は合成可能サブセットを構文ごとに辿り、forever、disable、複数イベント制御がなぜその外にあるかを説明する——7.8 節の形式的な版である。If you take one idea away from this lecture, make it this: the synthesiser is a pattern matcher, and every rule in these thirty-seven slides is a description of one pattern. The event control picks the template, the assignment operator decides the ordering semantics, the completeness of the branches decides whether a value has to be remembered, and the placement of the output decides whether a signal is clean. None of it is arbitrary, and none of it requires memorisation once you can picture the flip-flop the tool is about to allocate.如果你只從這一講帶走一個想法,請讓它是這個:合成器是一台樣板比對機,而這三十七張投影片裡的每一條規則都是對某一個樣板的描述。事件控制挑選樣板,指派運算子決定順序語義,分支的完整性決定某個值是否必須被記住,而輸出的擺放位置決定一條訊號乾不乾淨。這裡面沒有一件事是任意的,而一旦你能在腦中看見工具即將配出來的那顆正反器,就沒有一件事需要死背。本講から一つだけ持ち帰るなら、これにしてほしい。合成器はパターンマッチャであり、この 37 枚のスライドにある規則はどれも一つのパターンの記述である。イベント制御がテンプレートを選び、代入演算子が順序の意味論を決め、分岐の完全性が値を記憶する必要があるかを決め、出力の置き場所が信号のきれいさを決める。どれ一つとして恣意的なものはなく、ツールがこれから割り当てようとしているフリップフロップを思い描けるようになれば、どれ一つとして丸暗記は要らない。