Outline — click to expand大綱 — 點擊展開アウトライン — クリックで展開
1. Primitives and UDPs1. 原型與 UDP1. プリミティブと UDP
2. Inside the UDP Table2. UDP 表格的內部2. UDP テーブルの内側
3. Combinational UDPs3. 組合邏輯 UDP3. 組み合わせ UDP
4. Sequential UDPs4. 循序邏輯 UDP4. 順序 UDP
5. Gate Delay5. 閘延遲5. ゲート遅延
6. Timescale and Precision6. 時間刻度與精度6. タイムスケールと精度
7. Net Delay7. net 延遲7. net 遅延
8. Module Path Delay8. 模組路徑延遲8. モジュールパス遅延
9. Composing the Layers9. 三層的組合9. 層を組み合わせる
10. Syntax, Tasks, Directives10. 語法、系統任務與編譯指令10. 構文・システムタスク・コンパイラ指令
11. Summary11. 總結11. まとめ
← DCIC Study Guides← 回到 DCIC 自學指南← DCIC 自学ガイドへ戻る
EE5733 Digital Communication IC Design · Lecture 3EE5733 數位通訊積體電路設計 · 第 3 講EE5733 デジタル通信 IC 設計 · 第 3 講

Syntax of the Verilog Model, ExplainedVerilog 模型的語法,完整解說Verilog モデルの構文、詳説

User-defined primitives as truth-table machines, the three layers of propagation delay, timescale rounding, specify blocks and pulse control, and the compiler directives that hold a cell library together.把使用者自訂原型當成真值表機器來看、傳播延遲的三個層次、timescale 的四捨五入、specify 區塊與脈衝控制,以及把一整座單元庫綁在一起的編譯指令。真理値表マシンとしてのユーザ定義プリミティブ、伝搬遅延の三つの層、タイムスケールの丸め、specify ブロックとパルス制御、そしてセルライブラリ全体をまとめ上げるコンパイラ指令。

Contents目錄目次

  1. Primitives, UDPs and Where They Fit原型、UDP,以及它們的定位プリミティブ、UDP、そしてその位置づけ
  2. Inside the UDP Table: How a Row Is MatchedUDP 表格的內部:一列是怎麼被對應到的UDP テーブルの内側:行はどう照合されるか
  3. Combinational UDPs in Detail組合邏輯 UDP 詳解組み合わせ UDP の詳細
  4. Sequential UDPs: Level- and Edge-Sensitive Tables循序邏輯 UDP:準位敏感表與邊緣敏感表順序 UDP:レベル感応表とエッジ感応表
  5. Gate Delay and the Inertial Model閘延遲與慣性延遲模型ゲート遅延と慣性遅延モデル
  6. Timescale, Precision and the Rounding Trap時間刻度、精度,以及四捨五入的陷阱タイムスケール、精度、そして丸めの落とし穴
  7. Net Delay: Delay That Belongs to the Wirenet 延遲:屬於導線的延遲net 遅延:配線に属する遅延
  8. Module Path Delay and the specify Block模組路徑延遲與 specify 區塊モジュールパス遅延と specify ブロック
  9. How the Three Delay Layers Compose三層延遲如何組合起來三つの遅延層はどう合成されるか
  10. BNF, System Tasks and Compiler DirectivesBNF、系統任務與編譯指令BNF、システムタスク、コンパイラ指令
  11. Summary, Checklist and Cross-References總結、檢查表與交叉參照まとめ、チェックリスト、相互参照

1. Primitives, UDPs and Where They Fit1. 原型、UDP,以及它們的定位1. プリミティブ、UDP、そしてその位置づけ

Lecture 3 looks like three unrelated topics stapled together: truth tables, delay numbers, and a list of backtick directives. They are not unrelated. They are the three things you need in order to write a simulation model of a piece of silicon that somebody else fabricated. A standard-cell library vendor hands you a .v file for every cell in the library. Open one. You will find a primitive with a truth table, a specify block full of numbers, and a wrapper of compiler directives. That file is the entire content of this lecture, written by a foundry instead of by you. Understanding it is the difference between running a post-layout simulation and believing it.第 3 講看起來像是三個不相干的題目被硬釘在一起:真值表、延遲數字,還有一串反引號開頭的指令。它們並非不相干。這三樣東西正是你要寫出別人代工做出來的那塊矽的模擬模型時所需要的全部。標準單元庫的供應商會為庫中每一顆單元交給你一個 .v 檔。隨便打開一個來看,你會看到一個帶真值表的 primitive、一個塞滿數字的 specify 區塊,再加上一層編譯指令包裝。那個檔案的內容就是本講的全部,只是由晶圓廠寫的,而不是你寫的。看懂它,是「跑過佈局後模擬」和「相信佈局後模擬」之間的差別。第 3 講は、互いに無関係な三つの話題をホチキスで留めただけのように見える。真理値表、遅延の数値、そしてバッククォートで始まる指令の一覧である。しかしこれらは無関係ではない。誰か他人が製造したシリコンのシミュレーションモデルを書くために必要な三つが、ちょうどこれらなのである。標準セルライブラリのベンダは、ライブラリ内のすべてのセルについて .v ファイルを渡してくる。一つ開いてみればよい。真理値表を持つ primitive、数値で埋まった specify ブロック、そしてコンパイラ指令による包みが見つかるはずだ。そのファイルの中身こそが本講のすべてであり、ただ書いたのが自分ではなくファウンドリだというだけである。それを理解しているかどうかが、レイアウト後シミュレーションを「走らせた」と「信じられる」の差になる。

1.1 What a primitive is, and why it is special1.1 原型是什麼,又為什麼特別1.1 プリミティブとは何か、なぜ特別なのか

A primitive in Verilog is a built-in logic model: and, or, nand, nor, buf, not, xor, xnor, plus the three-state family bufif0, bufif1, notif0, notif1, the MOS switches nmos, pmos, cmos and their resistive variants, and the supply/pull sources. They are not modules. You never write module and; the simulator already knows what an AND gate does, down to the four-value truth table that tells it 0 & x is 0 and 1 & x is x.Verilog 中的原型(primitive)是語言內建的邏輯模型:and、or、nand、nor、buf、not、xor、xnor,再加上三態家族 bufif0、bufif1、notif0、notif1,MOS 開關 nmos、pmos、cmos 及其電阻性變體,以及 supply/pull 這類電源。它們不是模組。你永遠不會寫 module and;模擬器本來就知道 AND 閘做什麼,連「0 & x 是 0、1 & x 是 x」這種四值真值表的細節都知道。Verilog のプリミティブとは、言語に組み込まれた論理モデルである。and、or、nand、nor、buf、not、xor、xnor に加え、三状態系の bufif0、bufif1、notif0、notif1、MOS スイッチ nmos、pmos、cmos とその抵抗性の変種、そして supply/pull の電源がある。これらはモジュールではない。module and と書くことは決してない。シミュレータは AND ゲートの振る舞いを、0 & x が 0、1 & x が x という四値真理値表のレベルまで、はじめから知っている。

Three structural facts about primitives govern everything that follows. First, a primitive has exactly one output, and it is always the first port. Second, it may have any number of inputs: and (d, a, b, c) is a three-input AND whose output is d. Third, a primitive instance is not a module instance — it has no named port connections, no parameters, and its instance name is optional. Writing and (z1, x1, y1); with no name at all is perfectly legal, which is why gate-level netlists produced by synthesis tools are a wall of unnamed primitives.關於原型,有三項結構性的事實支配了接下來的一切。第一,原型恰好只有一個輸出,而且一定是第一個埠。第二,輸入的個數不限:and (d, a, b, c) 是一個輸出為 d 的三輸入 AND。第三,原型實體不是模組實體 — 它沒有具名埠連接、沒有參數,實體名稱也可以省略。寫成 and (z1, x1, y1); 完全不給名字是完全合法的,這也是為什麼合成工具產出的閘層級網表是一整片沒有名字的原型。プリミティブについては、以後のすべてを左右する三つの構造的事実がある。第一に、プリミティブは出力をちょうど一つだけ持ち、それは常に先頭のポートである。第二に、入力はいくつあってもよい。and (d, a, b, c) は出力が d の三入力 AND である。第三に、プリミティブのインスタンスはモジュールのインスタンスではない。名前付きポート接続もパラメータもなく、インスタンス名は省略できる。and (z1, x1, y1); と名前なしで書いても完全に合法であり、だからこそ合成ツールが出力するゲートレベルネットリストは、名前のないプリミティブの壁になる。

Why does the one-output-first rule matter so much? Because the table in a user-defined primitive has no column headings. The simulator aligns table columns to ports positionally, and it knows the last column is the output precisely because the output is pinned to port position zero. If the language allowed outputs anywhere in the port list, a truth table would be unreadable without a schema.為什麼「輸出必須排第一」這條規則如此關鍵?因為使用者自訂原型裡的表格沒有欄位標題。模擬器是依位置把表格的欄對齊到埠的,而它之所以知道最後一欄是輸出,正是因為輸出被釘死在埠位置零。如果語言允許輸出出現在埠列表的任何位置,真值表少了綱要就無法閱讀。なぜ「出力が先頭」という規則がこれほど重要なのか。ユーザ定義プリミティブのテーブルには列見出しがないからである。シミュレータはテーブルの列を位置によってポートに対応づけており、最後の列が出力だと分かるのは、出力がポート位置ゼロに固定されているからにほかならない。もし出力をポートリストのどこにでも置けるなら、真理値表はスキーマなしには読めなくなる。

1.2 Why user-defined primitives were invented1.2 為什麼會有使用者自訂原型1.2 なぜユーザ定義プリミティブが生まれたか

The built-in set is small and strictly combinational. It contains no latch, no flip-flop, no multiplexer, no AOI (and-or-invert) cell. Yet those are exactly the cells a real library contains, and a library model has to be fast: a gate-level netlist of a million instances is simulated event by event, and every instance evaluation costs time. A behavioural always block that recomputes an expression is an order of magnitude more expensive than a table lookup.內建的那一組數量很少,而且嚴格說來都是組合邏輯。裡面沒有閂鎖器、沒有正反器、沒有多工器,也沒有 AOI(and-or-invert)單元。然而真實單元庫裡有的正是這些單元,而且單元庫的模型必須快:一個百萬實體的閘層級網表是逐事件模擬的,每一次實體求值都要花時間。一個會重算表示式的行為式 always 區塊,成本比查表高一個數量級。組み込みの一式は数が少なく、しかも厳密に組み合わせ回路だけである。ラッチもフリップフロップもマルチプレクサも、AOI(and-or-invert)セルもない。ところが実際のライブラリが持つのはまさにそうしたセルであり、しかもライブラリのモデルは速くなければならない。百万インスタンスのゲートレベルネットリストはイベントごとにシミュレートされ、インスタンスの評価一回ごとに時間がかかる。式を再計算する動作記述の always ブロックは、テーブル引きより一桁高価である。

So the language offers primitive ... endprimitive: you write a truth table, and the simulator compiles it into a lookup structure that evaluates in roughly the cost of an array index. The slides give the motivation in one line — "predefined primitives are limited" — and that is true, but the performance argument is the reason UDPs survived into the era when everybody else moved to behavioural modelling.所以語言提供了 primitive ... endprimitive:你寫一張真值表,模擬器把它編譯成一個查表結構,求值的成本大約就是一次陣列索引。講義用一句話給出動機 —「預先定義的原型種類有限」— 這沒錯,但效能上的理由才是 UDP 能在大家都轉向行為式建模的年代存活下來的原因。そこで言語は primitive ... endprimitive を用意した。真理値表を書くと、シミュレータはそれを配列添字一回ぶん程度のコストで評価できるルックアップ構造にコンパイルする。講義スライドは動機を一行で述べている — 「定義済みプリミティブは限られている」— それは正しいが、他の誰もが動作記述へ移った時代に UDP が生き残った理由は、性能のほうである。

The mental model心智模型考え方のモデル

A UDP is not a tiny module. It is a compiled truth table with an evaluation rule. There is no procedural code inside it, no expressions, no temporaries, no hierarchy. Everything it can ever do is written out as rows, and the only question the simulator ever asks is: which row matches right now?UDP 不是一個小模組。它是一張附帶求值規則、已編譯好的真值表。它裡面沒有程序式程式碼、沒有表示式、沒有暫存變數、沒有階層。它能做的一切都被攤平寫成一列一列,而模擬器唯一會問的問題就是:現在對應到哪一列?UDP は小さなモジュールではない。評価規則を伴う、コンパイル済みの真理値表である。手続き的なコードも式も一時変数も階層も、その内部には存在しない。できることはすべて行として書き出されており、シミュレータが問う唯一の問いは「いまどの行が一致するか」である。

1.3 Rules shared by built-in and user-defined primitives1.3 內建原型與使用者自訂原型共通的規則1.3 組み込みプリミティブとユーザ定義プリミティブに共通の規則

The lecture lists these as a flat bullet list. They are worth separating into the ones that are syntactic and the ones that bite you at run time.講義把這些列成一串平鋪的項目。值得把它們分成「屬於語法的」和「會在執行期咬你一口的」兩類。講義ではこれらが平坦な箇条書きとして並んでいる。構文上の規則と、実行時に牙を剥く規則とに分けて見ておく価値がある。

Rule規則規則Why it exists / what breaks為什麼有這條規則/違反時壞在哪なぜ存在するか/破ると何が壊れるか
Never stand-alone; must be instantiated inside a module不能獨立存在;一定要在模組內被實體化単独では存在できない。モジュール内でインスタンス化しなければならないA primitive has no hierarchy of its own. A UDP declared at file scope is a definition; nothing simulates until some module instantiates it.原型本身沒有階層。在檔案範圍宣告的 UDP 只是一個定義;在某個模組把它實體化之前,什麼都不會被模擬。プリミティブ自身は階層を持たない。ファイルスコープで宣言された UDP は定義にすぎず、どこかのモジュールがインスタンス化するまで何もシミュレートされない。
Instance name optional實體名稱可以省略インスタンス名は省略可能Convenient for machine-generated netlists. The cost is that an unnamed instance cannot be reached by a hierarchical path, so you cannot force or probe it from a test bench.這對機器產生的網表很方便。代價是沒有名字的實體無法用階層路徑指到,所以你沒辦法從測試平台 force 或探測它。機械生成のネットリストには都合がよい。代償として、名前のないインスタンスは階層パスで到達できないため、テストベンチから force したりプローブしたりできない。
Default delay is zero預設延遲為零既定の遅延はゼロWith no #, the output changes in the same time step as the input. This is what makes zero-delay RTL-ish gate simulation possible — and what makes a combinational loop of primitives hang the simulator.沒有寫 # 時,輸出與輸入在同一個時間步變化。這讓接近 RTL 風格的零延遲閘層級模擬成為可能 — 也讓一圈原型組成的組合迴路把模擬器掛死。# を書かなければ、出力は入力と同じ時間ステップで変化する。これが RTL 的なゼロ遅延ゲートシミュレーションを可能にし、同時にプリミティブの組み合わせループがシミュレータをハングさせる原因にもなる。
Port order: output first, then inputs埠的順序:輸出在前,輸入在後ポートの順序:出力が先、入力が後Positional only. Swap two inputs of a UDP at instantiation and you get a silently wrong truth table, not a compile error, unless the widths differ.純粹依位置對應。實體化時把 UDP 的兩個輸入接反,你得到的是一張默默錯掉的真值表,而不是編譯錯誤 — 除非位寬剛好不同。対応づけは位置のみによる。インスタンス化時に UDP の入力を二つ取り違えても、得られるのは黙って誤った真理値表であって、コンパイルエラーではない(幅が違う場合を除く)。
No inout不可以有 inoutinout は不可A UDP is a one-directional evaluation. Bidirectional behaviour needs a switch-level primitive (tran, tranif1) or a module.UDP 是單向求值。雙向的行為需要開關層級原型(tran、tranif1)或一個模組來做。UDP は一方向の評価である。双方向の振る舞いにはスイッチレベルプリミティブ(tran、tranif1)かモジュールが必要になる。
and          (z1, x1, y1);   // built-in, unnamed
and    M1    (z2, x2, y2);   // built-in, named
my_udp       (c2, a2, b2);   // user-defined, unnamed
my_udp N1    (c1, a1, b1);   // user-defined, named
my_udp #3 N2 (c3, a3, b3);   // user-defined with a delay: delay comes before the name

Note the position of the delay in the last line. For a primitive instance the syntax is <keyword> <delay> <name> (ports). Students routinely write my_udp N2 #3 (...) and get a parse error whose message mentions neither delays nor names.注意最後一行裡延遲的位置。原型實體的語法是 <關鍵字> <延遲> <名稱> (埠)。學生常常寫成 my_udp N2 #3 (...),然後得到一個訊息裡既沒提延遲也沒提名稱的語法錯誤。最後の行における遅延の位置に注意すること。プリミティブインスタンスの構文は <キーワード> <遅延> <名前> (ポート) である。学生はしばしば my_udp N2 #3 (...) と書き、遅延にも名前にも言及しないメッセージの構文エラーを受け取る。

1.4 Net output for combinational, reg output for sequential1.4 組合邏輯用 net 輸出,循序邏輯用 reg 輸出1.4 組み合わせは net 出力、順序は reg 出力

This is the rule most often quoted and least often explained. A combinational UDP declares its output as a net:這條規則最常被引用,也最少被解釋。組合邏輯 UDP 的輸出宣告為 net:この規則は最もよく引用され、最も説明されない。組み合わせ UDP は出力を net として宣言する。

primitive p_comb (y, a, b);
  output y;          // implicitly a net; no reg declaration
  input  a, b;
  ...
endprimitive

A sequential UDP adds reg y;. The reason is not stylistic. A sequential UDP has state, and in Verilog a value that persists between evaluations has to live in a variable, not in a net. The reg declaration is what creates that storage, and the storage is exactly what the state column of the table reads. If you write a table with n+2 columns but forget reg q;, the compiler reports a column-count mismatch, because without the reg there is no state column for the extra field to attach to.循序邏輯 UDP 要加上 reg y;。理由不是風格問題。循序邏輯 UDP 有狀態,而在 Verilog 裡,要在兩次求值之間留住的值必須存在變數中,不能存在 net 上。reg 宣告建立的就是那塊儲存,而那塊儲存正好就是表格狀態欄所讀的東西。如果你寫了 n+2 欄的表卻忘了 reg q;,編譯器會報欄數不符,因為沒有 reg 就沒有狀態欄可以讓多出來的那一欄掛上去。順序 UDP には reg y; が加わる。理由は様式の問題ではない。順序 UDP は状態を持ち、Verilog では評価と評価のあいだ保持される値は net ではなく変数に置かれなければならない。その記憶を作るのが reg 宣言であり、その記憶こそテーブルの状態列が読む対象である。n+2 列のテーブルを書きながら reg q; を忘れると、コンパイラは列数の不一致を報告する。reg がなければ、余分な列を結びつける状態列が存在しないからである。

Conversely, if you declare reg y; on a UDP whose table has only n+1 columns, some simulators accept it and treat the UDP as sequential with an invisible state. That is a portability landmine. Declare the reg if and only if the table has a state field.反過來說,如果你在一個表只有 n+1 欄的 UDP 上宣告了 reg y;,有些模擬器會照單全收,把這個 UDP 當成帶有看不見的狀態的循序邏輯。那是一顆可攜性地雷。唯有表中確實有狀態欄時,才宣告 reg。逆に、テーブルが n+1 列しかない UDP に reg y; を宣言すると、それを受け入れて見えない状態を持つ順序 UDP として扱うシミュレータもある。これは移植性の地雷である。reg を宣言するのは、テーブルに状態フィールドがあるとき、かつそのときに限る。

Output values a UDP can produceUDP 能產生的輸出值UDP が出せる出力値

A UDP output can be 0, 1 or x — never z. There is no three-state UDP. If you need a cell that drives high impedance, you must build it from a UDP plus a built-in bufif1/notif1, or model it with a continuous assignment. This is why library three-state buffers are modelled with built-in primitives and not with UDPs.UDP 的輸出可以是 0、1 或 x — 永遠不會是 z。沒有三態 UDP 這種東西。如果你需要一顆會驅動高阻抗的單元,必須用一個 UDP 加上內建的 bufif1/notif1 來組,或改用連續指定來描述。這也是為什麼單元庫裡的三態緩衝器是用內建原型建模,而不是用 UDP。UDP の出力は 0、1、x のいずれかであり、z にはならない。三状態 UDP というものは存在しない。高インピーダンスを駆動するセルが必要なら、UDP に組み込みの bufif1/notif1 を組み合わせるか、連続代入でモデル化するしかない。ライブラリの三状態バッファが UDP ではなく組み込みプリミティブでモデル化されているのは、このためである。

There are also size limits that the slides do not mention but which the standard imposes and real simulators enforce: a combinational UDP may have at most 10 inputs, and a sequential UDP at most 9 (the state column consumes one of the ten table fields the implementation reserves). A table for ten inputs has up to $3^{10} = 59049$ expanded rows, which is already a large compiled object; the limit is an engineering compromise, not a mathematical one. If you need a wider function, decompose it into two UDPs or write a module.還有一些講義沒提、但標準有規定且真實模擬器會強制的大小上限:組合邏輯 UDP 最多 10 個輸入,循序邏輯 UDP 最多 9 個(狀態欄用掉了實作保留的十個表格欄位之一)。十個輸入的表展開後最多有 $3^{10} = 59049$ 列,編譯出來已經是個很大的物件;這個上限是工程上的折衷,不是數學上的必然。如果你需要更寬的函數,就把它拆成兩個 UDP,或乾脆寫一個模組。講義には出てこないが、標準が課し実際のシミュレータも強制するサイズ上限もある。組み合わせ UDP は入力が最大 10 個、順序 UDP は最大 9 個である(状態列が、実装の予約する十個のテーブルフィールドのうち一つを消費する)。入力十個のテーブルは展開すると最大 $3^{10} = 59049$ 行になり、コンパイル結果はすでに巨大なオブジェクトである。この上限は数学的必然ではなく工学上の妥協である。より幅の広い関数が必要なら、二つの UDP に分解するかモジュールを書けばよい。

1.5 UDPs cannot be synthesised — and that is the point1.5 UDP 不能被合成 — 而這正是重點1.5 UDP は合成できない — そしてそれこそが要点である

No synthesis tool reads a UDP table and infers logic from it. Not synthesisable Ask a synthesiser to compile a design containing primitive and it will either error out or, worse, treat the instance as a black box with unknown function and optimise around it. This surprises students who reason, correctly, that a truth table is the most synthesisable thing imaginable.沒有任何合成工具會去讀 UDP 的表並從中推論出邏輯。不可合成 叫合成器去編譯一個含有 primitive 的設計,它要嘛直接報錯,要嘛更糟 — 把那個實體當成功能未知的黑盒子,然後繞著它最佳化。這會讓學生很意外,因為他們的推理其實沒錯:真值表聽起來是世界上最適合合成的東西。UDP のテーブルを読んで論理を推論する合成ツールは存在しない。合成不可 primitive を含む設計を合成器にかければ、エラーで止まるか、さらに悪いことに、そのインスタンスを機能不明のブラックボックスとして扱い、その周囲を最適化してしまう。真理値表こそ最も合成しやすいものではないかという、それ自体は正しい推論をする学生にとって、これは意外な事実である。

The resolution is that UDPs live on the other side of the flow. Synthesis goes from RTL to a netlist of library cells. The library cells themselves are not synthesised — they were designed transistor by transistor, characterised with SPICE, and shipped. The vendor needs two views of each cell: a timing view (the .lib / Liberty file, which the synthesiser and the static timing analyser read) and a simulation view (the Verilog .v model, which the event simulator reads). The UDP is the functional core of the simulation view.答案是:UDP 住在流程的另一側。合成是從 RTL 到單元庫單元網表。單元庫裡的單元本身並不是合成出來的 — 它們是一顆一顆電晶體畫出來、用 SPICE 特性化、然後出貨的。供應商必須為每顆單元提供兩種視圖:時序視圖(.lib/Liberty 檔,由合成器與靜態時序分析器讀取)與模擬視圖(Verilog .v 模型,由事件模擬器讀取)。UDP 就是模擬視圖的功能核心。答えは、UDP がフローの反対側に住んでいるということである。合成は RTL からライブラリセルのネットリストへ向かう。ライブラリセル自体は合成されたものではない。トランジスタ一つずつ設計され、SPICE で特性化され、出荷されたものである。ベンダは各セルについて二つのビューを用意する必要がある。タイミングビュー(.lib/Liberty ファイル。合成器と静的タイミング解析器が読む)とシミュレーションビュー(Verilog .v モデル。イベントシミュレータが読む)である。UDP はシミュレーションビューの機能的中核にあたる。

// Shape of a real standard-cell simulation model (abridged)
`celldefine
`timescale 1ns/10ps

primitive UDP_DFF (q, clk, d, notifier);
  output q;  input clk, d, notifier;  reg q;
  table
    r 0 ? : ? : 0;
    r 1 ? : ? : 1;
    f ? ? : ? : -;
    ? * ? : ? : -;
    ? ? * : ? : x;      // timing violation forces x
  endtable
endprimitive

module DFFHQX1 (Q, D, CK);
  output Q;  input D, CK;
  UDP_DFF u0 (Q, CK, D, notifier);
  specify
    (posedge CK => (Q : D)) = (0.142, 0.158);
    $setup (D, posedge CK, 0.071, notifier);
    $hold  (posedge CK, D, 0.034, notifier);
  endspecify
endmodule
`endcelldefine

Everything in that listing is Lecture 3 material: the UDP table, the edge-sensitive module path, the timing checks, the `celldefine wrapper and the `timescale. The notifier argument is the mechanism by which a setup or hold violation reaches the functional model — the timing check toggles notifier, the last table row sees a change on it, and the flip-flop output goes to x. That is how a post-layout simulation turns a timing failure into visible corruption instead of silently passing.那段程式碼裡的每一樣東西都是第 3 講的教材:UDP 表、邊緣敏感的模組路徑、時序檢查、`celldefine 包裝與 `timescale。notifier 引數則是 setup 或 hold 違規傳達到功能模型的機制 — 時序檢查把 notifier 翻轉,表格最後一列看到它變了,正反器的輸出就變成 x。佈局後模擬就是這樣把一次時序失效變成看得見的資料毀損,而不是默默地通過。そのリストに現れるものはすべて第 3 講の内容である。UDP テーブル、エッジ感応のモジュールパス、タイミングチェック、`celldefine の包み、そして `timescale。notifier 引数は、セットアップやホールドの違反が機能モデルに届く仕組みである。タイミングチェックが notifier をトグルし、テーブルの最終行がその変化を見て、フリップフロップの出力が x になる。レイアウト後シミュレーションがタイミング違反を、黙って通過させるのではなく目に見える破壊として示すのは、この経路による。

What to take from this section這一節該帶走什麼この節から持ち帰るもの

UDPs are not a modelling style you will choose for your own designs. They are a format you must be able to read, because the models that decide whether your chip works in gate-level simulation are written in it. The exam asks you to evaluate a table; the job asks you to debug one.UDP 不是你會為自己的設計選用的建模風格。它是一種你必須看得懂的格式,因為決定你的晶片在閘層級模擬中會不會動的那些模型,就是用它寫的。考試要你求一張表的值;工作則要你除錯一張表。UDP は自分の設計のために選ぶモデリングスタイルではない。読めなければならない形式である。ゲートレベルシミュレーションでチップが動くかどうかを決めるモデルが、まさにこの形式で書かれているからだ。試験はテーブルを評価せよと求め、仕事はテーブルをデバッグせよと求める。

2. Inside the UDP Table: How a Row Is Matched2. UDP 表格的內部:一列是怎麼被對應到的2. UDP テーブルの内側:行はどう照合されるか

The single most useful thing you can learn about UDPs is the matching algorithm, because every exam question and every real debugging session reduces to running it in your head. The lecture states the rules in scattered bullets; here they are as one procedure.關於 UDP,最有用的一件事就是學會匹配演算法,因為每一道考題、每一次真實的除錯都可以化約成在腦中跑一遍這個演算法。講義把規則散在各個項目裡;這裡把它們寫成單一的程序。UDP について学べる最も有用なことは照合アルゴリズムである。あらゆる試験問題も、あらゆる実際のデバッグも、これを頭の中で実行することに帰着するからだ。講義では規則が箇条書きに散らばっているが、ここでは一つの手順としてまとめる。

2.1 The symbol alphabet2.1 符號字母表2.1 記号のアルファベット

Symbol符號記号Stands for代表的意義意味するものLegal in可用於使える場所
0 1 xThe literal logic values字面上的邏輯值リテラルの論理値Input fields, state field, output field輸入欄、狀態欄、輸出欄入力フィールド、状態フィールド、出力フィールド
?Any of {0, 1, x} — a level don't-care{0, 1, x} 之中任一 — 準位上的不在意(don't-care){0, 1, x} のいずれか — レベルのドントケアInput fields, state field. Never the output field.輸入欄、狀態欄。絕不可用於輸出欄。入力フィールドと状態フィールド。出力フィールドには決して使えない。
bAny of {0, 1} — known values only{0, 1} 之中任一 — 只含已知值{0, 1} のいずれか — 既知の値のみInput fields, state field. Not the output.輸入欄、狀態欄。不可用於輸出。入力フィールドと状態フィールド。出力には使えない。
(vw)The input changed from v to w; v and w may themselves be 0, 1, x, ? or b該輸入由 v 變成 w;v 與 w 本身可以是 0、1、x、? 或 bその入力が v から w へ変化したこと。v と w 自体は 0、1、x、?、b のいずれでもよいInput fields of a sequential UDP only只能用於循序邏輯 UDP 的輸入欄順序 UDP の入力フィールドのみ
*Shorthand for (??) — any change at all(??) 的簡寫 — 任何變化都算(??) の短縮形 — あらゆる変化Input fields, sequential only輸入欄,只限循序邏輯入力フィールド、順序 UDP のみ
r(01), a rising edge(01),上升緣(01)、立ち上がりエッジInput fields, sequential only輸入欄,只限循序邏輯入力フィールド、順序 UDP のみ
f(10), a falling edge(10),下降緣(10)、立ち下がりエッジInput fields, sequential only輸入欄,只限循序邏輯入力フィールド、順序 UDP のみ
pAny "positive" edge: (01), (0x), (x1)任何「正向」邊緣:(01)、(0x)、(x1)あらゆる「正」のエッジ:(01)、(0x)、(x1)Input fields, sequential only輸入欄,只限循序邏輯入力フィールド、順序 UDP のみ
nAny "negative" edge: (10), (1x), (x0)任何「負向」邊緣:(10)、(1x)、(x0)あらゆる「負」のエッジ:(10)、(1x)、(x0)Input fields, sequential only輸入欄,只限循序邏輯入力フィールド、順序 UDP のみ
-No change — hold the current state不變 — 維持目前的狀態変化なし — 現在の状態を保持するOutput field of a sequential UDP only只能用於循序邏輯 UDP 的輸出欄順序 UDP の出力フィールドのみ

Two of these deserve emphasis because the slides list them without comment. p and n are the pessimism-tolerant edges: they fire not only on a clean 0→1 but also on 0→x and x→1. Library flip-flop models use r and f when they want to ignore unknown clock edges and p/n when they want to propagate the uncertainty. The choice is a deliberate optimism/pessimism trade-off, and getting it wrong is how a gate-level simulation either hides a real reset problem or drowns in spurious x.其中有兩個值得特別強調,因為講義只是列出來而沒有說明。p 與 n 是容忍悲觀的邊緣:它們不只在乾淨的 0→1 時觸發,0→x 與 x→1 也會觸發。單元庫的正反器模型在想忽略未知時脈邊緣時用 r 與 f,在想把不確定性傳下去時用 p/n。這個選擇是刻意的樂觀/悲觀取捨,選錯了,閘層級模擬要嘛藏住一個真實的重置問題,要嘛被一堆假的 x 淹沒。このうち二つは、講義が注釈なしに並べているだけなので強調しておく価値がある。p と n は悲観性に寛容なエッジである。きれいな 0→1 だけでなく、0→x や x→1 でも発火する。ライブラリのフリップフロップモデルは、不定のクロックエッジを無視したいときに r と f を、不確かさを伝えたいときに p/n を使う。これは意図的な楽観/悲観のトレードオフであり、選択を誤れば、ゲートレベルシミュレーションは実在するリセットの問題を隠すか、偽の x に溺れるかのどちらかになる。

And - is not a don't-care. It is an instruction: do not schedule anything; leave the output where it is. Putting - in an input field is a syntax error. Putting ? in an output field is a syntax error. Students mix these up constantly, because in Karnaugh-map notation both symbols mean "don't care".而 - 並不是不在意。它是一道指令:什麼都不要排程,讓輸出保持原狀。把 - 放進輸入欄是語法錯誤。把 ? 放進輸出欄也是語法錯誤。學生常常把這兩個搞混,因為在卡諾圖的記法裡兩個符號都是「不在意」。一方 - はドントケアではない。これは命令である。何もスケジュールするな、出力をそのままにしておけ、という意味だ。- を入力フィールドに置くのは構文エラーであり、? を出力フィールドに置くのも構文エラーである。カルノー図の記法ではどちらの記号も「ドントケア」を意味するため、学生は絶えずこの二つを混同する。

There is no z in a UDP tableUDP 表裡沒有 zUDP テーブルに z は存在しない

The symbol z is simply not part of the table alphabet. If an input net carries z at run time, the simulator substitutes x before matching. This has a practical consequence: a UDP driven by an un-driven net or by a disabled three-state bus behaves exactly as if that input were unknown, and the output almost always goes to x. When you see a gate-level simulation go x immediately after reset release, a floating three-state bus feeding a UDP input is the first thing to check.符號 z 根本不屬於表格的字母表。如果某個輸入 net 在執行期帶著 z,模擬器會在比對之前先把它換成 x。這有個實際後果:由一條沒有驅動源的 net、或由一條被停用的三態匯流排所驅動的 UDP,其行為完全等同於該輸入是未知值,而輸出幾乎一定變成 x。當你看到閘層級模擬在重置一放開就變 x,第一個該檢查的就是有沒有一條浮接的三態匯流排接到 UDP 的輸入。記号 z はテーブルのアルファベットに含まれていない。実行時に入力 net が z を運んでいれば、シミュレータは照合の前にそれを x に置き換える。これには実際的な帰結がある。駆動されていない net や無効化された三状態バスに駆動された UDP は、その入力が不定であるのとまったく同じように振る舞い、出力はほぼ必ず x になる。リセット解除直後にゲートレベルシミュレーションが x になったら、まず疑うべきは UDP 入力に入っている浮いた三状態バスである。

2.2 The matching procedure2.2 匹配的程序2.2 照合の手順

When any input of a UDP instance changes value, the simulator does the following.當某個 UDP 實體的任一輸入改變值時,模擬器會做下面的事。UDP インスタンスのいずれかの入力が値を変えると、シミュレータは次のことを行う。

  1. Freeze the picture. It records the new value of every input, the previous value of every input, and (for a sequential UDP) the current value of the output reg, which is the state.先把畫面定格。它記下每個輸入的新值、每個輸入的前一個值,以及(對循序邏輯 UDP 而言)輸出 reg 目前的值,也就是狀態。画面を静止させる。各入力の新しい値、各入力の直前の値、そして(順序 UDP であれば)出力 reg の現在値、すなわち状態を記録する。
  2. Scan the rows in order. For each row it tests every field. A level field matches if the new input value is in the set the symbol denotes. An edge field matches if the pair (previous value, new value) is in the set the edge symbol denotes. The state field matches if the current output value is in the set the symbol denotes.依序掃描每一列。對每一列檢查所有欄位。準位欄位在新的輸入值屬於該符號所代表的集合時成立。邊緣欄位在(前值, 新值)這個序對屬於該邊緣符號所代表的集合時成立。狀態欄位在目前的輸出值屬於該符號所代表的集合時成立。行を順に走査する。各行についてすべてのフィールドを検査する。レベルフィールドは、新しい入力値がその記号の表す集合に属すれば一致する。エッジフィールドは、(旧値, 新値)の順序対がそのエッジ記号の表す集合に属すれば一致する。状態フィールドは、現在の出力値がその記号の表す集合に属すれば一致する。
  3. Apply the first match. The output field of the matching row gives the new output: a literal 0, 1 or x, or - meaning hold.套用第一個對應到的列。該列的輸出欄給出新的輸出:字面值 0、1 或 x,或者表示維持的 -。最初に一致した行を適用する。その行の出力フィールドが新しい出力を与える。リテラルの 0、1、x、あるいは保持を意味する - である。
  4. If no row matches, drive x. Unconditionally. No warning, no error.如果沒有任何一列對應,就驅動 x。無條件如此。沒有警告,也沒有錯誤。どの行も一致しなければ x を駆動する。無条件に、である。警告もエラーも出ない。

Step 2 hides the rule that catches people out: only one input may have changed. Verilog processes events one at a time. If two inputs of a UDP are assigned in the same time step, the simulator evaluates the UDP twice, once for each change, in an order the standard does not define. A row whose fields require two simultaneous edges can therefore never match. The standard enforces this by forbidding more than one edge specifier per row.第 2 步藏著一條常常絆倒人的規則:同一時刻只能有一個輸入發生變化。Verilog 一次處理一個事件。如果 UDP 的兩個輸入在同一個時間步被指定,模擬器會把這個 UDP 求值兩次,每次對應一個變化,而順序是標準沒有定義的。因此,一列如果要求兩個邊緣同時發生,就永遠不可能對應到。標準透過「每一列最多只能有一個邊緣指定子」來強制這一點。手順 2 には、人をつまずかせる規則が隠れている。変化した入力は一つだけでなければならない。Verilog はイベントを一度に一つずつ処理する。UDP の二つの入力が同じ時間ステップで代入されると、シミュレータはその UDP を変化ごとに一度ずつ、計二回評価する。その順序は標準が定めていない。したがって、二つのエッジが同時に起きることを要求する行は決して一致しない。標準は「一行につきエッジ指定子は高々一つ」と定めてこれを強制している。

Row match condition列的匹配條件行の一致条件 $$\text{match}(\text{row}) \;=\; \Big(\bigwedge_{i=1}^{n} \text{field}_i \ni \big(v_i^{-}, v_i^{+}\big)\Big) \;\wedge\; \big(\text{state field} \ni q\big)$$

where $v_i^{-}$ and $v_i^{+}$ are the old and new values of input $i$, a level field tests only $v_i^{+}$, and an edge field tests the ordered pair. At most one field in the row may be an edge field.其中 $v_i^{-}$ 與 $v_i^{+}$ 是輸入 $i$ 的舊值與新值,準位欄位只檢查 $v_i^{+}$,邊緣欄位則檢查這個序對。一列之中最多只能有一個欄位是邊緣欄位。ここで $v_i^{-}$ と $v_i^{+}$ は入力 $i$ の旧値と新値であり、レベルフィールドは $v_i^{+}$ のみを検査し、エッジフィールドは順序対を検査する。一行のうちエッジフィールドになれるのは高々一つである。

2.3 What "anything unspecified is x" really costs2.3 「沒列到的一律是 x」真正的代價2.3 「指定されていないものはすべて x」が本当に要求する代償

The slide says "Anything unspecified will be treated as x" in one line. That line is the single biggest source of mysterious x in gate-level simulation, and it deserves unpacking.投影片用一行寫著「任何未指定的情況都會被當成 x」。那一行是閘層級模擬中神祕 x 的最大單一來源,值得攤開來講。スライドには「指定されていないものはすべて x として扱われる」と一行だけ書かれている。その一行が、ゲートレベルシミュレーションにおける謎の x の最大の供給源であり、展開して説明するに値する。

A combinational UDP with three inputs has $3^3 = 27$ possible input combinations once you include x. A table that covers only the eight all-known combinations leaves 19 uncovered. Every one of those 19 drives the output to x. That is not a bug in the simulator; it is the model telling you "I was never characterised for this case". But the practical effect is that a single unknown anywhere in a gate-level netlist propagates forward through every UDP it touches, and because an x output feeds the next cell's input, the unknown region grows monotonically. One uninitialised flip-flop can turn an entire datapath red within a few clock cycles.三輸入的組合邏輯 UDP,把 x 算進去之後共有 $3^3 = 27$ 種可能的輸入組合。只涵蓋八種全為已知值組合的表,留下了 19 種沒有涵蓋。那 19 種每一種都會把輸出驅動成 x。這不是模擬器的錯;這是模型在告訴你「我從來沒有為這個情況做過特性化」。但實務上的效果是:閘層級網表中任何一處的單一未知值都會往前傳過它碰到的每一個 UDP,而且因為 x 的輸出會餵進下一顆單元的輸入,未知的區域只會單調擴大。一顆沒有初始化的正反器,可以在幾個時脈週期內把整條資料路徑染紅。三入力の組み合わせ UDP は、x を含めると入力の組み合わせが $3^3 = 27$ 通りある。すべて既知の八通りだけを覆うテーブルは、19 通りを覆い残す。その 19 通りはいずれも出力を x に駆動する。これはシミュレータの不具合ではなく、「この場合については特性化されていない」とモデルが告げているのである。しかし実際の効果はこうだ。ゲートレベルネットリストのどこか一か所の不定値が、通過するすべての UDP を経て前方へ伝わり、x の出力が次のセルの入力になるため、不定領域は単調に拡大する。初期化されていないフリップフロップ一つが、数クロックのうちにデータパス全体を赤く染めることがある。

The defence is to write rows that recognise the cases where the output is determined despite an unknown input. An AND gate with one input at 0 outputs 0 no matter what the other input is, even if it is x. A table that does not say so is pessimistic: it reports less knowledge than the circuit actually has. Section 3.3 works through the multiplexer case in detail.對策是補上那些「即使有未知輸入、輸出仍然確定」的列。AND 閘只要有一個輸入是 0,不論另一個輸入是什麼,輸出就是 0,就算另一個是 x 也一樣。沒有把這件事寫出來的表就是悲觀的:它回報的知識比電路實際具有的少。3.3 節會詳細走一遍多工器的例子。対策は、入力が不定であっても出力が決まる場合を認識する行を書くことである。AND ゲートは入力の一方が 0 なら、もう一方が何であっても — たとえ x でも — 出力は 0 である。それを書いていないテーブルは悲観的であり、回路が実際に持っている知識より少ない知識しか報告しない。3.3 節でマルチプレクサの例を詳しく追う。

Pessimism vs optimism, and why both are dangerous悲觀與樂觀,以及為什麼兩者都危險悲観と楽観、そしてなぜどちらも危険か
  • Pessimistic models produce x where silicon would produce a definite value. Symptom: a simulation that never comes out of reset, or a checker that fires on a signal you can prove is correct. Cost: wasted debugging days, and engineers who start ignoring x.悲觀的模型會在矽晶片其實會給出確定值的地方產生 x。症狀:模擬永遠跳不出重置,或是某個你能證明是對的訊號卻讓檢查器報錯。代價:白白浪費好幾天除錯,以及開始對 x 視而不見的工程師。悲観的なモデルは、シリコンなら確定値を出すところで x を出す。症状:リセットから永遠に抜けないシミュレーション、あるいは正しいと証明できる信号でチェッカが発火する。代償:無駄なデバッグの日々と、x を無視しはじめる技術者。
  • Optimistic models produce a definite value where silicon could go either way. Symptom: a simulation that passes and a chip that does not. Cost: a respin.樂觀的模型會在矽晶片其實可能往兩邊跑的地方給出確定值。症狀:模擬過了,晶片不會動。代價:重新投片。楽観的なモデルは、シリコンならどちらにも転びうるところで確定値を出す。症状:シミュレーションは通るのにチップは動かない。代償:再試作。
  • Library models are deliberately pessimistic on the clock path (an x on the clock gives an x on Q) and deliberately optimistic nowhere. When you write your own UDP, lean the same way.單元庫的模型在時脈路徑上是刻意悲觀的(時脈上的 x 會讓 Q 變成 x),而且刻意在任何地方都不樂觀。你自己寫 UDP 時,請往同一邊靠。ライブラリのモデルはクロック経路では意図的に悲観的であり(クロックの x は Q を x にする)、どこにおいても意図的に楽観的ではない。自分で UDP を書くときも同じ側に倒すこと。

2.4 Expansion arithmetic: how many rows is a shorthand row worth?2.4 展開的算術:一列簡寫抵幾列?2.4 展開の算術:短縮形の一行は何行ぶんか

A row containing k occurrences of ? and m occurrences of b expands to $3^{k}\cdot 2^{m}$ concrete rows. This is the arithmetic the compiler performs when it builds the lookup structure, and it is worth doing by hand once because it tells you immediately whether a table is complete.含有 k 個 ? 與 m 個 b 的一列,會展開成 $3^{k}\cdot 2^{m}$ 個具體的列。這正是編譯器在建立查表結構時所做的算術,而且值得親手算一次,因為它會立刻告訴你一張表是否完整。? を k 個、b を m 個含む行は、$3^{k}\cdot 2^{m}$ 個の具体的な行に展開される。これはコンパイラがルックアップ構造を構築するときに行う計算そのものであり、一度は手で計算してみる価値がある。テーブルが完全かどうかが即座に分かるからだ。

Take the two-input AND as a worked case. The complete combinational table over $\{0,1,x\}$ has nine rows. Now count what a compact table covers:拿兩輸入 AND 當作例題。在 $\{0,1,x\}$ 上的完整組合表有九列。現在來數一數精簡版的表涵蓋了多少:二入力 AND を例題にとる。$\{0,1,x\}$ 上の完全な組み合わせテーブルは九行である。では簡潔なテーブルが何を覆っているか数えてみる。

primitive and_udp (y, a, b);
  output y;
  input  a, b;
  table
  // a b : y
     0 ? : 0;     // expands to 3 rows: 00, 01, 0x
     ? 0 : 0;     // expands to 3 rows: 00, 10, x0   (00 repeats)
     1 1 : 1;     // 1 row
  endtable
endprimitive
Worked count — is and_udp complete?逐項計算 — and_udp 完整嗎?数えてみる — and_udp は完全か
Row 1, 0 ?: $3^1 = 3$ combinations, namely 00, 01, 0x.第 1 列,0 ?:$3^1 = 3$ 種組合,也就是 00、01、0x。第 1 行 0 ?:$3^1 = 3$ 通り、すなわち 00、01、0x。
Row 2, ? 0: $3^1 = 3$ combinations, namely 00, 10, x0. The combination 00 is covered twice, which is legal because both rows give the same output.第 2 列,? 0:$3^1 = 3$ 種組合,也就是 00、10、x0。組合 00 被涵蓋了兩次,這是合法的,因為兩列給出相同的輸出。第 2 行 ? 0:$3^1 = 3$ 通り、すなわち 00、10、x0。00 は二度覆われるが、両方の行が同じ出力を与えるので合法である。
Row 3, 1 1: one combination.第 3 列,1 1:一種組合。第 3 行 1 1:一通り。
Distinct coverage: $\{00, 01, 0x, 10, x0, 11\}$ — six of the nine. The three uncovered are 1x, x1 and xx.相異的涵蓋範圍:$\{00, 01, 0x, 10, x0, 11\}$ — 九種中的六種。沒被涵蓋的三種是 1x、x1 與 xx。重複を除いた被覆:$\{00, 01, 0x, 10, x0, 11\}$ — 九通り中の六通り。覆われていないのは 1x、x1、xx の三通りである。
All three uncovered cases genuinely are unknown: with one input unknown and the other at 1, the AND output really does depend on the unknown. So this table is complete in the useful sense: every combination it omits is a combination whose answer is x anyway. Adding 1 x : x; x 1 : x; x x : x; would change nothing except the compiled size.這三種沒被涵蓋的情況確實都是未知:一個輸入未知、另一個是 1 時,AND 的輸出真的取決於那個未知值。所以這張表在有用的意義上是完整的:它漏掉的每一種組合,答案本來就是 x。加上 1 x : x; x 1 : x; x x : x; 除了讓編譯後的體積變大之外,什麼都不會改變。覆われていない三通りはいずれも本当に不定である。一方の入力が不定でもう一方が 1 なら、AND の出力は確かにその不定値に依存する。したがってこのテーブルは有用な意味で完全である。省かれた組み合わせの答えは、もともとすべて x だからだ。1 x : x; x 1 : x; x x : x; を加えても、コンパイル後のサイズが増えるほかには何も変わらない。

Contrast that with an OR built the same way. If you write only 1 ? : 1; ? 1 : 1; 0 0 : 0; you cover 10, 11, 1x, 01, x1, 00 and leave 0x, x0, xx to default to x — again correct. The pattern generalises: for a monotone gate, listing the controlling value with ? on the other inputs plus the single all-non-controlling row is both minimal and exactly as precise as silicon.拿同樣方式寫成的 OR 來對照。如果你只寫 1 ? : 1; ? 1 : 1; 0 0 : 0;,你涵蓋了 10, 11, 1x, 01, x1, 00,而把 0x、x0、xx 留給預設值 x — 同樣正確。這個模式可以推廣:對單調的閘而言,把控制值配上其他輸入的 ? 列出來,再加上「全部都不是控制值」的那一列,就同時達到最精簡且與矽晶片一樣精確。同じやり方で書いた OR と比べてみる。1 ? : 1; ? 1 : 1; 0 0 : 0; だけを書けば、10, 11, 1x, 01, x1, 00 を覆い、0x、x0、xx は既定の x に委ねられる — これも正しい。このパターンは一般化できる。単調なゲートであれば、制御値を書いて他の入力を ? にした行と、すべてが非制御値である一行とを並べるだけで、最小でありながらシリコンとまったく同じ精度になる。

2.5 Ambiguity, contradiction and row order2.5 歧義、矛盾與列的順序2.5 曖昧さ、矛盾、行の順序

Two rows may overlap. If they agree on the output, nothing happens and the duplication is harmless (as with 00 above). If they disagree, the standard calls the table ambiguous and says the behaviour is undefined; in practice simulators either warn at compile time and take the first row, or warn and produce x. Different tools, different answers — which is why you must never rely on row order in a UDP.兩列可能重疊。如果它們的輸出一致,什麼事都不會發生,重複是無害的(就像前面的 00)。如果它們不一致,標準稱這張表是有歧義的,並說行為未定義;實務上模擬器要嘛在編譯期警告並採用第一列,要嘛警告並產生 x。不同工具、不同答案 — 這就是為什麼你絕不能在 UDP 裡依賴列的順序。二つの行が重なることがある。出力が一致していれば何も起こらず、重複は無害である(先ほどの 00 のように)。一致していなければ、標準はそのテーブルを曖昧と呼び、動作は未定義だと述べる。実際には、シミュレータはコンパイル時に警告して最初の行を採るか、警告して x を出すかのいずれかである。ツールが違えば答えも違う — だからこそ UDP では行の順序に頼ってはならない。

UDP rows have no priorityUDP 的列沒有優先順序UDP の行に優先順位はない

This is the opposite of a casez or an if/else if chain, where the first match wins by definition and overlapping patterns are a standard idiom. In a UDP you must make the rows mutually exclusive by construction. The usual technique is to qualify every row with the dominant condition: in a flip-flop with asynchronous clear, every clock row carries clr_n = 1 in its field so that it simply cannot match while the clear is asserted. See Practice 5.這與 casez 或 if/else if 鏈正好相反:在那裡,第一個對應到的依定義就贏,重疊的樣式是標準慣用法。在 UDP 裡,你必須讓各列在建構上就互斥。常用的技巧是替每一列加上主控條件:在附非同步清除的正反器中,每一列時脈列的欄位裡都帶著 clr_n = 1,這樣清除被啟動時那些列根本不可能對應到。參見練習 5。これは casez や if/else if の連鎖とは正反対である。そこでは定義上、最初に一致したものが勝ち、パターンの重なりは標準的な書き方である。UDP では、行を構成の段階で相互排他にしなければならない。よく使われる手法は、各行を支配的な条件で限定することだ。非同期クリア付きフリップフロップでは、クロックの行すべてのフィールドに clr_n = 1 を置いておけば、クリアがアサートされている間それらの行は決して一致しない。演習 5 を参照。

One more subtlety. In a sequential UDP, a row with no edge specifier anywhere is a level-sensitive row, and the standard gives level-sensitive rows precedence over edge-sensitive rows when both match. This is a genuine priority rule, and it is the one exception to "rows have no priority". It exists so that asynchronous set/clear rows, which are level-sensitive, override the clock rows without you having to enumerate the interaction. Section 4.6 returns to it.還有一個細節。在循序邏輯 UDP 裡,一整列都沒有邊緣指定子的列是準位敏感列,而標準規定:當準位敏感列與邊緣敏感列同時對應時,準位敏感列優先。這是一條真正的優先權規則,也是「列沒有優先順序」的唯一例外。它存在的目的,是讓屬於準位敏感的非同步設定/清除列能夠壓過時脈列,而不必由你去逐一列舉兩者的交互作用。4.6 節會再回到這一點。もう一つ微妙な点がある。順序 UDP において、どこにもエッジ指定子のない行はレベル感応の行であり、標準は、両方が一致する場合にレベル感応の行がエッジ感応の行に優先すると定めている。これは本物の優先規則であり、「行に優先順位はない」の唯一の例外である。レベル感応である非同期セット/クリアの行が、相互作用をいちいち列挙しなくてもクロックの行を上書きできるように存在している。4.6 節でこれに戻る。

Practice 1 — Expand the shorthand練習 1 — 展開簡寫演習 1 — 短縮形を展開する

Problem: (a) How many concrete input combinations does the row ? b 1 : 0; cover in a three-input combinational UDP? (b) A four-input combinational UDP has a table of six rows, each containing exactly two ? and no b, with no overlaps. What fraction of the $3^4$ combination space is covered, and what is the output everywhere else? (c) Is the row r ? : ? : -; legal in a two-input combinational UDP? (d) Is 1 0 : ? ; legal anywhere?題目:(a) 在一個三輸入的組合邏輯 UDP 中,? b 1 : 0; 這一列涵蓋多少個具體的輸入組合? (b) 某個四輸入組合邏輯 UDP 的表有六列,每列恰好含兩個 ?、沒有 b,且彼此不重疊。$3^4$ 的組合空間被涵蓋了多少比例?其餘各處的輸出是什麼? (c) 在一個兩輸入的組合邏輯 UDP 中,r ? : ? : -; 這一列合法嗎? (d) 1 0 : ? ; 在任何地方合法嗎?問題:(a) 三入力の組み合わせ UDP において、? b 1 : 0; の行は具体的な入力組み合わせを何通り覆うか。(b) ある四入力の組み合わせ UDP のテーブルは六行からなり、各行はちょうど二つの ? を含み b は含まず、互いに重ならない。$3^4$ の組み合わせ空間のどれだけが覆われ、それ以外の出力は何になるか。(c) 二入力の組み合わせ UDP で r ? : ? : -; の行は合法か。(d) 1 0 : ? ; はどこかで合法か。

Click to reveal solution點擊顯示解答クリックで解答を表示

(a) $3^1 \times 2^1 \times 1 = 6$ combinations: the first input ranges over {0,1,x}, the second over {0,1}, the third is fixed at 1.(a) $3^1 \times 2^1 \times 1 = 6$ 種組合:第一個輸入在 {0,1,x} 上變動,第二個在 {0,1} 上變動,第三個固定為 1。(a) $3^1 \times 2^1 \times 1 = 6$ 通り。第一の入力は {0,1,x} を動き、第二は {0,1} を動き、第三は 1 に固定される。

(b) Each row covers $3^2 = 9$ combinations, so six disjoint rows cover 54 of $3^4 = 81$, that is two thirds. The remaining 27 combinations match no row and drive the output to x.(b) 每列涵蓋 $3^2 = 9$ 種組合,所以六列互不重疊共涵蓋 $3^4 = 81$ 中的 54 種,也就是三分之二。剩下的 27 種對應不到任何一列,會把輸出驅動成 x。(b) 各行は $3^2 = 9$ 通りを覆うので、重ならない六行で $3^4 = 81$ 通り中 54 通り、すなわち三分の二を覆う。残る 27 通りはどの行にも一致せず、出力を x に駆動する。

(c) No. r is an edge specifier and edge specifiers are only permitted in sequential UDPs — a combinational UDP has no notion of a previous input value, only of the present one. The compiler rejects it.(c) 不合法。r 是邊緣指定子,而邊緣指定子只允許出現在循序邏輯 UDP 中 — 組合邏輯 UDP 根本沒有「輸入前一個值」的概念,只有現在的值。編譯器會拒絕它。(c) 合法ではない。r はエッジ指定子であり、エッジ指定子は順序 UDP にしか許されない。組み合わせ UDP には入力の直前値という概念がなく、現在値しか存在しないからである。コンパイラはこれを拒否する。

(d) No. ? is forbidden in the output field. The output must be a definite 0, 1 or x, or (sequential only) -. The reason is that ? in the output would mean "the simulator may pick any value", which is not something an event-driven simulator can schedule.(d) 不合法。? 在輸出欄是被禁止的。輸出必須是確定的 0、1 或 x,或者(僅限循序邏輯)-。理由是輸出欄的 ? 會代表「模擬器可以挑任何值」,而那不是事件驅動模擬器能排程的東西。(d) 合法ではない。? は出力フィールドでは禁じられている。出力は確定した 0、1、x、あるいは(順序 UDP に限り)- でなければならない。出力の ? は「シミュレータが任意の値を選んでよい」を意味することになるが、それはイベント駆動シミュレータがスケジュールできるものではないからである。

3. Combinational UDPs in Detail3. 組合邏輯 UDP 詳解3. 組み合わせ UDP の詳細

3.1 Column discipline3.1 欄位的紀律3.1 列の規律

A combinational UDP with n inputs has a table of n+1 columns: the first n are the inputs in port-list order, then a colon, then the single output column, then a semicolon. There are no headings; the comment line that every well-written table carries is purely for humans.有 n 個輸入的組合邏輯 UDP,其表格有 n+1 欄:前 n 欄是依埠列表順序排列的輸入,接著一個冒號,然後是單一的輸出欄,最後是分號。表格沒有標題;每張寫得好的表都會有的那行註解純粹是給人看的。入力が n 個の組み合わせ UDP のテーブルは n+1 列からなる。最初の n 列がポートリストの順序での入力、次にコロン、続いて唯一の出力列、最後にセミコロンである。見出しはない。よく書かれたテーブルに必ず付いているコメント行は、純粋に人間のためのものである。

Port-list order is the whole contract. If the declaration is primitive mux_prim (mux_out, select, a, b); then column one is select, column two is a, column three is b. Reorder the inputs in the declaration and every row in the table changes meaning without a single warning. This is the most common way a hand-written UDP goes wrong, and the reason the colon before the output exists at all: it is the one visual anchor that tells you where the inputs stop.埠列表的順序就是整份契約。如果宣告是 primitive mux_prim (mux_out, select, a, b);,那麼第一欄是 select、第二欄是 a、第三欄是 b。把宣告裡的輸入重新排序,表中每一列的意義都會改變,而且連一個警告都不會有。這是手寫 UDP 最常出錯的方式,也正是輸出前面那個冒號存在的理由:它是唯一能告訴你輸入到哪裡結束的視覺錨點。ポートリストの順序が契約のすべてである。宣言が primitive mux_prim (mux_out, select, a, b); なら、第一列は select、第二列は a、第三列は b である。宣言の入力を並べ替えれば、テーブルのすべての行の意味が変わる。警告は一つも出ない。手書きの UDP が壊れる最も多い原因がこれであり、出力の前のコロンが存在する理由でもある。入力がどこで終わるかを教えてくれる唯一の視覚的な目印なのだ。

3.2 The multiplexer, row by row3.2 多工器,逐列解讀3.2 マルチプレクサを一行ずつ

Here is the lecture's two-to-one multiplexer written out in full, with select = 0 choosing a and select = 1 choosing b.以下是講義那個 2 對 1 多工器的完整寫法,select = 0 選 a,select = 1 選 b。以下は講義の 2 対 1 マルチプレクサを省略せずに書いたものである。select = 0 で a を、select = 1 で b を選ぶ。

primitive mux_prim (mux_out, select, a, b);
  output mux_out;
  input  select, a, b;
  table
  //  select   a   b   :  mux_out
        0     0   0   :    0;    // table order = port order
        0     0   1   :    0;    // one output, many inputs, no inout
        0     0   x   :    0;    // only 0, 1, x may appear
        0     1   0   :    1;    // a z input is treated as x
        0     1   1   :    1;
        0     1   x   :    1;    // last column is the output

        1     0   0   :    0;
        1     1   0   :    0;
        1     x   0   :    0;
        1     0   1   :    1;
        1     1   1   :    1;
        1     x   1   :    1;

        x     0   0   :    0;    // reduces pessimism
        x     1   1   :    1;
  endtable
endprimitive

Fourteen rows out of the $3^3 = 27$ possible combinations. Walk the three groups:$3^3 = 27$ 種可能組合中寫了十四列。逐一看這三組:$3^3 = 27$ 通りの組み合わせのうち十四行を書いている。三つのグループを順に見ていく。

Group組別グループRows列行What the group asserts這一組主張什麼そのグループが主張すること
select = 06The output equals a, for all three values of a, and the value of b is irrelevant — which is why b takes all three values in each half. Note 0 x ? : ? is not present: when a itself is unknown the output is unknown, and the default handles it.對 a 的三個值而言,輸出都等於 a,而 b 的值無關緊要 — 這就是為什麼每半邊裡 b 都取遍三個值。注意 0 x ? : ? 並沒有出現:當 a 本身未知時輸出就是未知,由預設行為處理即可。a の三つの値すべてについて出力は a に等しく、b の値は無関係である。だから各半分で b が三つの値すべてを取る。0 x ? : ? が存在しないことに注意。a 自身が不定なら出力も不定であり、既定の動作に任せればよい。
select = 16The mirror image: the output equals b and a is irrelevant.鏡像的情況:輸出等於 b,而 a 無關緊要。その鏡像である。出力は b に等しく、a は無関係である。
select = x2The pessimism-reduction rows. When the two data inputs agree, the choice does not matter and the output is known even though the control is not.降低悲觀的那兩列。當兩個資料輸入相同時,選誰都一樣,所以即使控制訊號未知,輸出仍然是已知的。悲観性を下げる行である。二つのデータ入力が一致していれば、どちらを選んでも同じなので、制御が不定でも出力は確定する。

The 13 uncovered combinations are exactly the ones where the answer honestly is unknown: select = 0 with a = x (three rows), select = 1 with b = x (three rows), and select = x with a ≠ b or either data input unknown (seven rows). The model is tight: nothing is lost, nothing is invented.那 13 種沒被涵蓋的組合,正好就是答案老老實實地未知的那些:select = 0 且 a = x(三列)、select = 1 且 b = x(三列),以及 select = x 且 a ≠ b 或任一資料輸入未知(七列)。這個模型很緊:沒有漏掉什麼,也沒有憑空捏造什麼。覆われていない 13 通りは、まさに答えが正直に不定である場合である。select = 0 かつ a = x(三通り)、select = 1 かつ b = x(三通り)、そして select = x で a ≠ b またはいずれかのデータ入力が不定の場合(七通り)。このモデルは引き締まっている。失われたものも、でっち上げられたものもない。

3.3 The ? shorthand and what it buys3.3 ? 簡寫及其好處3.3 ? という短縮形とその効用

The same function compresses to six rows:同一個函數可以壓縮成六列:同じ関数は六行に圧縮できる。

Shorthand table簡寫表短縮形のテーブル
  table
  //  select  a   b  : mux_out
        0    0   ?  :   0;   // ? = 0, 1, x
        0    1   ?  :   1;
        1    ?   0  :   0;
        1    ?   1  :   1;
        ?    0   0  :   0;
        ?    1   1  :   1;
  endtable
Coverage arithmetic涵蓋範圍的算術被覆の算術
row 1:  1 x 1 x 3 = 3   (000, 001, 00x)
row 2:  1 x 1 x 3 = 3   (010, 011, 01x)
row 3:  1 x 3 x 1 = 3   (100, 110, 1x0)
row 4:  1 x 3 x 1 = 3   (101, 111, 1x1)
row 5:  3 x 1 x 1 = 3   (000, 100, x00)
row 6:  3 x 1 x 1 = 3   (011, 111, x11)
                     ----
raw total            18
minus 4 duplicates   14   // 000, 011, 100, 111

Eighteen row-expansions, but four combinations are covered twice — 000 by rows 1 and 5, 011 by rows 2 and 6, 100 by rows 3 and 5, 111 by rows 4 and 6 — so the distinct coverage is 14, exactly matching the long form. The duplicates are consistent (both rows give the same output in each case), so the table is unambiguous. This is the check you should run on any shorthand table you write: expand, count, and look for a combination covered twice with different outputs.展開後共十八列,但有四種組合被涵蓋兩次 — 000 來自第 1 與第 5 列、011 來自第 2 與第 6 列、100 來自第 3 與第 5 列、111 來自第 4 與第 6 列 — 所以相異的涵蓋數是 14,正好與完整寫法相符。重複的部分彼此一致(每一種情況下兩列給出相同的輸出),所以這張表沒有歧義。這就是你對自己寫的任何一張簡寫表都該做的檢查:展開、計數,然後找出有沒有哪個組合被涵蓋兩次卻給出不同的輸出。展開すると十八行ぶんになるが、四つの組み合わせが二度覆われる — 000 は第 1 行と第 5 行、011 は第 2 行と第 6 行、100 は第 3 行と第 5 行、111 は第 4 行と第 6 行である。したがって重複を除いた被覆は 14 となり、完全形とちょうど一致する。重複はいずれも整合しており(どの場合も両方の行が同じ出力を与える)、テーブルは曖昧ではない。自分で書いた短縮形のテーブルには必ずこの検査を行うこと。展開し、数え、二度覆われていて出力が異なる組み合わせがないか探す。

Why bother with the long form at all, then? Because expansion is how the compiler builds the lookup table, and a row with many ? in a wide UDP expands explosively. A nine-input sequential UDP with a row of eight ? expands to $3^8 = 6561$ entries from one line of source. Library writers keep an eye on this; it is why vendor models use b rather than ? wherever the unknown case is handled by a separate row.那為什麼還要有完整寫法?因為展開正是編譯器建立查表的方式,而在寬的 UDP 裡,一列含有很多 ? 會爆炸性地展開。一個九輸入的循序邏輯 UDP,只要有一列含八個 ?,一行原始碼就展開成 $3^8 = 6561$ 筆。單元庫的作者會盯著這件事;這也是為什麼在未知情況由另一列單獨處理時,供應商的模型會用 b 而不用 ?。では完全形を書く意味はどこにあるのか。展開こそコンパイラがルックアップテーブルを構築する方法であり、幅の広い UDP では ? を多く含む行が爆発的に展開するからである。九入力の順序 UDP で ? を八個含む行が一行あれば、ソース一行から $3^8 = 6561$ 項目が生成される。ライブラリの作者はこれに目を光らせている。不定の場合を別の行で処理できるところでベンダのモデルが ? ではなく b を使うのは、そのためである。

The pessimism-reduction rows are the interesting part降低悲觀的那幾列才是有趣的地方悲観性を下げる行こそが面白いところ

Delete ? 0 0 : 0; and ? 1 1 : 1; from the table and the multiplexer still implements the right function for all known inputs. What changes is behaviour during the unknown transient — and in a gate-level simulation of a real chip, the first few hundred nanoseconds after power-up are entirely unknown transient.把 ? 0 0 : 0; 與 ? 1 1 : 1; 從表中刪掉,對所有已知輸入而言,這個多工器仍然實現正確的函數。改變的是未知暫態期間的行為 — 而在一顆真實晶片的閘層級模擬裡,上電後的前幾百奈秒整段都是未知暫態。? 0 0 : 0; と ? 1 1 : 1; をテーブルから削っても、既知の入力すべてについてマルチプレクサは正しい関数を実装する。変わるのは不定な過渡状態での振る舞いである — そして実チップのゲートレベルシミュレーションでは、電源投入後の最初の数百ナノ秒はまるごと不定な過渡状態である。

Consider a bus multiplexer whose select comes from a controller that is still in reset. Without those rows, every output bit is x, so every downstream flip-flop captures x, so the unknown spreads. With them, any bit where both sources happen to agree resolves. On a real design that is often most of the bus. This is not cosmetic — it is the difference between a netlist simulation that converges after reset and one that stays red forever.想想一個匯流排多工器,它的 select 來自一個還在重置中的控制器。少了那兩列,每一個輸出位元都是 x,於是下游每一顆正反器都擷取到 x,未知就這樣擴散。有了那兩列,只要某個位元的兩個來源剛好相同,就能定下來。在真實設計上,那往往是匯流排的大多數位元。這不是化妝 — 這是「網表模擬在重置後收斂」與「永遠紅到底」的差別。まだリセット中のコントローラから select が来るバスマルチプレクサを考えてみよう。あの二行がなければ出力ビットはすべて x になり、下流のフリップフロップはすべて x を取り込み、不定は広がっていく。二行があれば、二つのソースがたまたま一致しているビットはすべて確定する。実際の設計ではそれがバスの大半であることが多い。これは見た目の問題ではない。ネットリストのシミュレーションがリセット後に収束するか、永遠に赤いままかの違いである。

3.4 The majority function3.4 多數決函數3.4 多数決関数

The lecture's second combinational example is $\text{out} = ab + ac + bc$, the three-input majority function, which is also the carry-out of a full adder.講義的第二個組合邏輯例子是 $\text{out} = ab + ac + bc$,也就是三輸入的多數決函數,同時也是全加器的進位輸出。講義の二つ目の組み合わせ回路の例は $\text{out} = ab + ac + bc$、すなわち三入力の多数決関数であり、全加算器のキャリー出力でもある。

primitive boolean_func (out, a, b, c);
  output out;
  input  a, b, c;
  table
  // a b c : out
     0 0 ? : 0;
     0 ? 0 : 0;
     ? 0 0 : 0;
     1 1 ? : 1;
     1 ? 1 : 1;
     ? 1 1 : 1;
  endtable
endprimitive

Six rows for a function of three variables, and the structure is beautifully direct: any two inputs at 0 force the output to 0; any two inputs at 1 force it to 1. That is the definition of a majority gate, transcribed. The third input is a ? because it genuinely cannot change the answer, and that includes the case where it is x.三個變數的函數只用六列,而且結構漂亮而直接:任兩個輸入為 0 就把輸出壓成 0;任兩個輸入為 1 就把輸出推成 1。那就是多數決閘的定義,直接抄寫下來。第三個輸入寫成 ?,因為它真的不可能改變答案,連它是 x 的情況也包含在內。三変数の関数がわずか六行で、しかも構造が見事に素直である。いずれか二つの入力が 0 なら出力は 0 に決まり、いずれか二つが 1 なら 1 に決まる。多数決ゲートの定義をそのまま書き写したものだ。第三の入力が ? なのは、それが本当に答えを変えられないからであり、それが x である場合も含まれる。

Worked evaluation — boolean_func with unknowns實際求值 — 含未知值的 boolean_func評価の実演 — 不定値を含む boolean_func
a=1, b=1, c=x. Scan: row 1 0 0 ? needs a=0, no. Row 2 0 ? 0 needs a=0, no. Row 3 ? 0 0 needs b=0, no. Row 4 1 1 ? — a=1 yes, b=1 yes, c matches ? yes. Match: out = 1. Correct: two ones already make a majority.a=1, b=1, c=x。逐列掃描:第 1 列 0 0 ? 需要 a=0,不符。第 2 列 0 ? 0 需要 a=0,不符。第 3 列 ? 0 0 需要 b=0,不符。第 4 列 1 1 ? — a=1 符合、b=1 符合、c 對上 ? 符合。對應成功:out = 1。正確:已經有兩個 1 就構成多數。a=1, b=1, c=x。順に走査する。第 1 行 0 0 ? は a=0 を要求、不一致。第 2 行 0 ? 0 は a=0 を要求、不一致。第 3 行 ? 0 0 は b=0 を要求、不一致。第 4 行 1 1 ? — a=1 で一致、b=1 で一致、c は ? に一致。一致:out = 1。正しい。1 が二つあれば既に多数である。
a=0, b=x, c=1. Row 1 needs b=0, no. Row 2 needs c=0, no. Row 3 needs b=0, no. Row 4 needs a=1, no. Row 5 1 ? 1 needs a=1, no. Row 6 ? 1 1 needs b=1, and b is x, no. No match: out = x. Correct: with one 0, one 1 and one unknown, the majority genuinely depends on the unknown.a=0, b=x, c=1。第 1 列需要 b=0,不符。第 2 列需要 c=0,不符。第 3 列需要 b=0,不符。第 4 列需要 a=1,不符。第 5 列 1 ? 1 需要 a=1,不符。第 6 列 ? 1 1 需要 b=1,而 b 是 x,不符。無列對應:out = x。正確:一個 0、一個 1、一個未知,多數決確實取決於那個未知值。a=0, b=x, c=1。第 1 行は b=0 を要求、不一致。第 2 行は c=0 を要求、不一致。第 3 行は b=0 を要求、不一致。第 4 行は a=1 を要求、不一致。第 5 行 1 ? 1 は a=1 を要求、不一致。第 6 行 ? 1 1 は b=1 を要求するが b は x、不一致。一致なし:out = x。正しい。0 が一つ、1 が一つ、不定が一つなら、多数決は確かにその不定値に依存する。
a=z, b=0, c=0. The z is converted to x first. Row 1 0 0 ? needs a=0, and a is x, no. Row 2 needs a=0, no. Row 3 ? 0 0 — a matches ?, b=0, c=0. Match: out = 0. Correct, and note that this is pessimism reduction doing its job: two zeros settle the question regardless of the floating third input.a=z, b=0, c=0。z 會先被轉成 x。第 1 列 0 0 ? 需要 a=0,而 a 是 x,不符。第 2 列需要 a=0,不符。第 3 列 ? 0 0 — a 對上 ?,b=0,c=0。對應成功:out = 0。正確,而且請注意這正是降低悲觀在發揮作用:兩個 0 就把問題定下來了,第三個浮接的輸入是什麼都無所謂。a=z, b=0, c=0。z はまず x に変換される。第 1 行 0 0 ? は a=0 を要求するが a は x、不一致。第 2 行は a=0 を要求、不一致。第 3 行 ? 0 0 — a は ? に一致、b=0、c=0。一致:out = 0。正しい。そしてこれこそ悲観性低減が働いている場面である。0 が二つあれば、浮いた第三の入力が何であれ決着がつく。

Compare this with how you would write the same function in RTL — assign out = (a & b) | (a & c) | (b & c); — and evaluate it with a = z, b = 0, c = 0. Verilog's bitwise operators treat z as x too, so each AND yields 0 (x & 0 = 0), and the OR of three zeros is 0. The two models agree here, but they do not always: the built-in operators apply x-optimism term by term, whereas a UDP applies exactly the rows you wrote. That divergence is why a synthesised netlist and its RTL source can disagree on x propagation even when the logic is formally equivalent. Lecture 5 revisits this under the heading of simulation/synthesis mismatch.拿它和你在 RTL 裡寫同一個函數的方式比較 — assign out = (a & b) | (a & c) | (b & c); — 並以 a = z, b = 0, c = 0 求值。Verilog 的位元運算子同樣把 z 當成 x,所以每個 AND 都得到 0(x & 0 = 0),三個 0 做 OR 還是 0。兩個模型在這裡一致,但不是永遠一致:內建運算子是逐項套用 x 樂觀化,而 UDP 套用的恰好只有你寫出來的那些列。這個分歧正是為什麼合成出來的網表與它的 RTL 原始碼,即使在形式上等價,仍可能在 x 的傳播上意見不同。第 5 講會在「模擬/合成不一致」這個標題下再談。同じ関数を RTL で書いた場合 — assign out = (a & b) | (a & c) | (b & c); — と比べ、a = z, b = 0, c = 0 で評価してみる。Verilog のビット演算子も z を x として扱うので、各 AND は 0 を返し(x & 0 = 0)、三つの 0 の OR は 0 になる。ここでは二つのモデルが一致するが、常にそうとは限らない。組み込み演算子は項ごとに x の楽観化を適用するのに対し、UDP は書かれた行だけを適用する。この食い違いこそ、論理的には等価であっても、合成後のネットリストとその RTL ソースが x の伝搬で食い違いうる理由である。第 5 講はこれをシミュレーションと合成の不一致という見出しで再び扱う。

Practice 2 — Evaluate with x and z練習 2 — 含 x 與 z 的求值演習 2 — x と z を含む評価

Problem: Using the six-row shorthand mux_prim table from §3.3, give mux_out for: (a) select=0, a=1, b=x; (b) select=1, a=x, b=0; (c) select=x, a=1, b=0; (d) select=x, a=z, b=z; (e) select=z, a=1, b=1. For each, say in one sentence whether the answer is the best possible answer or whether a better table could do better.題目:使用 §3.3 的六列簡寫 mux_prim 表,求出 mux_out:(a) select=0, a=1, b=x;(b) select=1, a=x, b=0;(c) select=x, a=1, b=0;(d) select=x, a=z, b=z;(e) select=z, a=1, b=1。對每一題,用一句話說明這個答案是否已是最好的可能答案,或者更好的表格能做得更好。問題:§3.3 の六行の短縮形 mux_prim テーブルを用いて mux_out を求めよ。(a) select=0, a=1, b=x、(b) select=1, a=x, b=0、(c) select=x, a=1, b=0、(d) select=x, a=z, b=z、(e) select=z, a=1, b=1。それぞれについて、その答えが可能な限り最良か、よりよいテーブルならもっと良くできるかを一文で述べよ。

Click to reveal solution點擊顯示解答クリックで解答を表示

(a) 1. Row 2 0 1 ? matches. Best possible: with select known and a = 1, b cannot matter.(a) 1。第 2 列 0 1 ? 對應。這是最好的答案:select 已知且 a = 1 時,b 不可能有影響。(a) 1。第 2 行 0 1 ? が一致する。最良である。select が既知で a = 1 なら b は影響し得ない。

(b) 0. Row 3 1 ? 0 matches. Best possible, by the mirror argument.(b) 0。第 3 列 1 ? 0 對應。依鏡像的理由,同樣是最好的答案。(b) 0。第 3 行 1 ? 0 が一致する。対称な理由により、これも最良である。

(c) x. No row matches: rows 1–4 need a known select, rows 5 and 6 need the data inputs to agree. This is the best possible answer — the output really does depend on the unknown select.(c) x。沒有任何一列對應:第 1–4 列需要已知的 select,第 5、6 列需要兩個資料輸入相同。這就是最好的答案 — 輸出確實取決於那個未知的 select。(c) x。一致する行はない。第 1〜4 行は既知の select を要求し、第 5・6 行はデータ入力の一致を要求する。これこそ最良の答えである。出力は確かに不定の select に依存する。

(d) x. Both z inputs become x, so we have x x x; rows 5 and 6 need 0 0 or 1 1. No table could do better: nothing is known.(d) x。兩個 z 輸入都變成 x,於是我們有 x x x;第 5、6 列需要 0 0 或 1 1。沒有任何表能做得更好:什麼都不知道。(d) x。二つの z 入力はともに x になるので x x x となる。第 5・6 行は 0 0 か 1 1 を要求する。どんなテーブルでもこれ以上はできない。何も分かっていないからだ。

(e) 1. select = z becomes x, and row 6 ? 1 1 matches because both data inputs are 1. This is exactly the pessimism-reduction row earning its keep: a floating select line still yields a definite output when the data agrees.(e) 1。select = z 變成 x,而第 6 列 ? 1 1 對應,因為兩個資料輸入都是 1。這正是降低悲觀那一列在回本:即使 select 線浮接,只要資料一致,輸出仍然確定。(e) 1。select = z は x になり、データ入力がともに 1 なので第 6 行 ? 1 1 が一致する。これこそ悲観性低減の行が元を取る場面である。select 線が浮いていても、データが一致していれば出力は確定する。

Practice 3 — Write a comparator UDP練習 3 — 寫一個比較器 UDP演習 3 — 比較器 UDP を書く

Problem: Write a combinational UDP eq1 with ports (y, a, b) that outputs 1 when a == b and 0 when they differ — i.e. an XNOR. Make it as un-pessimistic as the function allows, and justify every omitted combination. Then state how many of the nine combinations your table leaves to the default.題目:寫一個埠為 (y, a, b) 的組合邏輯 UDP eq1,當 a == b 時輸出 1、不同時輸出 0 — 也就是一個 XNOR。在這個函數允許的範圍內盡量不悲觀,並為每一個省略掉的組合說明理由。最後說出你的表把九種組合中的幾種留給了預設行為。問題:ポートが (y, a, b) の組み合わせ UDP eq1 を書け。a == b のとき 1、異なるとき 0 を出力する、すなわち XNOR である。この関数が許す限り悲観的でないようにし、省いた組み合わせすべてについて理由を述べよ。さらに、九通りのうち何通りを既定の動作に委ねたかを答えよ。

Click to reveal solution點擊顯示解答クリックで解答を表示
primitive eq1 (y, a, b);
  output y;
  input  a, b;
  table
  // a b : y
     0 0 : 1;
     1 1 : 1;
     0 1 : 0;
     1 0 : 0;
  endtable
endprimitive

Four rows; the remaining five combinations (0x, 1x, x0, x1, xx) all default to x.四列;其餘五種組合(0x、1x、x0、x1、xx)全部預設為 x。四行である。残る五通り(0x、1x、x0、x1、xx)はすべて既定の x になる。

Justification: XNOR has no controlling value. Unlike AND or OR, where one input can decide the output on its own, equality needs both operands. If either input is unknown, the answer is genuinely unknown, so there is nothing a cleverer table could add — the model is already as sharp as the function. Any attempt to write, say, x x : 1; ("two unknowns might be equal") would be optimistic and would hide real problems: two independently unknown nets are not known to be equal.理由:XNOR 沒有控制值。不像 AND 或 OR 那樣有某個輸入能自己決定輸出,相等與否需要兩個運算元都知道。只要任一輸入未知,答案就真的未知,所以再聰明的表也加不出東西 — 這個模型已經和函數本身一樣銳利了。若有人想寫 x x : 1;(「兩個未知值說不定相等」),那會是樂觀的,而且會藏住真正的問題:兩條各自未知的 net,並不能認定它們相等。理由:XNOR には制御値がない。AND や OR のように一方の入力だけで出力が決まるのとは違い、等価判定は両方のオペランドを必要とする。どちらかの入力が不定なら答えも本当に不定であり、どれほど巧妙なテーブルでも付け加えられるものはない。このモデルはすでに関数と同じだけ鋭いのである。たとえば x x : 1;(「二つの不定値は等しいかもしれない」)と書けば、それは楽観的であり、実在する問題を隠してしまう。互いに独立に不定な二つの net が等しいとは分からないからである。

This is the counterpart to the multiplexer result and a good exam discriminator: pessimism reduction is possible exactly where the function has a controlling value, and impossible where it does not.這與多工器的結果互為對照,也是很好的考試鑑別點:恰好在函數具有控制值的地方可以降低悲觀,而在沒有控制值的地方則不可能。これはマルチプレクサの結果と対をなし、試験でよい識別点になる。悲観性の低減は、関数に制御値があるところでちょうど可能であり、ないところでは不可能である。

4. Sequential UDPs: Level- and Edge-Sensitive Tables4. 循序邏輯 UDP:準位敏感表與邊緣敏感表4. 順序 UDP:レベル感応表とエッジ感応表

4.1 The state column4.1 狀態欄4.1 状態の列

A sequential UDP has n inputs, one output, one unit of state, and n+2 columns:循序邏輯 UDP 有 n 個輸入、一個輸出、一單位的狀態,以及 n+2 欄:順序 UDP は n 個の入力、一つの出力、一単位の状態を持ち、列は n+2 個である。

Sequential row format循序邏輯的列格式順序 UDP の行の形式 $$\underbrace{\texttt{in}_1 \;\; \texttt{in}_2 \;\cdots\; \texttt{in}_n}_{n \text{ input fields}} \;\;\texttt{:}\;\; \underbrace{\texttt{state}}_{\text{current } q} \;\;\texttt{:}\;\; \underbrace{\texttt{next}}_{\text{new } q}\texttt{;}$$

The crucial realisation is that the state and the output are the same object. There is no separate state register hidden inside the UDP; the state is the current value of the output reg, and the last column is simultaneously "the next state" and "the new output value". The lecture writes the heading as q_out/next state for exactly this reason. A UDP therefore models a Moore machine with a one-bit state and an identity output function — which is all a latch or a flip-flop ever is.關鍵的領悟是:狀態與輸出是同一個東西。UDP 內部沒有另外藏一個狀態暫存器;狀態就是輸出 reg 目前的值,而最後一欄同時是「下一個狀態」與「新的輸出值」。講義把欄位標題寫成 q_out/next state,正是這個理由。因此一個 UDP 所建模的是一台狀態只有一位元、輸出函數為恆等的 Moore 機 — 而閂鎖器或正反器從來就只是這樣。決定的に重要な理解は、状態と出力が同じものだということである。UDP の内部に別の状態レジスタが隠れているわけではない。状態そのものが出力 reg の現在値であり、最後の列は「次状態」であると同時に「新しい出力値」でもある。講義が見出しを q_out/next state と書いているのは、まさにこのためだ。したがって UDP がモデル化するのは、状態が 1 ビットで出力関数が恒等写像の Moore 機械である — ラッチやフリップフロップとは、もともとそれだけのものだ。

The state field accepts 0, 1, x, ? or b, never an edge specifier (state does not have a "previous value" in the matching sense) and never -. The output field accepts 0, 1, x or -, never ? or b.狀態欄接受 0、1、x、? 或 b,不接受邊緣指定子(在匹配的意義上,狀態沒有「前一個值」),也不接受 -。輸出欄接受 0、1、x 或 -,不接受 ? 或 b。状態フィールドは 0、1、x、?、b を受け付け、エッジ指定子は受け付けず(照合の意味で状態には「直前値」がない)、- も受け付けない。出力フィールドは 0、1、x、- を受け付け、? と b は受け付けない。

Because the output reg starts life as x, a sequential UDP powers up unknown unless you initialise it. The standard permits a single initial statement inside the primitive body:因為輸出 reg 一開始的值就是 x,循序邏輯 UDP 若不初始化,上電後就是未知。標準允許在原型本體內寫一個 initial 敘述:出力 reg は x から始まるので、初期化しなければ順序 UDP は電源投入時に不定である。標準はプリミティブ本体の中に initial 文を一つだけ書くことを許している。

primitive dff_init (q, clk, d);
  output q;  input clk, d;  reg q;
  initial q = 1'b0;        // legal only as a simple constant assignment to the output reg
  table
    r 0 : ? : 0;
    r 1 : ? : 1;
    f ? : ? : -;
    ? * : ? : -;
  endtable
endprimitive

That initial is the only procedural statement allowed anywhere in a UDP, it may assign only a constant 0, 1 or x, and it runs once at time zero. Library models use it sparingly, because a flip-flop that silently powers up at 0 can hide a missing reset — the same optimism trap discussed in §2.3.那個 initial 是 UDP 裡唯一允許的程序式敘述,它只能指定常數 0、1 或 x,而且只在時間零執行一次。單元庫的模型很少用它,因為一顆默默從 0 開機的正反器可能藏住缺漏的重置 — 就是 §2.3 討論過的那個樂觀陷阱。この initial は UDP 内で唯一許される手続き文であり、代入できるのは定数 0、1、x のみ、実行は時刻ゼロに一度きりである。ライブラリのモデルがこれを控えめにしか使わないのは、黙って 0 で立ち上がるフリップフロップがリセットの欠落を隠しかねないからだ — §2.3 で論じた楽観の罠そのものである。

4.2 Level-sensitive behaviour: the transparent latch4.2 準位敏感的行為:透通閂鎖器4.2 レベル感応の振る舞い:透過ラッチ

primitive latch (q_out, enable, data);
  output q_out;
  input  enable, data;
  reg    q_out;

  table
  //  en   data  :  state  :  q_out / next state
       1    1    :    ?    :     1;
       1    0    :    ?    :     0;
       0    ?    :    ?    :     -;    // '-' means "no change"
  endtable
endprimitive

Three rows, and every one of them is level-sensitive: no field contains an edge specifier. Read them as English.三列,而且每一列都是準位敏感的:沒有任何欄位含邊緣指定子。把它們當英文讀一遍。三行あり、そのいずれもレベル感応である。エッジ指定子を含むフィールドが一つもない。これらを文章として読んでみる。

Count the coverage: rows 1 and 2 each cover one input combination times three state values, row 3 covers $1 \times 3 \times 3 = 9$. Total 15 of the $3 \times 3 \times 3 = 27$ possible (input, input, state) triples. The twelve uncovered ones all have enable = x or data = x with enable = 1, and they all default to x.數一數涵蓋範圍:第 1、2 列各涵蓋一種輸入組合乘以三個狀態值,第 3 列涵蓋 $1 \times 3 \times 3 = 9$ 種。合計 15 種,佔 $3 \times 3 \times 3 = 27$ 種可能的(輸入, 輸入, 狀態)三元組。沒被涵蓋的那十二種,不是 enable = x,就是 enable = 1 時 data = x,而它們全部預設為 x。被覆を数えてみる。第 1 行と第 2 行はそれぞれ入力の組み合わせ一通り × 状態三値を覆い、第 3 行は $1 \times 3 \times 3 = 9$ 通りを覆う。合計 15 通りで、可能な(入力, 入力, 状態)の三つ組 $3 \times 3 \times 3 = 27$ 通りのうちの 15 である。覆われない十二通りは enable = x であるか、enable = 1 で data = x であるかのいずれかで、すべて既定の x になる。

Is that right? Partly. enable = 1, data = x genuinely gives q = x — a transparent latch passes the unknown through. But enable = x is more interesting. If enable = x and data = 1 and the current state is already 1, then whether the latch is transparent or opaque, the answer is 1. A less pessimistic model adds:這樣對嗎?對了一半。enable = 1, data = x 確實應該給 q = x — 透通的閂鎖器會把未知值直接放過去。但 enable = x 就比較有趣了。如果 enable = x、data = 1,而目前狀態已經是 1,那麼不論閂鎖器是透通還是不透通,答案都是 1。比較不悲觀的模型會加上:これは正しいのか。半分は正しい。enable = 1, data = x は確かに q = x を与えるべきだ — 透過状態のラッチは不定値をそのまま通す。しかし enable = x のほうが興味深い。enable = x かつ data = 1 で、現在の状態がすでに 1 なら、ラッチが透過であろうと不透過であろうと答えは 1 である。より悲観的でないモデルは次の行を加える。

       x    1    :    1    :     1;   // transparent or not, the answer is 1
       x    0    :    0    :     0;   // transparent or not, the answer is 0

Real library latch models contain exactly these rows. They are the sequential analogue of the multiplexer's ? 0 0 : 0;, and they matter during clock-gating transients where the enable is briefly unknown.真實單元庫的閂鎖器模型裡就有這幾列。它們是多工器那條 ? 0 0 : 0; 在循序邏輯上的對應物,在時脈閘控(clock gating)的暫態中 enable 短暫未知時特別重要。実際のライブラリのラッチモデルには、まさにこれらの行が入っている。マルチプレクサの ? 0 0 : 0; に対応する順序回路版であり、クロックゲーティングの過渡で enable が一瞬不定になる場面で効いてくる。

4.3 Reading the latch waveform4.3 讀懂閂鎖器的波形4.3 ラッチ波形の読み方

dataenableq_out 0102030 405060 t transparenttransparenttransparent pulse blocked
A transparent latch driven by the three-row UDP. Shaded bands are the windows where enable = 1. The data pulse at t = 44 is invisible on q_out because the latch is opaque; the pulse that arrives at t = 18 is captured and held across the enable falling edge at t = 20.由三列 UDP 驅動的透通閂鎖器。陰影帶是 enable = 1 的視窗。t = 44 的資料脈衝在 q_out 上看不見,因為此時閂鎖器不透通;t = 18 抵達的脈衝則被擷取,並在 t = 20 的 enable 下降緣之後繼續保持。三行の UDP が駆動する透過ラッチ。網掛けの帯は enable = 1 の窓である。t = 44 のデータパルスは、ラッチが不透過なので q_out には現れない。t = 18 に到着したパルスは取り込まれ、t = 20 の enable 立ち下がりを越えて保持される。

Trace it event by event, because this is precisely what an exam asks you to do.逐事件追蹤一遍,因為考試要你做的正是這件事。イベントごとに追ってみる。試験で求められるのはまさにこれだからである。

tEvent事件事象Row that matches對應到的列一致する行q_outq_outq_out
0initial sets the reg to 0initial 把 reg 設為 0initial が reg を 0 にする—0
3, 6data pulses while enable = 0enable = 0 時 data 出現脈衝enable = 0 のあいだに data がパルスする0 ? : ? : -0 (held)0(保持)0(保持)
10enable rises, data = 0enable 上升,data = 0enable が立ち上がり、data = 01 0 : ? : 00
12data rises, enable = 1data 上升,enable = 1data が立ち上がり、enable = 11 1 : ? : 11
15data fallsdata 下降data が立ち下がる1 0 : ? : 00
18data risesdata 上升data が立ち上がる1 1 : ? : 11
20enable falls (data still 1)enable 下降(data 仍為 1)enable が立ち下がる(data はまだ 1)0 ? : ? : -1 (captured)1(已擷取)1(取り込み済み)
24data falls while opaque不透通期間 data 下降不透過のあいだに data が立ち下がる0 ? : ? : -1 (held)1(保持)1(保持)
30enable rises, data = 0enable 上升,data = 0enable が立ち上がり、data = 01 0 : ? : 00
33, 36data pulse inside the window視窗內的 data 脈衝窓の内側の data パルスrows 1 then 2先第 1 列,再第 2 列第 1 行、続いて第 2 行1, then 0先 1,再 01、次に 0
40enable falls, data = 0enable 下降,data = 0enable が立ち下がり、data = 00 ? : ? : -0 (captured)0(已擷取)0(取り込み済み)
44, 47data pulse while opaque不透通期間的 data 脈衝不透過のあいだの data パルス0 ? : ? : -0 (blocked)0(被擋下)0(遮断)
50enable rises, data = 0enable 上升,data = 0enable が立ち上がり、data = 01 0 : ? : 00 (no change visible)0(看不到變化)0(見える変化なし)
What the trace teaches這段追蹤教我們什麼このトレースが教えること

A latch captures the last value present before the enable deasserts, not the first. The pulse at t = 18–24 is captured at its leading edge and survives on q_out after the enable falls at t = 20 because the latch simply stops updating — it does not "sample". This is why hold-time violations on a latch-based design are about the data at the closing edge, and it is the behavioural root of time borrowing. Compare with the flip-flop below, where only the instant of the clock edge matters.閂鎖器擷取的是 enable 解除之前出現的最後一個值,不是第一個。t = 18–24 的脈衝在它的前緣就被擷取,並且在 t = 20 enable 下降之後仍留在 q_out 上,因為閂鎖器只是停止更新 — 它並不「取樣」。這就是為什麼以閂鎖器為基礎的設計,其 hold time 違規談的是關閉邊緣那一刻的資料,也是時間借用(time borrowing)在行為上的根源。拿下面的正反器對照,那裡只有時脈邊緣那一瞬間有意義。ラッチが取り込むのは、enable がディアサートされる直前に存在していた最後の値であって、最初の値ではない。t = 18〜24 のパルスはその先頭で取り込まれ、t = 20 に enable が立ち下がったあとも q_out に残る。ラッチは更新をやめるだけで、「サンプリング」はしないからである。ラッチベース設計のホールド時間違反が閉じるエッジでのデータの問題になるのはこのためであり、タイムボローイングの振る舞い上の根拠でもある。下のフリップフロップと比べてみること。そちらはクロックエッジの瞬間だけが意味を持つ。

4.4 Edge-sensitive behaviour: the D flip-flop4.4 邊緣敏感的行為:D 型正反器4.4 エッジ感応の振る舞い:D フリップフロップ

primitive d_prim1 (q_out, clock, data);
  output q_out;
  input  clock, data;
  reg    q_out;

  table
  //  clk    data  :  state  :  q_out / next state
      (01)    0    :    ?    :    0;    // rising clock edge, capture 0
      (01)    1    :    ?    :    1;    // rising clock edge, capture 1
      (0?)    1    :    1    :    1;    // 0->x edge, data=1, already 1: stays 1

      (?0)    ?    :    ?    :    -;    // any falling clock edge: hold

       ?     (??)  :    ?    :    -;    // data changed, clock steady: hold
  endtable
endprimitive

Five rows, and every one is doing specific work. The first two are the flip-flop proper. The last two are what make the model usable at all, and they are the rows students omit.五列,而且每一列都在做具體的工作。前兩列是正反器本身。後兩列才是讓這個模型堪用的關鍵,而它們正是學生會漏掉的列。五行あり、そのどれもが具体的な仕事をしている。最初の二行がフリップフロップ本体である。最後の二行こそモデルを使い物にしているところであり、学生が省いてしまうのもその二行である。

Why row 4 ((?0) ? : ? : -) is mandatory. Without it, a falling clock edge matches no row and the output goes to x. Every flip-flop in the design would be destroyed on the first negative clock edge. The row says explicitly: a falling clock changes nothing.為什麼第 4 列((?0) ? : ? : -)是必要的。少了它,時脈的下降緣對應不到任何一列,輸出就變成 x。設計中的每一顆正反器都會在第一個時脈負緣被毀掉。這一列明說:時脈下降什麼都不改變。なぜ第 4 行((?0) ? : ? : -)が必須か。これがなければクロックの立ち下がりはどの行にも一致せず、出力は x になる。設計中のすべてのフリップフロップが最初のクロック負エッジで壊れてしまう。この行は「クロックの立ち下がりは何も変えない」と明示している。

Why row 5 (? (??) : ? : -) is mandatory. Without it, any change on data while the clock is steady matches no row and the output goes to x. A flip-flop whose D input toggles between clock edges — which is every flip-flop in every design — would be destroyed immediately. The row says: data movement with a static clock is ignored.為什麼第 5 列(? (??) : ? : -)是必要的。少了它,時脈穩定不動時 data 上的任何變化都對應不到任何一列,輸出就變成 x。一顆 D 輸入會在時脈邊緣之間切換的正反器 — 也就是任何設計中的任何一顆正反器 — 會立刻被毀掉。這一列說:時脈不動時的資料變化一律忽略。なぜ第 5 行(? (??) : ? : -)が必須か。これがなければ、クロックが静止しているあいだの data の変化はどの行にも一致せず、出力は x になる。クロックエッジのあいだに D 入力がトグルするフリップフロップ — つまりどの設計のどのフリップフロップも — が即座に壊れる。この行は「クロックが静止しているときのデータの動きは無視する」と述べている。

Why row 3 ((0?) 1 : 1 : 1) exists. This is the pessimism-reduction row of the flip-flop. The clock has gone from 0 to something that is not 0 — either a clean 1 (already covered by row 2) or an x. If it went to x we do not know whether a real edge occurred. But if data = 1 and the flip-flop already holds 1, then both possibilities give 1: if the edge was real we capture 1, if it was not we hold 1. So the output stays 1 and the unknown clock does no damage. The symmetric row (0?) 0 : 0 : 0; is equally valid and appears in most library models; the lecture slide shows only the 1 case.為什麼會有第 3 列((0?) 1 : 1 : 1)。這是正反器的降低悲觀列。時脈從 0 變成了某個不是 0 的值 — 要嘛是乾淨的 1(已被第 2 列涵蓋),要嘛是 x。如果變成 x,我們不知道是否真的發生了一個邊緣。但如果 data = 1 而且正反器本來就保持著 1,那麼兩種可能都給出 1:邊緣是真的,我們擷取到 1;邊緣不是真的,我們保持 1。所以輸出維持 1,未知的時脈沒有造成傷害。對稱的那一列 (0?) 0 : 0 : 0; 同樣成立,而且出現在大多數單元庫模型中;講義投影片只列了 1 的情況。なぜ第 3 行((0?) 1 : 1 : 1)があるのか。これはフリップフロップにおける悲観性低減の行である。クロックは 0 から 0 でない何かへ変化した — きれいな 1(これは第 2 行が覆う)か、x かである。x になった場合、本当にエッジが起きたのかは分からない。しかし data = 1 でかつフリップフロップがすでに 1 を保持しているなら、どちらの可能性でも答えは 1 である。エッジが本物なら 1 を取り込み、そうでなければ 1 を保持する。よって出力は 1 のままで、不定のクロックは害を及ぼさない。対称な行 (0?) 0 : 0 : 0; も同様に正しく、ほとんどのライブラリモデルに入っている。講義スライドは 1 の場合しか挙げていない。

Slide discrepancy worth noting值得一提的投影片落差触れておく価値のあるスライドとの食い違い

The slide lists only (0?) 1 : 1 : 1; and not its complement (0?) 0 : 0 : 0;. The table is still correct — an incomplete pessimism-reduction set only costs precision, never correctness — but it is asymmetric for no stated reason. In an exam, reproducing the slide is safe; in a model you ship, write both rows.投影片只列了 (0?) 1 : 1 : 1;,沒有列它的補集 (0?) 0 : 0 : 0;。這張表仍然正確 — 降低悲觀的列列得不齊只會損失精度,不會損失正確性 — 但它在沒有給出理由的情況下是不對稱的。考試時照抄投影片是安全的;但你要出貨的模型,兩列都要寫。スライドは (0?) 1 : 1 : 1; だけを挙げ、その対である (0?) 0 : 0 : 0; を挙げていない。テーブルは依然として正しい — 悲観性低減の行が不完全でも失われるのは精度だけで正しさではない — が、理由が示されないまま非対称になっている。試験ではスライドを再現すれば安全だが、出荷するモデルでは両方の行を書くこと。

4.5 Edge-specifier rules4.5 邊緣指定子的規則4.5 エッジ指定子の規則

4.6 Level-sensitive rows beat edge-sensitive rows4.6 準位敏感列勝過邊緣敏感列4.6 レベル感応の行はエッジ感応の行に勝つ

This is the one priority rule in the UDP world, and the slides do not mention it. When a sequential UDP contains both level-sensitive rows (no edge field anywhere) and edge-sensitive rows, and an event matches one of each, the level-sensitive row wins.這是 UDP 世界裡唯一的優先權規則,而投影片沒有提到它。當一個循序邏輯 UDP 同時含有準位敏感列(任何欄位都沒有邊緣)與邊緣敏感列,而某個事件同時對應到兩者各一列時,準位敏感的那一列勝出。これは UDP の世界で唯一の優先規則であり、スライドは触れていない。順序 UDP がレベル感応の行(どのフィールドにもエッジがない)とエッジ感応の行の両方を含み、あるイベントがその両方に一致するとき、レベル感応の行が勝つ。

The rule exists for asynchronous set and clear. Consider a flip-flop with active-low clear:這條規則是為了非同步設定與清除而存在的。考慮一顆低準位有效清除的正反器:この規則は非同期セットとクリアのために存在する。ローアクティブのクリアを持つフリップフロップを考える。

  table
  //  clk    d    clr_n :  state :  q+
       ?     ?     0    :   ?   :  0;    // LEVEL row: clear dominates
      (01)   0     1    :   ?   :  0;    // EDGE rows
      (01)   1     1    :   ?   :  1;
  endtable

Suppose the clock rises at exactly the moment clr_n is already 0. The event is the clock edge, so row 2 or 3 is a candidate; row 1 is also a candidate because clr_n = 0 is a level condition that is true. Without the precedence rule you would have an ambiguous table. With it, the clear wins, which is what the silicon does. Note that the edge rows in the listing above also carry clr_n = 1 in their field, which already makes them mutually exclusive with row 1 — belt and braces, and the style you should copy.假設時脈上升的那一刻 clr_n 已經是 0。事件是時脈邊緣,所以第 2 或第 3 列是候選;第 1 列也是候選,因為 clr_n = 0 是一個為真的準位條件。沒有這條優先權規則,你會得到一張有歧義的表。有了它,清除勝出,而這正是矽晶片的行為。注意上面那段程式碼裡的邊緣列也在欄位裡帶著 clr_n = 1,這本身就已經讓它們與第 1 列互斥了 — 雙重保險,也是你該照抄的寫法。clr_n がすでに 0 のちょうどその瞬間にクロックが立ち上がったとする。イベントはクロックエッジなので第 2 行か第 3 行が候補になり、clr_n = 0 は真であるレベル条件なので第 1 行も候補になる。優先規則がなければ曖昧なテーブルである。規則があればクリアが勝ち、それはシリコンの振る舞いと一致する。上のリストのエッジ行がいずれもフィールドに clr_n = 1 を持っており、それだけですでに第 1 行と相互排他になっていることにも注意。二重の備えであり、まねるべき書き方である。

Practice 4 — Trace the latch練習 4 — 追蹤閂鎖器演習 4 — ラッチを追跡する

Problem: Using the three-row latch UDP (no pessimism-reduction rows), and assuming q_out starts at x with no initial, give q_out at t = 5, 13, 22, 35 and 46 for the stimulus in the figure above. Then say what changes at t = 22 if the design adds the two rows x 1 : 1 : 1; and x 0 : 0 : 0; and the enable is briefly x during its falling transition at t = 20.題目:使用三列的 latch UDP(沒有降低悲觀的列),並假設沒有 initial、q_out 從 x 開始,對上圖的激勵訊號,寫出 t = 5、13、22、35、46 時的 q_out。接著說明:如果設計加上 x 1 : 1 : 1; 與 x 0 : 0 : 0; 兩列,而 enable 在 t = 20 的下降轉態期間短暫為 x,t = 22 時會有什麼不同。問題:三行の latch UDP(悲観性低減の行なし)を用い、initial がなく q_out が x から始まるとして、上図の刺激に対する t = 5、13、22、35、46 の q_out を答えよ。さらに、設計に x 1 : 1 : 1; と x 0 : 0 : 0; の二行を加え、enable が t = 20 の立ち下がり遷移中に一瞬 x になる場合、t = 22 で何が変わるかを述べよ。

Click to reveal solution點擊顯示解答クリックで解答を表示

t = 5: x. Enable has been 0 the whole time, so every evaluation hits the hold row and the reg never leaves its initial x. This is why library latch models are the first place unknowns accumulate at power-up.t = 5:x。enable 一直是 0,所以每次求值都落在保持列,reg 從未離開它最初的 x。這就是為什麼單元庫的閂鎖器模型是上電時未知值最先堆積的地方。t = 5:x。enable はずっと 0 なので、評価は毎回保持の行に当たり、reg は最初の x から動かない。電源投入時に不定が最初に溜まる場所がライブラリのラッチモデルであるのは、このためである。

t = 13: 1. Enable went to 1 at t = 10 (giving q = 0 via row 2, the first time the reg acquires a known value), then data rose at t = 12 and row 1 drove it to 1.t = 13:1。enable 在 t = 10 變成 1(經由第 2 列給出 q = 0,這是 reg 第一次取得已知值),接著 data 在 t = 12 上升,第 1 列把它驅動成 1。t = 13:1。enable が t = 10 で 1 になり(第 2 行により q = 0。reg が既知の値を得る最初の瞬間である)、続いて t = 12 に data が立ち上がり、第 1 行が 1 に駆動する。

t = 22: 1. Data was 1 when enable fell at t = 20, so the hold row preserves 1 even though data falls at t = 24.t = 22:1。enable 在 t = 20 下降時 data 是 1,所以即使 data 在 t = 24 下降,保持列仍然保住 1。t = 22:1。t = 20 で enable が立ち下がったとき data は 1 だったので、t = 24 に data が立ち下がっても保持の行が 1 を守る。

t = 35: 1. Enable rose at 30 with data = 0 giving q = 0; the data pulse from 33 to 36 makes the transparent latch follow, so at t = 35 (inside the pulse) q = 1.t = 35:1。enable 在 30 上升時 data = 0,給出 q = 0;33 到 36 的資料脈衝讓透通的閂鎖器跟著走,所以在 t = 35(脈衝之內)q = 1。t = 35:1。t = 30 で enable が立ち上がったとき data = 0 なので q = 0 になり、33 から 36 のデータパルスに透過状態のラッチが追従するので、t = 35(パルスの内側)では q = 1 である。

t = 46: 0. Enable fell at t = 40 with data = 0, capturing 0; the pulse at 44–47 is blocked.t = 46:0。enable 在 t = 40 下降時 data = 0,擷取到 0;44–47 的脈衝被擋下。t = 46:0。t = 40 で enable が立ち下がったとき data = 0 なので 0 を取り込み、44〜47 のパルスは遮断される。

With the extra rows and an x on enable at t = 20: at that instant data = 1 and the state is already 1, so the new row x 1 : 1 : 1 matches and q_out stays 1 instead of going to x. Without those rows, a glitchy or unknown enable edge corrupts the latch even though the data was stable through it — exactly the pessimism that makes reset sequences in gate-level simulation fail to converge.加上那兩列、且 enable 在 t = 20 為 x 時:那一瞬間 data = 1 而狀態已經是 1,所以新的 x 1 : 1 : 1 列對應成功,q_out 維持 1 而不會變成 x。少了那兩列,即使資料全程穩定,一個帶毛刺或未知的 enable 邊緣也會毀掉閂鎖器 — 正是這種悲觀讓閘層級模擬的重置序列無法收斂。二行を加え、t = 20 で enable が x の場合:その瞬間 data = 1 で状態もすでに 1 なので、新しい行 x 1 : 1 : 1 が一致し、q_out は x にならず 1 のままである。二行がなければ、データがその間ずっと安定していても、グリッチや不定の enable エッジがラッチを壊してしまう — ゲートレベルシミュレーションのリセットシーケンスが収束しなくなる、まさにその悲観性である。

Practice 5 — A falling-edge D flip-flop with asynchronous clear練習 5 — 附非同步清除的負緣觸發 D 型正反器演習 5 — 非同期クリア付き立ち下がりエッジ D フリップフロップ

Problem: Write a sequential UDP dff_neg_clr with port order (q, clk, d, clr_n): negative-edge triggered, asynchronous active-low clear. Make sure no two rows can both match, and explain the row that handles the release of the clear.題目:寫一個埠順序為 (q, clk, d, clr_n) 的循序邏輯 UDP dff_neg_clr:負緣觸發、非同步低準位有效清除。確認沒有任兩列會同時對應,並解釋處理清除解除的那一列。問題:ポート順が (q, clk, d, clr_n) の順序 UDP dff_neg_clr を書け。立ち下がりエッジトリガ、非同期ローアクティブクリアとする。二つの行が同時に一致しないことを確かめ、クリアの解除を扱う行について説明せよ。

Click to reveal solution點擊顯示解答クリックで解答を表示
primitive dff_neg_clr (q, clk, d, clr_n);
  output q;
  input  clk, d, clr_n;
  reg    q;
  table
  //  clk     d    clr_n  :  state :  q+
       ?      ?     0     :   ?   :  0;   // 1. clear asserted: level row, dominates
       ?      ?    (?1)   :   ?   :  -;   // 2. clear released: hold, do not clock
      (10)    0     1     :   ?   :  0;   // 3. falling edge captures 0
      (10)    1     1     :   ?   :  1;   // 4. falling edge captures 1
      (1x)    ?     1     :   ?   :  -;   // 5. ambiguous edges: stay put
      (x0)    ?     1     :   ?   :  -;   // 6.
      (0?)    ?     1     :   ?   :  -;   // 7. rising clock: hold
       ?     (??)   1     :   ?   :  -;   // 8. data moves, clock steady: hold
  endtable
endprimitive

Mutual exclusion. Rows 3–8 all require clr_n = 1; row 1 requires clr_n = 0; row 2 requires a transition to 1 on clr_n, which is an event on a different input and therefore cannot coincide with the clock or data events of rows 3–8. No two rows can match the same event.互斥性。第 3–8 列都要求 clr_n = 1;第 1 列要求 clr_n = 0;第 2 列要求 clr_n 上有一個往 1 的轉態,那是另一個輸入上的事件,因此不可能與第 3–8 列的時脈或資料事件同時發生。沒有任兩列能對應到同一個事件。相互排他性。第 3〜8 行はいずれも clr_n = 1 を要求し、第 1 行は clr_n = 0 を要求する。第 2 行は clr_n 上の 1 への遷移を要求するが、これは別の入力のイベントなので、第 3〜8 行のクロックやデータのイベントと同時には起こり得ない。同じイベントに二つの行が一致することはない。

Row 2, the release row, is the one people forget. When clr_n goes from 0 to 1, that is a change on an input of the UDP, so the table is evaluated. Rows 3–8 all require an event on clk or d, so none of them matches this event. Row 1 no longer matches because clr_n is now 1. Without row 2 the event would fall through to the default and the flip-flop would go to x the instant you release reset — a spectacular and very common bug. The (?1) rather than (01) also covers the x→1 release that happens when reset itself comes out of an unknown.第 2 列,也就是解除列,才是大家會忘記的那一列。當 clr_n 由 0 變 1 時,那是 UDP 某個輸入上的變化,所以表格會被求值。第 3–8 列都要求 clk 或 d 上有事件,所以它們都不對應這個事件。第 1 列也不再對應,因為 clr_n 現在是 1。少了第 2 列,這個事件就會落入預設,正反器會在你解除重置的那一瞬間變成 x — 一個很壯觀也很常見的錯誤。寫 (?1) 而不是 (01),還額外涵蓋了重置本身從未知值出來時的 x→1 解除。第 2 行、すなわち解除の行こそ、みなが忘れる行である。clr_n が 0 から 1 になるのは UDP の入力の変化なので、テーブルが評価される。第 3〜8 行はいずれも clk か d のイベントを要求するので、このイベントには一致しない。第 1 行も clr_n がいま 1 なので一致しない。第 2 行がなければ、このイベントは既定に落ち、リセットを解除した瞬間にフリップフロップが x になる — 派手で、きわめてありふれたバグである。(01) ではなく (?1) と書くことで、リセット自体が不定から復帰するときの x→1 の解除も覆える。

Rows 5 and 6 are the optimistic treatment of unknown clock edges: a 1→x or x→0 transition might or might not be a real falling edge, and these rows choose to hold rather than to corrupt. A pessimistic library would write : x; instead of : -; there. Which you choose is a modelling policy decision, and vendors document it.第 5、6 列是對未知時脈邊緣的樂觀處理:1→x 或 x→0 的轉態可能是、也可能不是真正的下降緣,而這兩列選擇保持而不是把值毀掉。悲觀的單元庫會在那裡寫 : x; 而不是 : -;。選哪一種是一個建模政策決定,供應商會把它寫進文件。第 5・6 行は不定のクロックエッジに対する楽観的な扱いである。1→x や x→0 の遷移は本物の立ち下がりエッジかもしれないしそうでないかもしれないが、これらの行は値を壊すのではなく保持することを選んでいる。悲観的なライブラリならそこに : -; ではなく : x; と書く。どちらを選ぶかはモデリング方針の決定であり、ベンダはそれを文書化している。

5. Gate Delay and the Inertial Model5. 閘延遲與慣性延遲模型5. ゲート遅延と慣性遅延モデル

Verilog has three places you can attach a propagation delay, and they are not alternatives — they stack. The lecture names them:Verilog 有三個地方可以掛上傳播延遲,而且它們不是三選一 — 它們會疊加。講義把它們命名如下:Verilog には伝搬遅延を付けられる場所が三つあり、それらは択一ではなく積み重なる。講義はそれらを次のように呼んでいる。

Layer層層Written as寫法書き方Belongs to屬於帰属先Lecture's label講義的稱呼講義での呼称
Gate delay閘延遲ゲート遅延and #3 (y, a, b);The primitive instance (the driver)原型實體(驅動器)プリミティブのインスタンス(ドライバ)inertial delay慣性延遲慣性遅延
Net delaynet 延遲net 遅延wire #2 y;The net (the interconnect)net(繞線)net(配線)transport delay傳輸延遲伝送遅延
Module path delay模組路徑延遲モジュールパス遅延specify (a => y) = 3; endspecifyThe module, pin to pin模組,腳對腳モジュール、ピンからピンへ—

This section takes the first layer apart. Sections 7 and 8 take the other two, and section 9 puts them back together.本節把第一層拆開來看。第 7 節與第 8 節處理另外兩層,第 9 節再把它們組回去。本節では第一の層を分解する。7 節と 8 節が残る二つを扱い、9 節で組み直す。

5.1 Rise, fall and turn-off: the three delays5.1 上升、下降與關斷:三種延遲5.1 立ち上がり・立ち下がり・ターンオフ:三つの遅延

Verilog does not have "a" propagation delay. It has up to three, selected by the destination value of the transition, not by the direction in any intuitive sense:Verilog 沒有「那個」傳播延遲。它最多有三個,而且是依轉態的目的值來選,不是依任何直覺意義上的方向:Verilog に「唯一の」伝搬遅延というものはない。最大三つあり、しかもそれを選ぶのは遷移の行き先の値であって、直感的な意味での向きではない。

Name名稱名称Symbol符號記号Fires on在什麼時候觸發発火する条件
Rising delay上升延遲立ち上がり遅延$d_r$Any transition to 1: 0→1, x→1, z→1任何變成 1 的轉態:0→1、x→1、z→11 へのあらゆる遷移:0→1、x→1、z→1
Falling delay下降延遲立ち下がり遅延$d_f$Any transition to 0: 1→0, x→0, z→0任何變成 0 的轉態:1→0、x→0、z→00 へのあらゆる遷移:1→0、x→0、z→0
Turn-off delay關斷延遲ターンオフ遅延$d_z$Any transition to z: 0→z, 1→z, x→z任何變成 z 的轉態:0→z、1→z、x→zz へのあらゆる遷移:0→z、1→z、x→z

Read those definitions carefully: the rule is "where you are going", never "where you came from". A transition x→1 uses the rising delay even though x is not 0. This matters in gate-level simulation because the first real transition of every net after power-up is x→something, and it is timed with the rise or fall delay accordingly.請仔細讀這些定義:規則是「你要去哪裡」,絕不是「你從哪裡來」。x→1 的轉態用的是上升延遲,即使 x 並不是 0。這在閘層級模擬中很重要,因為上電之後每一條 net 的第一個真實轉態都是 x→某個值,而它就依此用上升或下降延遲計時。これらの定義は注意して読むこと。規則は「どこへ行くか」であって、「どこから来たか」では決してない。x→1 の遷移は x が 0 でなくても立ち上がり遅延を使う。これはゲートレベルシミュレーションで重要になる。電源投入後、あらゆる net の最初の実際の遷移は x→何か であり、それに応じて立ち上がりか立ち下がりの遅延で時刻が決まるからだ。

Turn-off delay only makes sense for a gate that can drive high impedance. That is why the plain logic primitives accept at most two delay values and the three-state family accepts three:關斷延遲只對能驅動高阻抗的閘有意義。這就是為什麼單純的邏輯原型最多接受兩個延遲值,而三態家族接受三個:ターンオフ遅延は高インピーダンスを駆動できるゲートにしか意味がない。だからこそ単純な論理プリミティブは遅延値を最大二つしか受け付けず、三状態系は三つを受け付ける。

and               (y, x1, x2);      // no delay: output changes in the same time step
and #2            (y, x1, x2);      // one value: all transitions take 2
and #1.45         (y, x1, x2);      // a real number: rounded to the module's precision
and #(2,3)        (y, x1, x2);      // (rise, fall)
bufif1 #(3,4,1)   (out, in, ctrl);  // (rise, fall, turn-off)
bufif1 #(3:4:5, 6:7:8, 5:8:9) (out, in, ctrl);   // each one a min:typ:max triple

Writing and #(2,3,4) is an error: an AND gate can never produce z, so there is no transition for the third number to describe. Simulators reject it.寫成 and #(2,3,4) 是錯誤的:AND 閘永遠不可能產生 z,所以沒有任何轉態可以讓第三個數字去描述。模擬器會拒絕它。and #(2,3,4) と書くのは誤りである。AND ゲートは z を出せないので、第三の数値が記述すべき遷移が存在しない。シミュレータはこれを拒否する。

The bufif1 truth table, and where L and H come frombufif1 的真值表,以及 L 與 H 從哪裡來bufif1 の真理値表と、L・H の出どころ

bufif1: in \ ctrlbufif1:in \ ctrlbufif1:in \ ctrl01xz
0z0LL
1z1HH
xzxxx
zzxxx

L means "0 or z" and H means "1 or z". They are not storable net values; they are a compact way of saying "the gate is either driving the stated value or not driving at all, and I cannot tell which". A net receiving L from one driver and 0 from another resolves to 0; a net receiving L and 1 resolves to x. These intermediate symbols exist precisely so that bus resolution can still produce a definite answer when one driver's enable is unknown — the same pessimism-reduction philosophy as §3.3, applied at the net level instead of the table level.L 代表「0 或 z」,H 代表「1 或 z」。它們不是可以儲存在 net 上的值;它們是「這個閘要嘛正在驅動所說的值、要嘛根本沒在驅動,而我分不出是哪一種」的精簡寫法。一條 net 若從某個驅動器收到 L、從另一個收到 0,會解析成 0;收到 L 與 1 則解析成 x。這些中間符號存在的目的,正是為了讓匯流排在某個驅動器的致能未知時仍能解析出確定答案 — 與 §3.3 相同的降低悲觀哲學,只是套用在 net 層級而不是表格層級。L は「0 または z」、H は「1 または z」を意味する。これらは net に保持できる値ではなく、「このゲートは述べられた値を駆動しているか、まったく駆動していないかのどちらかで、どちらかは分からない」を簡潔に言い表す記法である。あるドライバから L を、別のドライバから 0 を受けた net は 0 に解決し、L と 1 を受けた net は x に解決する。これらの中間記号が存在するのは、あるドライバのイネーブルが不定でもバスの解決が確定した答えを出せるようにするためである — §3.3 と同じ悲観性低減の思想を、テーブルのレベルではなく net のレベルに適用したものだ。

5.2 The delay mapping table5.2 延遲對應表5.2 遅延マッピング表

Given one, two or three delay values, which one times a given transition? The lecture's mapping table answers it exhaustively.給定一個、兩個或三個延遲值,某個特定轉態該用哪一個?講義的對應表把這件事窮舉完畢。一つ、二つ、あるいは三つの遅延値が与えられたとき、ある遷移にはどれが使われるのか。講義のマッピング表がそれを網羅的に答えている。

from \ to由 \ 到遷移元 \ 遷移先01xz
0—$d_r$$d^{*}$$d_z$
1$d_f$—$d^{*}$$d_z$
x$d_f$$d_r$—$d^{**}$
z$d_f$$d_r$$d^{*}$—
Transitions into x, and x into z變成 x 的轉態,以及 x 變成 zx への遷移、および x から z への遷移 $$\text{two delays: } d^{*} = d^{**} = \min(d_r, d_f)$$ $$\text{three delays: } d^{*} = \min(d_r, d_f, d_z), \qquad d^{**} = d_z$$

Three observations make this table memorable instead of arbitrary.有三點觀察能讓這張表變得好記,而不只是一堆任意規定。三つの観察によって、この表は恣意的な決まりごとではなく覚えやすいものになる。

The diagonal is empty. A "transition" from a value to itself is not a transition; nothing is scheduled, no delay applies. The simulator discards it before the delay machinery is reached.對角線是空的。從一個值「轉態」到它自己並不是轉態;什麼都不會被排程,也沒有延遲可言。模擬器在延遲機制被用到之前就把它丟掉了。対角線は空である。ある値から同じ値への「遷移」は遷移ではない。何もスケジュールされず、遅延も適用されない。シミュレータは遅延の機構に届く前にそれを捨てる。

Columns 0, 1 and z are uniform. Whatever the starting value, going to 1 costs $d_r$, going to 0 costs $d_f$, going to z costs $d_z$. That is just the §5.1 definition restated — the destination decides.0、1、z 這三欄是一致的。不管起始值是什麼,去 1 要 $d_r$,去 0 要 $d_f$,去 z 要 $d_z$。那不過是把 §5.1 的定義重述一遍 — 目的地決定一切。0、1、z の三列は一様である。開始値が何であれ、1 へ行くなら $d_r$、0 へ行くなら $d_f$、z へ行くなら $d_z$ である。これは §5.1 の定義の言い換えにすぎない — 行き先がすべてを決める。

The x column uses the minimum. This is the only interesting entry, and it is a deliberate pessimism choice. When a gate's output becomes unknown, the simulator does not know which physical transition is actually under way, so it assumes the output could become uncertain as early as any of them could take effect. Making the unknown window start at the earliest possible moment and (via the next transition's own delay) end at the latest possible moment produces the widest x region — which is the safe direction for a model whose purpose is to expose uncertainty rather than hide it.x 那一欄取最小值。這是唯一有趣的欄位,而且是刻意的悲觀選擇。當一個閘的輸出變成未知時,模擬器並不知道實際上正在進行的是哪一種物理轉態,所以它假設輸出可能在其中任何一種生效的最早時刻就變得不確定。讓未知視窗在最早的可能時刻開始,並(透過下一個轉態本身的延遲)在最晚的可能時刻結束,產生的 x 區域最寬 — 對一個目的在於暴露不確定性而非隱藏它的模型來說,那是安全的方向。x の列は最小値を使う。これが唯一興味深い項目であり、意図的な悲観の選択である。ゲートの出力が不定になるとき、実際に進行している物理的遷移がどれなのかシミュレータには分からない。そこで、どの遷移が効くにせよ、その中で最も早い時刻に出力が不確かになりうると仮定する。不定の窓を可能な限り早く始め、(次の遷移自身の遅延によって)可能な限り遅く終わらせれば、x の領域は最も広くなる — 不確かさを隠すのではなく露わにすることを目的とするモデルにとって、それが安全な方向である。

The one asymmetry is $d^{**}$, the x→z entry. With three delays specified, this is $d_z$ rather than the minimum, because going to z is unambiguously a turn-off event: whatever the output was before, the gate is switching off and the turn-off delay is the right physical number. With only two delays there is no $d_z$ to use, so it falls back to $\min(d_r, d_f)$.唯一不對稱的是 $d^{**}$,也就是 x→z 那一格。指定三個延遲時,它是 $d_z$ 而不是最小值,因為變成 z 毫無疑問就是一個關斷事件:不管輸出之前是什麼,這個閘正在關掉,而關斷延遲就是正確的物理數字。只指定兩個延遲時沒有 $d_z$ 可用,於是退回 $\min(d_r, d_f)$。唯一の非対称は $d^{**}$、すなわち x→z の項目である。遅延を三つ指定した場合、これは最小値ではなく $d_z$ になる。z へ行くことは紛れもなくターンオフの事象だからだ。出力が以前何であれ、ゲートは切れつつあり、ターンオフ遅延こそ正しい物理量である。遅延が二つしかなければ使える $d_z$ がないので、$\min(d_r, d_f)$ に戻る。

Worked example — bufif1 #(3, 4, 1) (out, in, ctrl);實例 — bufif1 #(3, 4, 1) (out, in, ctrl);実例 — bufif1 #(3, 4, 1) (out, in, ctrl);
So $d_r = 3$, $d_f = 4$, $d_z = 1$, and $d^{*} = \min(3,4,1) = 1$, $d^{**} = d_z = 1$.所以 $d_r = 3$、$d_f = 4$、$d_z = 1$,而 $d^{*} = \min(3,4,1) = 1$、$d^{**} = d_z = 1$。よって $d_r = 3$、$d_f = 4$、$d_z = 1$ であり、$d^{*} = \min(3,4,1) = 1$、$d^{**} = d_z = 1$ となる。
0→1: destination 1 → $d_r = \mathbf{3}$.0→1:目的地為 1 → $d_r = \mathbf{3}$。0→1:行き先は 1 → $d_r = \mathbf{3}$。
1→0: destination 0 → $d_f = \mathbf{4}$.1→0:目的地為 0 → $d_f = \mathbf{4}$。1→0:行き先は 0 → $d_f = \mathbf{4}$。
1→z (the enable deasserts): destination z → $d_z = \mathbf{1}$.1→z(致能解除):目的地為 z → $d_z = \mathbf{1}$。1→z(イネーブルのディアサート):行き先は z → $d_z = \mathbf{1}$。
0→x: destination x → $d^{*} = \mathbf{1}$.0→x:目的地為 x → $d^{*} = \mathbf{1}$。0→x:行き先は x → $d^{*} = \mathbf{1}$。
x→z: the special entry → $d^{**} = d_z = \mathbf{1}$.x→z:特例欄位 → $d^{**} = d_z = \mathbf{1}$。x→z:特別な項目 → $d^{**} = d_z = \mathbf{1}$。
z→1 (the enable asserts onto a 1): destination 1 → $d_r = \mathbf{3}$.z→1(致能加上去而輸入為 1):目的地為 1 → $d_r = \mathbf{3}$。z→1(イネーブルがアサートされ、入力が 1):行き先は 1 → $d_r = \mathbf{3}$。
For and #(2,3) there is no $d_z$, so a 1→x transition uses $d^{*} = \min(2,3) = \mathbf{2}$.對 and #(2,3) 而言沒有 $d_z$,所以 1→x 的轉態用 $d^{*} = \min(2,3) = \mathbf{2}$。and #(2,3) には $d_z$ がないので、1→x の遷移は $d^{*} = \min(2,3) = \mathbf{2}$ を使う。

5.3 min:typ:max and how a triple is chosen5.3 min:typ:max,以及一組三元值是怎麼被選中的5.3 min:typ:max と、三つ組の選ばれ方

Every one of the one, two or three delay values may itself be written as a colon-separated triple min:typ:max. So the maximal gate delay specification carries nine numbers:一個、兩個或三個延遲值中的每一個,本身都可以寫成用冒號分隔的三元組 min:typ:max。所以最完整的閘延遲規格帶有九個數字:一つ、二つ、三つのいずれの遅延値も、それ自体をコロン区切りの三つ組 min:typ:max として書ける。したがって最大限のゲート遅延指定は九個の数値を持つ。

bufif1 #(3:4:5, 6:7:8, 5:8:9) (out, in, ctrl);
//       rise      fall     turn-off
//     min:typ:max  ...        ...

These correspond to the process/voltage/temperature corners the cell was characterised at: min is the fast corner (fast process, high voltage, often low temperature), typ the nominal silicon, max the slow corner. Exactly one of the three is used for a given simulation run, and the choice is made at elaboration time for the whole design, not per instance. Simulators expose it as a command-line switch:它們對應到該單元被特性化時的製程/電壓/溫度角落:min 是快角(快製程、高電壓,通常也是低溫),typ 是標稱矽,max 是慢角。每一次模擬只會用到三者之一,而且這個選擇是在 elaboration 時對整個設計一次決定的,不是逐實體決定。模擬器把它做成命令列開關:これらはセルが特性化されたプロセス/電圧/温度のコーナーに対応する。min は高速コーナー(高速プロセス、高電圧、多くは低温)、typ は公称シリコン、max は低速コーナーである。一回のシミュレーションで使われるのはそのうちちょうど一つであり、選択は設計全体についてエラボレーション時に行われる。インスタンスごとではない。シミュレータはこれをコマンドラインのスイッチとして提供している。

// typical invocation forms (tool-dependent)
vcs  +mindelays  ...        // use the first number of every triple
vcs  +typdelays  ...        // the default
vcs  +maxdelays  ...        // use the third number
vsim -t 1ps -sdfmax /dut=chip.sdf  ...
You cannot mix corners in one run同一次模擬不能混用角落一回の実行でコーナーを混ぜることはできない

A common misconception is that min:typ:max gives the simulator a range to reason about, or that it will somehow check both. It does not. One number is selected from every triple and the run is a single-corner run. Verifying both corners means running the simulation twice. This is also why you cannot catch a hold violation and a setup violation in the same pass: setup failures appear at the slow corner, hold failures at the fast corner.常見的誤解是以為 min:typ:max 給了模擬器一個區間可以推理,或是以為它會把兩邊都檢查一遍。並沒有。每一組三元值都只挑出一個數字,整場模擬就是單一角落的模擬。要驗證兩個角落就得跑兩次。這也是為什麼你不可能在同一趟裡同時抓到 hold 違規與 setup 違規:setup 失效出現在慢角,hold 失效出現在快角。よくある誤解は、min:typ:max がシミュレータに推論すべき範囲を与えるとか、両方を自動で検査してくれるという思い込みである。そうではない。三つ組ごとに一つの数値が選ばれ、実行は単一コーナーの実行になる。両方のコーナーを検証するにはシミュレーションを二回走らせるしかない。セットアップ違反とホールド違反を同じ一回で捕まえられないのもこのためである。セットアップの失敗は低速コーナーに、ホールドの失敗は高速コーナーに現れる。

A detail that trips people up: if a triple is given in only some positions, the others still need to be well-formed. #(3:4:5, 6) is legal — the fall delay is 6 at every corner. And a single #3:4:5 with no parentheses applies that triple to all transitions.一個常絆倒人的細節:如果只在部分位置給了三元值,其餘位置仍必須寫得合法。#(3:4:5, 6) 是合法的 — 下降延遲在每個角落都是 6。而沒有括號的單一 #3:4:5 會把那組三元值套用到所有轉態。人をつまずかせる細部がある。一部の位置にだけ三つ組を書いた場合でも、残りの位置は正しい形でなければならない。#(3:4:5, 6) は合法である — 立ち下がり遅延はどのコーナーでも 6 だ。括弧のない単独の #3:4:5 は、その三つ組をすべての遷移に適用する。

5.4 Inertial delay, mechanically5.4 慣性延遲的機制5.4 慣性遅延の仕組み

"Gate delay is inertial" is usually stated and never explained. Here is the mechanism, in terms of the event queue from Lecture 2.「閘延遲是慣性的」這句話通常只被陳述而從不被解釋。以下是它的機制,用第 2 講的事件佇列來說明。「ゲート遅延は慣性である」は述べられるだけで説明されないことが多い。以下がその仕組みであり、第 2 講のイベントキューの言葉で述べる。

  1. An input of the gate changes. The simulator evaluates the gate's function and obtains a candidate output value $v$.閘的某個輸入改變。模擬器對閘的函數求值,得到一個候選輸出值 $v$。ゲートの入力が変化する。シミュレータはゲートの関数を評価し、候補となる出力値 $v$ を得る。
  2. If $v$ equals the value currently scheduled for the output (or the output's present value, when nothing is pending), nothing happens.如果 $v$ 等於目前已排程給該輸出的值(或在沒有待決事件時,等於輸出的現值),什麼都不會發生。$v$ がその出力に対してすでにスケジュールされている値(保留中のイベントがなければ出力の現在値)と等しければ、何も起こらない。
  3. Otherwise the simulator computes the delay $d$ from the mapping table and wants to schedule "output becomes $v$" at time $t + d$.否則模擬器由對應表算出延遲 $d$,並打算在時間 $t + d$ 排程「輸出變成 $v$」。そうでなければ、シミュレータはマッピング表から遅延 $d$ を求め、時刻 $t + d$ に「出力が $v$ になる」をスケジュールしようとする。
  4. Before scheduling, it looks for an event already pending on that output. If one exists and has not yet executed, that pending event is cancelled and replaced by the new one.在排程之前,它會先去找該輸出上已經待決的事件。如果有一個存在而且尚未執行,那個待決事件會被取消,由新的取代。スケジュールする前に、その出力に保留中のイベントがないかを探す。存在していてまだ実行されていなければ、その保留中のイベントは取り消され、新しいイベントに置き換えられる。

Step 4 is the entire inertial model. Nothing explicitly measures pulse width; the pulse-swallowing behaviour is an emergent property of "only one output transition may be in flight at a time, and the newest one wins".第 4 步就是慣性模型的全部。沒有任何地方明確量測脈衝寬度;吞掉脈衝的行為是「同一時間最多只能有一個輸出轉態在飛行中,而且最新的那個獲勝」所湧現出來的性質。手順 4 が慣性モデルのすべてである。パルス幅を明示的に測るところはどこにもない。パルスを飲み込む振る舞いは、「出力の遷移は同時に一つしか飛んでいられず、最新のものが勝つ」から創発する性質である。

Pulse survival through a gate脈衝能否通過一個閘パルスがゲートを通過できる条件 $$\text{pulse } 0\!\to\!1\!\to\!0 \text{ of width } w \text{ survives} \iff w > d_r, \qquad w_{\text{out}} = w + d_f - d_r$$ $$\text{pulse } 1\!\to\!0\!\to\!1 \text{ of width } w \text{ survives} \iff w > d_f, \qquad w_{\text{out}} = w + d_r - d_f$$

The governing quantity is the delay of the leading edge, because that is the event which must already have executed by the time the trailing edge arrives — if it is still pending, it is cancelled and the pulse vanishes. With a symmetric delay $d_r = d_f = d$ both lines reduce to the familiar rule: survives iff $w > d$, and the surviving pulse keeps its original width. Widths exactly equal to the delay land in the same time slot as the pending event and are implementation-defined.決定一切的量是前緣的延遲,因為那正是在後緣到達之時必須已經執行完畢的事件 — 如果它還在待決,就會被取消,脈衝隨之消失。在對稱延遲 $d_r = d_f = d$ 之下,兩條式子都化約成我們熟悉的規則:$w > d$ 時存活,而存活下來的脈衝保有原來的寬度。寬度恰好等於延遲時,會落在與待決事件相同的時間槽,屬於實作定義的行為。すべてを決めるのは先行エッジの遅延である。後続エッジが到着する時点までに実行済みでなければならないのがそのイベントだからだ — まだ保留中なら取り消され、パルスは消える。対称な遅延 $d_r = d_f = d$ ではどちらの式もおなじみの規則に帰着する。$w > d$ のとき生き残り、生き残ったパルスは元の幅を保つ。幅がちょうど遅延に等しい場合は保留中のイベントと同じ時間スロットに落ち、実装依存となる。

Worked example — asymmetric delay widens or narrows a pulse實例 — 不對稱延遲會把脈衝變寬或變窄実例 — 非対称な遅延はパルスを広げも狭めもする
Take buf #(2, 5) (y, a); — rise 2, fall 5 — and a positive input pulse from t = 10 to t = 20, so $w = 10$.取 buf #(2, 5) (y, a); — 上升 2、下降 5 — 以及一個從 t = 10 到 t = 20 的正脈衝輸入,所以 $w = 10$。buf #(2, 5) (y, a); — 立ち上がり 2、立ち下がり 5 — と、t = 10 から t = 20 までの正パルス入力をとる。したがって $w = 10$ である。
The input rise at t = 10 schedules y→1 at $10 + 2 = 12$. No event is pending, so it stands.t = 10 的輸入上升把 y→1 排在 $10 + 2 = 12$。此時沒有待決事件,所以它成立。t = 10 の入力の立ち上がりが y→1 を $10 + 2 = 12$ にスケジュールする。保留中のイベントはないのでそのまま成立する。
The input fall at t = 20 schedules y→0 at $20 + 5 = 25$. The earlier event at t = 12 has already executed, so nothing is cancelled.t = 20 的輸入下降把 y→0 排在 $20 + 5 = 25$。t = 12 的較早事件已經執行過了,所以沒有任何東西被取消。t = 20 の入力の立ち下がりが y→0 を $20 + 5 = 25$ にスケジュールする。t = 12 の先行イベントはすでに実行済みなので、何も取り消されない。
Output pulse: t = 12 to t = 25, width 13. The pulse was widened by $d_f - d_r = 3$.輸出脈衝:t = 12 到 t = 25,寬度 13。脈衝被加寬了 $d_f - d_r = 3$。出力パルス:t = 12 から t = 25、幅 13。パルスは $d_f - d_r = 3$ だけ広げられた。
Now shrink the input pulse to $w = 2$ (t = 10 to t = 12). Rise schedules y→1 at 12; fall at t = 12 schedules y→0 at 17. At t = 12 the rise event is being processed in the same time step as the new input event, and the two events are in the same time slot — the simulator's ordering here is not defined by the standard, which is why some tools show a zero-width glitch and some show nothing at all.現在把輸入脈衝縮成 $w = 2$(t = 10 到 t = 12)。上升把 y→1 排在 12;t = 12 的下降把 y→0 排在 17。在 t = 12,上升事件與新的輸入事件在同一個時間步被處理,兩個事件落在同一個時間槽 — 模擬器在這裡的排序並非標準所定義,這就是為什麼有些工具顯示一個零寬度的毛刺,有些什麼都不顯示。次に入力パルスを $w = 2$(t = 10 から t = 12)に縮める。立ち上がりが y→1 を 12 に、t = 12 の立ち下がりが y→0 を 17 にスケジュールする。t = 12 では立ち上がりイベントが新しい入力イベントと同じ時間ステップで処理され、二つのイベントは同じ時間スロットに入る — ここでのシミュレータの順序は標準が定めていない。だからこそ、幅ゼロのグリッチを表示するツールもあれば、何も表示しないツールもある。
Shrink further to $w = 1$ (t = 10 to t = 11). Rise is pending for t = 12; at t = 11 the fall computes y→0 at t = 16 and cancels the pending rise. The output never moves. The pulse is swallowed.再縮成 $w = 1$(t = 10 到 t = 11)。上升待決於 t = 12;在 t = 11,下降算出 y→0 要排在 t = 16,並取消那個待決的上升。輸出從頭到尾沒有動。脈衝被吞掉了。さらに $w = 1$(t = 10 から t = 11)に縮める。立ち上がりは t = 12 に保留中である。t = 11 で立ち下がりが y→0 を t = 16 に算出し、保留中の立ち上がりを取り消す。出力は一度も動かない。パルスは飲み込まれた。

Note the subtlety in the middle step: a pulse whose width is exactly equal to the delay lands on a tool-dependent boundary. In an exam, use "width $\lt$ delay ⇒ swallowed, width $\gt$ delay ⇒ passed" and say explicitly that the equality case is implementation-defined. In a design, never build anything that depends on a pulse of width equal to a cell delay.注意中間那一步的微妙之處:寬度恰好等於延遲的脈衝落在一個依工具而異的邊界上。考試時請用「寬度 $\lt$ 延遲 ⇒ 被吞掉、寬度 $\gt$ 延遲 ⇒ 通過」,並明白寫出相等的情況屬於實作定義。在設計中,絕對不要做出任何依賴「脈衝寬度等於單元延遲」的東西。中間の段階の微妙さに注意すること。幅がちょうど遅延に等しいパルスは、ツールに依存する境界に落ちる。試験では「幅 $\lt$ 遅延 ⇒ 飲み込まれる、幅 $\gt$ 遅延 ⇒ 通過する」を使い、等しい場合は実装依存であると明示すること。設計においては、セル遅延に等しい幅のパルスに依存するものを決して作らないこと。

5.5 Inertial versus transport5.5 慣性延遲與傳輸延遲的對照5.5 慣性遅延と伝送遅延の対比

a y (inertial) gate #4 y (transport) shifted #4 4 w = 2 2 < 4: swallowed 051015 2530 t
The same stimulus through a 4-unit delay. Inertial (gate) delay cancels the pending output event when the input reverses too soon, so the 2-unit pulse at t = 18 never reaches the output. A pure transport delay shifts every edge by 4 and reproduces the narrow pulse at t = 22–24.同一組激勵通過 4 單位的延遲。慣性(閘)延遲會在輸入太快反轉時取消待決的輸出事件,所以 t = 18 的 2 單位脈衝從未抵達輸出。純粹的傳輸延遲則把每個邊緣平移 4,並重現 t = 22–24 的窄脈衝。同じ刺激を 4 単位の遅延に通したもの。慣性(ゲート)遅延は入力が早く反転しすぎると保留中の出力イベントを取り消すので、t = 18 の 2 単位のパルスは出力に届かない。純粋な伝送遅延はすべてのエッジを 4 だけ平行移動し、t = 22〜24 の細いパルスを再現する。

The wide pulse (t = 2 to t = 10, width 8) survives both models identically: the output goes high at 6 and low at 14. The narrow pulse (t = 18 to t = 20, width 2) is where they diverge. Inertially, the rise scheduled for t = 22 is cancelled by the fall arriving at t = 20, and the output stays flat. With a transport delay the two edges are independent and both appear, shifted by 4.寬脈衝(t = 2 到 t = 10,寬度 8)在兩個模型下存活得一模一樣:輸出在 6 變高、在 14 變低。窄脈衝(t = 18 到 t = 20,寬度 2)才是兩者分道揚鑣的地方。在慣性模型下,排在 t = 22 的上升被 t = 20 抵達的下降取消,輸出保持平坦。在傳輸延遲下,兩個邊緣各自獨立,都會出現,只是平移了 4。幅の広いパルス(t = 2 から t = 10、幅 8)は両方のモデルでまったく同じように生き残る。出力は 6 で高く、14 で低くなる。両者が分かれるのは幅の狭いパルス(t = 18 から t = 20、幅 2)である。慣性モデルでは t = 22 に予定された立ち上がりが t = 20 に到着する立ち下がりによって取り消され、出力は平坦なままになる。伝送遅延では二つのエッジは独立で、どちらも 4 だけ移動して現れる。

Is net delay really transport? Be careful herenet 延遲真的是傳輸延遲嗎?這裡要小心net 遅延は本当に伝送遅延か。ここは慎重に

The slides label gate delay "inertial" and net delay "transport", following the textbook. The first half is unambiguous and is in the standard. The second half is a teaching simplification that you should understand precisely.投影片沿用教科書的說法,把閘延遲標為「慣性」、net 延遲標為「傳輸」。前半毫無疑義,而且寫在標準裡。後半則是一個教學上的簡化,你應該精確地理解它。スライドは教科書に倣い、ゲート遅延を「慣性」、net 遅延を「伝送」と記している。前半は曖昧さがなく、標準にも書かれている。後半は教育上の単純化であり、正確に理解しておくべきである。

What is certainly true, and what the label is reaching for: a net delay is applied to a waveform that its driver has already filtered. The gate does the pulse rejection; the net then shifts the result. The net does not re-filter with its own window in the way a second gate in series would, and the slide's waveform on page 22 illustrates that composition.確定為真、也正是那個標籤想表達的是:net 延遲所作用的波形,已經被它的驅動器先過濾過了。脈衝的濾除是閘做的;net 只是把結果平移。net 並不會像串聯的第二個閘那樣用自己的視窗再濾一次,而投影片第 22 頁的波形示範的就是這個組合。確実に正しく、このラベルが言おうとしているのは次のことである。net 遅延が作用する波形は、そのドライバによってすでにフィルタされている。パルスの除去はゲートが行い、net はその結果を平行移動するだけだ。net は、直列に置かれた二つ目のゲートのように自分の窓で再度フィルタすることはない。スライド 22 ページの波形が示しているのはこの合成である。

What is not safe to assume: that a net delay will faithfully transport a pulse narrower than itself. IEEE 1364 does not guarantee pure transport semantics for net delays, and mainstream simulators apply inertial-style filtering there too. If you genuinely need transport behaviour — a delay line, a channel model, a skew generator — the portable construct is an intra-assignment delay in procedural code, always @(a) y <= #4 a;, where the non-blocking assignment samples the right-hand side immediately and schedules the update independently, so every edge survives. Do not build a delay line out of wire #4 and expect it to work the same way in two different simulators.不能安心假設的是:net 延遲會忠實地傳輸一個比它自己還窄的脈衝。IEEE 1364 並不保證 net 延遲具有純粹的傳輸語意,而主流模擬器在那裡同樣套用慣性式的濾除。如果你真的需要傳輸行為 — 延遲線、通道模型、偏斜產生器 — 可攜的寫法是在程序式程式碼中使用指定內延遲:always @(a) y <= #4 a;,非阻斷指定會立刻取樣右手邊並獨立排程更新,於是每個邊緣都能存活。不要用 wire #4 蓋一條延遲線,然後期待它在兩個不同的模擬器裡表現一致。安全に仮定できないのは、net 遅延が自分より狭いパルスを忠実に伝送するということである。IEEE 1364 は net 遅延に純粋な伝送意味論を保証しておらず、主要なシミュレータはそこでも慣性的なフィルタを適用する。本当に伝送の振る舞いが必要なら — 遅延線、チャネルモデル、スキュー生成器など — 移植性のある構文は手続き記述の代入内遅延、すなわち always @(a) y <= #4 a; である。ノンブロッキング代入は右辺を直ちにサンプルして更新を独立にスケジュールするので、すべてのエッジが生き残る。wire #4 で遅延線を作り、それが二つの異なるシミュレータで同じように動くと期待してはならない。

5.6 Why inertial is the right default — and when it lies5.6 為什麼慣性延遲是正確的預設 — 以及它什麼時候在說謊5.6 なぜ慣性遅延が正しい既定値か — そしてそれが嘘をつくとき

Inertial delay is a model of a physical fact: a real gate has capacitance, and a pulse that does not last long enough to charge or discharge the output node does not produce a full-swing transition at the output. A 20 ps pulse at the input of a cell whose propagation delay is 60 ps genuinely does not appear at the output. Verilog's rule is a crude but correct first-order approximation of that.慣性延遲建模的是一個物理事實:真實的閘有電容,而一個持續時間不足以把輸出節點充飽或放完的脈衝,不會在輸出產生一次全擺幅的轉態。一顆傳播延遲為 60 ps 的單元,其輸入上的 20 ps 脈衝確實不會出現在輸出。Verilog 的規則是對此粗糙但正確的一階近似。慣性遅延がモデル化しているのは一つの物理的事実である。実際のゲートには容量があり、出力ノードを充電あるいは放電しきるだけ持続しないパルスは、出力にフルスイングの遷移を生じさせない。伝搬遅延 60 ps のセルの入力に現れた 20 ps のパルスは、実際に出力には現れない。Verilog の規則はそれに対する粗いが正しい一次近似である。

Where the model lies, and you should know these:這個模型在哪些地方說謊,你應該要知道:このモデルがどこで嘘をつくかは知っておくべきである。

Practice 6 — Pick the delay練習 6 — 選出該用的延遲演習 6 — どの遅延を選ぶか

Problem: For notif0 #(6, 9, 2) (out, in, ctrl_n); state which delay value times each of the following output transitions, showing the mapping-table reasoning: (a) 0→1; (b) x→0; (c) 1→z; (d) z→x; (e) x→z; (f) 1→1. Then repeat (b), (d) and (e) for xor #(6, 9) (y, a, b);.題目:對 notif0 #(6, 9, 2) (out, in, ctrl_n);,說出下列每一個輸出轉態由哪一個延遲值計時,並寫出對應表的推理:(a) 0→1;(b) x→0;(c) 1→z;(d) z→x;(e) x→z;(f) 1→1。接著對 xor #(6, 9) (y, a, b); 重做 (b)、(d) 與 (e)。問題:notif0 #(6, 9, 2) (out, in, ctrl_n); について、次の各出力遷移がどの遅延値で時刻づけられるかを、マッピング表に基づく理由とともに答えよ。(a) 0→1、(b) x→0、(c) 1→z、(d) z→x、(e) x→z、(f) 1→1。続いて xor #(6, 9) (y, a, b); について (b)、(d)、(e) を繰り返せ。

Click to reveal solution點擊顯示解答クリックで解答を表示

Here $d_r = 6$, $d_f = 9$, $d_z = 2$, so $d^{*} = \min(6,9,2) = 2$ and $d^{**} = d_z = 2$.這裡 $d_r = 6$、$d_f = 9$、$d_z = 2$,所以 $d^{*} = \min(6,9,2) = 2$ 且 $d^{**} = d_z = 2$。ここでは $d_r = 6$、$d_f = 9$、$d_z = 2$ なので、$d^{*} = \min(6,9,2) = 2$、$d^{**} = d_z = 2$ である。

(a) 6. Destination is 1 → rising delay.(a) 6。目的地是 1 → 上升延遲。(a) 6。行き先は 1 → 立ち上がり遅延。

(b) 9. Destination is 0 → falling delay. The fact that the source is x is irrelevant; only the destination selects.(b) 9。目的地是 0 → 下降延遲。來源是 x 這件事無關緊要;只有目的地決定選用哪一個。(b) 9。行き先は 0 → 立ち下がり遅延。遷移元が x であることは無関係で、選択するのは行き先だけである。

(c) 2. Destination is z → turn-off delay.(c) 2。目的地是 z → 關斷延遲。(c) 2。行き先は z → ターンオフ遅延。

(d) 2. Destination is x → $d^{*} = \min(6,9,2) = 2$.(d) 2。目的地是 x → $d^{*} = \min(6,9,2) = 2$。(d) 2。行き先は x → $d^{*} = \min(6,9,2) = 2$。

(e) 2. This is the special $d^{**}$ cell. With three delays specified it is $d_z = 2$ — which happens to equal $d^{*}$ here, so write the reasoning out rather than the number, or you will not get the credit.(e) 2。這是特殊的 $d^{**}$ 格。指定了三個延遲時它等於 $d_z = 2$ — 在這裡剛好與 $d^{*}$ 相等,所以請把推理寫出來而不是只寫數字,否則拿不到分數。(e) 2。これは特別な $d^{**}$ の項目である。遅延を三つ指定した場合は $d_z = 2$ であり、ここではたまたま $d^{*}$ と一致する。数値だけでなく理由を書くこと。さもないと点はもらえない。

(f) No delay at all. It is not a transition; nothing is scheduled and the diagonal of the mapping table is empty.(f) 完全沒有延遲。它根本不是轉態;什麼都不會被排程,而對應表的對角線是空的。(f) 遅延は一切ない。そもそも遷移ではない。何もスケジュールされず、マッピング表の対角線は空である。

For xor #(6, 9): there is no $d_z$, so $d^{*} = d^{**} = \min(6,9) = 6$. (b) still 9 (destination 0). (d) 6. (e) cannot happen — an XOR primitive never drives z, so the question is vacuous; if it appeared on an exam, say so.對 xor #(6, 9) 而言:沒有 $d_z$,所以 $d^{*} = d^{**} = \min(6,9) = 6$。(b) 仍是 9(目的地為 0)。(d) 6。(e) 不可能發生 — XOR 原型永遠不會驅動 z,所以這一題是空的;如果它出現在考卷上,就這樣寫。xor #(6, 9) については $d_z$ がないので $d^{*} = d^{**} = \min(6,9) = 6$ となる。(b) は依然 9(行き先は 0)。(d) は 6。(e) は起こり得ない — XOR プリミティブは z を駆動しないので、この問いは空虚である。試験に出たらそう書けばよい。

Practice 7 — Swallow the glitch練習 7 — 吞掉毛刺演習 7 — グリッチを飲み込む

Problem: A two-input AND with and #5 (y, a, b) sees a = 1 throughout, and b with the waveform: 0 until t = 10, 1 from t = 10 to t = 13, 0 from 13 to 30, 1 from 30 onwards. (a) Draw y. (b) Now change the gate to and #(5, 2). Draw y again. (c) Explain in terms of pending-event cancellation why the answers differ. (d) What does a VCD-based toggle-count power estimate report in case (a) versus what the silicon actually dissipates?題目:一個兩輸入 AND,寫成 and #5 (y, a, b),全程 a = 1,而 b 的波形為:t = 10 之前為 0,t = 10 到 t = 13 為 1,13 到 30 為 0,30 之後為 1。(a) 畫出 y。(b) 現在把閘改成 and #(5, 2),再畫一次 y。(c) 用待決事件取消的角度解釋為什麼答案不同。(d) 在情況 (a) 下,以 VCD 翻轉次數為基礎的功耗估算會回報什麼,而矽晶片實際消耗的又是什麼?問題:二入力 AND を and #5 (y, a, b) とし、a = 1 が全期間続く。b の波形は、t = 10 まで 0、t = 10 から t = 13 まで 1、13 から 30 まで 0、30 以降は 1 である。(a) y を描け。(b) ゲートを and #(5, 2) に変えて y を描き直せ。(c) 保留イベントの取り消しという観点から、答えが異なる理由を説明せよ。(d) (a) の場合、VCD のトグル数に基づく電力見積もりは何を報告し、シリコンは実際に何を消費するか。

Click to reveal solution點擊顯示解答クリックで解答を表示

(a) and #5. At t = 10 the output value becomes 1, scheduled for t = 15. At t = 13 the output value becomes 0, scheduled for t = 18; the pending t = 15 event has not executed yet, so it is cancelled. Nothing happens at 15 or 18. At t = 30 the rise is scheduled for t = 35 and nothing cancels it. So y is 0 until t = 35 and 1 thereafter — the 3-unit pulse is completely invisible.(a) and #5。在 t = 10,輸出值變成 1,排程在 t = 15。在 t = 13,輸出值變成 0,排程在 t = 18;而 t = 15 的待決事件尚未執行,所以它被取消。15 與 18 都什麼都沒發生。在 t = 30,上升排程在 t = 35,沒有東西取消它。所以 y 在 t = 35 之前是 0,之後是 1 — 那個 3 單位的脈衝完全看不見。(a) and #5。t = 10 で出力値が 1 になり t = 15 にスケジュールされる。t = 13 で出力値が 0 になり t = 18 にスケジュールされるが、t = 15 の保留イベントはまだ実行されていないので取り消される。15 でも 18 でも何も起こらない。t = 30 で立ち上がりが t = 35 にスケジュールされ、これを取り消すものはない。したがって y は t = 35 まで 0、以後 1 であり、3 単位のパルスはまったく見えない。

(b) and #(5, 2). Unchanged: the output stays 0. The fall delay is irrelevant here. What decides the fate of a positive pulse is the rising delay, because the rise is the event that must already have executed when the fall arrives. At t = 13 the rise scheduled for t = 15 is still pending, so it is cancelled, and the replacement fall event writes a 0 onto an output that is already 0. The survival condition is $w > d_r$, i.e. $3 > 5$, which is false. Making the gate and #(2, 5) instead would let the pulse through: the rise executes at t = 12, the fall is scheduled at $13 + 5 = 18$, and the output carries a pulse of width $w + d_f - d_r = 3 + 5 - 2 = 6$ — six units wide from a three-unit input pulse.(b) and #(5, 2)。不變:輸出維持 0。這裡下降延遲無關緊要。決定一個正脈衝命運的是上升延遲,因為上升才是下降到達時必須已經執行完畢的事件。在 t = 13,排在 t = 15 的上升仍然待決,所以被取消,而取代它的下降事件把 0 寫到一個本來就是 0 的輸出上。存活條件是 $w > d_r$,也就是 $3 > 5$,不成立。若改成 and #(2, 5) 就能讓脈衝通過:上升在 t = 12 執行,下降排在 $13 + 5 = 18$,輸出帶著一個寬度 $w + d_f - d_r = 3 + 5 - 2 = 6$ 的脈衝 — 三單位的輸入脈衝變成六單位寬。(b) and #(5, 2)。変わらない。出力は 0 のままである。ここでは立ち下がり遅延は無関係だ。正パルスの運命を決めるのは立ち上がり遅延である。立ち下がりが到着する時点で実行済みでなければならないのが立ち上がりのイベントだからだ。t = 13 では t = 15 に予定された立ち上がりがまだ保留中なので取り消され、代わりの立ち下がりイベントは、すでに 0 である出力に 0 を書く。生存条件は $w > d_r$、すなわち $3 > 5$ であり偽である。代わりに and #(2, 5) にすればパルスは通る。立ち上がりは t = 12 に実行され、立ち下がりは $13 + 5 = 18$ にスケジュールされ、出力には幅 $w + d_f - d_r = 3 + 5 - 2 = 6$ のパルスが現れる — 三単位の入力パルスが六単位の幅になる。

(c) The gate keeps at most one transition in flight. Every new input event recomputes the output and, if the result differs from what is pending, overwrites the pending event rather than queueing a second one. Pulse rejection is a side effect of that single-slot rule, not a width measurement.(c) 閘在任何時刻最多只保留一個飛行中的轉態。每一個新的輸入事件都會重算輸出,而如果結果與待決的值不同,就覆蓋掉那個待決事件,而不是再排一個。脈衝濾除是這條「單一槽位」規則的副作用,不是在量測寬度。(c) ゲートが飛行中に保持できる遷移は高々一つである。新しい入力イベントごとに出力が再計算され、結果が保留中のものと異なれば、二つ目をキューに入れるのではなく保留イベントを上書きする。パルスの除去はこの単一スロット規則の副作用であって、幅の測定ではない。

(d) The VCD contains two transitions on y in the whole run (the one at t = 35, plus whatever happened before t = 10), so the toggle count — and therefore the estimated dynamic power on that net — omits the glitch entirely. In silicon the output node was partially charged and discharged during the 3-unit pulse and dissipated real energy. Glitch power is a well-known gap between gate-level estimates and measurement, and in glitch-prone arithmetic (ripple carries, array multipliers) it can be a double-digit percentage of the total.(d) 整場模擬中 VCD 在 y 上只有兩次轉態(t = 35 那一次,加上 t = 10 之前發生的任何變化),所以翻轉次數 — 以及由此估出的該 net 動態功耗 — 完全漏掉了那個毛刺。在矽晶片裡,輸出節點在那 3 單位的脈衝期間確實被部分充放電,也確實消耗了能量。毛刺功耗是閘層級估算與實測之間廣為人知的落差,而在容易產生毛刺的算術電路(漣波進位、陣列乘法器)中,它可以佔總量的兩位數百分比。(d) 実行全体を通じて VCD に記録される y の遷移は二回だけであり(t = 35 のものと、t = 10 以前に起きた分)、トグル数は — したがってその net の推定動的電力も — グリッチを完全に見落とす。シリコンでは、出力ノードはその 3 単位のパルスのあいだ部分的に充放電され、実際にエネルギーを消費している。グリッチ電力はゲートレベルの見積もりと実測のあいだのよく知られたギャップであり、グリッチの出やすい演算回路(リプルキャリー、アレイ乗算器)では総量の二桁パーセントに達することもある。

6. Timescale, Precision and the Rounding Trap6. 時間刻度、精度,以及四捨五入的陷阱6. タイムスケール、精度、そして丸めの落とし穴

Every # in Verilog is a pure number. #3 means three time units, and nothing in the expression says what a time unit is. The `timescale compiler directive supplies the missing information, and because it is a compiler directive rather than a language construct, it behaves in ways that surprise people.Verilog 裡的每一個 # 都只是一個純數字。#3 表示三個時間單位,而表示式本身完全沒說一個時間單位是多少。`timescale 這個編譯指令補上了這段缺失的資訊,而由於它是編譯指令而不是語言結構,它的行為常讓人意外。Verilog の # はどれも純粋な数値にすぎない。#3 は三時間単位を意味するが、式のどこにも時間単位が何であるかは書かれていない。`timescale コンパイラ指令がこの欠けた情報を補う。そしてこれが言語構造ではなくコンパイラ指令であるため、その振る舞いは人を驚かせる。

6.1 Unit and precision6.1 單位與精度6.1 単位と精度

`timescale <time_unit> / <time_precision>
`timescale 1ns / 10ps       // #1 means 1 ns; delays are rounded to 10 ps
`timescale 10ps / 1ps       // #1 means 10 ps; delays are rounded to 1 ps
What a delay expression actually becomes一個延遲表示式實際會變成什麼遅延式が実際に何になるか $$t_{\text{abs}} \;=\; \operatorname{round}\!\left(\frac{N \cdot U}{P}\right) \cdot P$$

where $N$ is the number after the #, $U$ the module's time unit and $P$ its time precision. The rounding happens once, at compile time, in the module where the delay is written.其中 $N$ 是 # 後面的數字,$U$ 是該模組的時間單位,$P$ 是它的時間精度。四捨五入只發生一次,在編譯期,在寫下那個延遲的模組裡。ここで $N$ は # の後ろの数値、$U$ はそのモジュールの時間単位、$P$ はその時間精度である。丸めは一度だけ、コンパイル時に、その遅延が書かれたモジュールの中で起こる。

6.2 Rounding happens at compile time, in the local module6.2 四捨五入發生在編譯期,而且在本地模組裡6.2 丸めはコンパイル時に、ローカルなモジュールで起こる

`timescale 10ps / 1ps     nor #3.57 (z, x1, x2); 3.57 × 10 ps = 35.7 ps, but the time wheel only has 1 ps slots 30313233 34353637 383940 picoseconds (the precision grid) 35.7 ps rounds to 36 ps
The precision defines a grid. A delay that does not land on the grid is rounded to the nearest grid point, once, when the module is compiled — not at run time, and not in the units of whoever instantiates the module.精度定義出一張格線。沒有落在格線上的延遲會被四捨五入到最近的格點,而且只做一次,在該模組被編譯的時候 — 不是在執行期,也不是用實體化它的那一方的單位。精度はグリッドを定める。グリッド上に乗らない遅延は最も近い格子点へ丸められる。丸めは一度だけ、そのモジュールがコンパイルされるときに行われる — 実行時ではなく、インスタンス化する側の単位でもない。

Two consequences follow immediately and both are exam material.立刻有兩個後果,而且兩個都是考試題材。ここから直ちに二つの帰結が出る。どちらも試験に出る材料である。

Rounding is permanent. Once #3.57 has been rounded to 36 ps it is 36 ps forever. A later module with finer precision cannot recover the 35.7. If you want a 35.7 ps delay you must declare a precision of 100 fs or better in the module where the delay is written.四捨五入是永久的。#3.57 一旦被捨入成 36 ps,它就永遠是 36 ps。後面某個精度更細的模組救不回那個 35.7。如果你要 35.7 ps 的延遲,就必須在寫下該延遲的那個模組裡宣告 100 fs 或更細的精度。丸めは恒久的である。#3.57 がいったん 36 ps に丸められたら、それは永遠に 36 ps である。あとからより細かい精度のモジュールが来ても 35.7 は取り戻せない。35.7 ps の遅延が欲しければ、その遅延を書くモジュールで 100 fs 以上に細かい精度を宣言しなければならない。

Rounding can round to zero. Under `timescale 1ns/1ns, the delay #0.4 becomes #0. The gate becomes a zero-delay gate: it no longer swallows any pulses, it no longer breaks feedback loops, and if it was there to fix a race it has silently stopped doing so. Under `timescale 1ns/1ns an entire sub-nanosecond cell library collapses to zero delay and your post-layout simulation becomes a (very slow) zero-delay simulation that happens to pass.四捨五入可能捨到零。在 `timescale 1ns/1ns 之下,延遲 #0.4 變成 #0。那個閘就變成零延遲閘:它不再吞掉任何脈衝,不再切斷回授迴路,而如果它原本是用來修正某個競態的,它已經默默地停止那麼做了。在 `timescale 1ns/1ns 之下,整座次奈秒的單元庫都塌成零延遲,你的佈局後模擬就變成一場(非常慢的)零延遲模擬,而且剛好會通過。丸めはゼロになりうる。`timescale 1ns/1ns のもとでは遅延 #0.4 は #0 になる。そのゲートはゼロ遅延のゲートになり、もうパルスを飲み込まず、帰還ループも断ち切らず、競合の修正のために置かれていたのなら黙ってその役目をやめている。`timescale 1ns/1ns のもとでは、サブナノ秒のセルライブラリ全体がゼロ遅延に潰れ、レイアウト後シミュレーションは(きわめて遅い)ゼロ遅延シミュレーションになり、しかもたまたま合格してしまう。

The classic failure典型的失敗典型的な失敗

A cell library is characterised in picoseconds. The library .v file carries `timescale 1ns/10ps or finer. Someone writes a test bench with `timescale 1ns/1ns because the stimulus is in whole nanoseconds, and — crucially — forgets a `timescale in one of the design files, which then inherits the test bench's. Every delay in that file rounds to a whole nanosecond; sub-nanosecond delays round to zero. The simulation runs, produces no error, and is worthless. Always put a `timescale at the top of every file, and make the precision at least as fine as the smallest delay in the design.單元庫是以皮秒特性化的。單元庫的 .v 檔帶著 `timescale 1ns/10ps 或更細的設定。某人因為激勵都是整數奈秒,就用 `timescale 1ns/1ns 寫了測試平台,而且 — 這才是關鍵 — 在某個設計檔裡忘了寫 `timescale,於是那個檔案繼承了測試平台的設定。該檔案裡每一個延遲都被捨入成整數奈秒;次奈秒的延遲全部捨成零。模擬跑得起來,不報任何錯,而且毫無價值。永遠要在每一個檔案的最前面放一個 `timescale,並且讓精度至少細到設計中最小的延遲。セルライブラリはピコ秒で特性化されている。ライブラリの .v ファイルには `timescale 1ns/10ps かそれ以上に細かい指定が付いている。ある人が、刺激が整数ナノ秒だからと `timescale 1ns/1ns でテストベンチを書き、そして — ここが肝心だが — 設計ファイルの一つで `timescale を書き忘れ、そのファイルがテストベンチの設定を引き継ぐ。そのファイル中のすべての遅延が整数ナノ秒に丸められ、サブナノ秒の遅延はすべてゼロに丸められる。シミュレーションは走り、エラーも出さず、そして無価値である。すべてのファイルの先頭に必ず `timescale を置き、精度は設計中の最小の遅延と同じかそれ以上に細かくすること。

6.3 The two-module example, line by line6.3 兩個模組的例子,逐行解讀6.3 二つのモジュールの例を一行ずつ

Source原始碼ソース
`timescale 1 ns / 10 ps
module modA (y, x1, x2);
  input  x1, x2;
  output y;
  nand #(3.225, 4.237) (y, x1, x2);
endmodule

`timescale 10 ns / 10 ns
module modB ();
  reg  x1, x2;
  wire y;
  modA M1 (y, x1, x2);

  initial
    $monitor($time, " %f x1=%b x2=%b y=%b",
             $realtime, x1, x2, y);

  initial begin
    #5 x1 = 0; x2 = 0;
    #5 x2 = 1;
    #5 x1 = 1;
    #5 x2 = 0;
  end
endmodule
Output輸出出力
$time   $realtime    x1    x2    y
-------------------------------------
   0     0.000000   x=x   x=x   y=x
   5     5.000000   x1=0  x2=0  y=x
   5     5.323000   x1=0  x2=0  y=1
  10    10.000000   x1=0  x2=1  y=1
  15    15.000000   x1=1  x2=1  y=1
  15    15.424000   x1=1  x2=1  y=0
  20    20.000000   x1=1  x2=0  y=0
  20    20.323000   x1=1  x2=0  y=1

Everything in both columns is reported in modB's 10 ns unit, because $monitor is called from modB.兩欄中的所有數值都以 modB 的 10 ns 為單位回報,因為 $monitor 是從 modB 呼叫的。どちらの列の値も modB の 10 ns 単位で報告される。$monitor が modB から呼ばれているからである。

Work the numbers.把數字算一遍。数値を追ってみる。

Where 5.323 and 15.424 come from5.323 與 15.424 是怎麼來的5.323 と 15.424 の出どころ
modA's delays. Unit 1 ns, precision 10 ps. Rise: $3.225 \text{ ns} = 3225 \text{ ps}$, rounded to the 10 ps grid gives $3230 \text{ ps} = 3.23 \text{ ns}$. Fall: $4.237 \text{ ns} = 4237 \text{ ps} \to 4240 \text{ ps} = 4.24 \text{ ns}$.modA 的延遲。單位 1 ns、精度 10 ps。上升:$3.225 \text{ ns} = 3225 \text{ ps}$,捨入到 10 ps 的格線得到 $3230 \text{ ps} = 3.23 \text{ ns}$。下降:$4.237 \text{ ns} = 4237 \text{ ps} \to 4240 \text{ ps} = 4.24 \text{ ns}$。modA の遅延。単位 1 ns、精度 10 ps。立ち上がりは $3.225 \text{ ns} = 3225 \text{ ps}$ を 10 ps のグリッドに丸めて $3230 \text{ ps} = 3.23 \text{ ns}$。立ち下がりは $4.237 \text{ ns} = 4237 \text{ ps} \to 4240 \text{ ps} = 4.24 \text{ ns}$。
modB's stimulus. Unit 10 ns. The first #5 is $5 \times 10 = 50$ ns of absolute time. The display column labelled $time shows 5 because modB's unit is 10 ns and $50/10 = 5$.modB 的激勵。單位 10 ns。第一個 #5 是 $5 \times 10 = 50$ ns 的絕對時間。標示為 $time 的那一欄顯示 5,因為 modB 的單位是 10 ns,而 $50/10 = 5$。modB の刺激。単位は 10 ns。最初の #5 は絶対時間で $5 \times 10 = 50$ ns である。$time と記された列が 5 を表示するのは、modB の単位が 10 ns で $50/10 = 5$ だからである。
The first output transition. At absolute 50 ns the inputs become 0, 0, so the NAND output must go to 1: a rising transition, delay 3.23 ns. It lands at absolute $50 + 3.23 = 53.23$ ns. In modB's unit that is $53.23 / 10 = \mathbf{5.323}$. That is the number in the output.第一次輸出轉態。在絕對時間 50 ns,輸入變成 0, 0,所以 NAND 的輸出必須變成 1:上升轉態,延遲 3.23 ns。它落在絕對時間 $50 + 3.23 = 53.23$ ns。以 modB 的單位來看就是 $53.23 / 10 = \mathbf{5.323}$。那就是輸出裡的那個數字。最初の出力遷移。絶対時間 50 ns で入力が 0, 0 になるので、NAND の出力は 1 へ向かう。立ち上がり遷移で遅延は 3.23 ns、絶対時間 $50 + 3.23 = 53.23$ ns に着地する。modB の単位では $53.23 / 10 = \mathbf{5.323}$ である。出力に現れている数値がそれだ。
The fall at 15.424. At absolute 150 ns x1 becomes 1 with x2 already 1, so the NAND output falls: delay 4.24 ns, landing at 154.24 ns, i.e. $\mathbf{15.424}$ modB units.15.424 的下降。在絕對時間 150 ns,x1 變成 1 而 x2 本來就是 1,所以 NAND 的輸出下降:延遲 4.24 ns,落在 154.24 ns,也就是 $\mathbf{15.424}$ 個 modB 單位。15.424 の立ち下がり。絶対時間 150 ns で x1 が 1 になり、x2 はすでに 1 なので NAND の出力は立ち下がる。遅延 4.24 ns で 154.24 ns、すなわち modB 単位で $\mathbf{15.424}$ に着地する。
The rise at 20.323. At absolute 200 ns x2 returns to 0, the output rises again with the 3.23 ns delay, landing at 203.23 ns = 20.323 modB units. Consistent.20.323 的上升。在絕對時間 200 ns,x2 回到 0,輸出以 3.23 ns 的延遲再次上升,落在 203.23 ns = 20.323 個 modB 單位。一致。20.323 の立ち上がり。絶対時間 200 ns で x2 が 0 に戻り、出力は 3.23 ns の遅延で再び立ち上がり、203.23 ns = modB 単位で 20.323 に着地する。整合している。
No transition at 10. At absolute 100 ns the inputs become 0, 1 and the NAND output is still 1. No transition, no delay, no new line except the one $monitor prints because an input changed.在 10 沒有轉態。在絕對時間 100 ns,輸入變成 0, 1,而 NAND 的輸出仍是 1。沒有轉態、沒有延遲,也沒有新的一行 — 除了 $monitor 因為某個輸入改變而印出的那一行。10 では遷移がない。絶対時間 100 ns で入力が 0, 1 になるが、NAND の出力は 1 のままである。遷移も遅延もなく、新しい行も出ない — 入力が変化したために $monitor が印字する一行を除いては。

Notice what the example proves: the delay numbers were interpreted in modA's timescale, while the reported times are in modB's. Those are two different modules with two different timescales, and the simulator reconciles them through a single absolute time axis whose resolution is the finest precision anywhere in the design — here 10 ps.注意這個例子證明了什麼:延遲數字是以 modA 的時間刻度解讀的,而回報的時間卻是以 modB 的單位。那是兩個時間刻度不同的模組,而模擬器透過單一的絕對時間軸把它們調和起來,那條時間軸的解析度是設計中任何一處最細的精度 — 這裡是 10 ps。この例が何を証明しているかに注意すること。遅延の数値は modA のタイムスケールで解釈され、報告される時刻は modB の単位である。タイムスケールの異なる二つのモジュールであり、シミュレータは単一の絶対時間軸を介して両者を調停する。その時間軸の分解能は、設計中のどこかにある最も細かい精度 — ここでは 10 ps — である。

6.4 $time versus $realtime6.4 $time 與 $realtime 的對照6.4 $time と $realtime の対比

$time$realtime
Return type回傳型別戻り値の型64-bit integer64 位元整數64 ビット整数real (double)實數(double)実数(double)
Scaling換算スケーリングTo the time unit of the module that calls it換算到呼叫它的那個模組的時間單位呼び出したモジュールの時間単位へSame同左同じ
Rounding四捨五入丸めRounded to the nearest integer四捨五入到最接近的整數最も近い整数へ丸めるNot rounded不四捨五入丸めない
Format code格式碼書式コード%d or %t%d 或 %t%d または %t%f or %t%f 或 %t%f または %t

Both are scaled to the caller's unit; only $time rounds. In the example, the event at absolute 53.23 ns is reported by $time as 5 (because $5.323$ rounds to 5) and by $realtime as 5.323000. An event at 57 ns would be reported by $time as 6, not 5 — the standard says round, not truncate, and students who assume truncation get the boundary cases wrong.兩者都換算到呼叫者的單位;只有 $time 會四捨五入。在那個例子裡,絕對時間 53.23 ns 的事件被 $time 回報成 5(因為 $5.323$ 捨入成 5),被 $realtime 回報成 5.323000。發生在 57 ns 的事件會被 $time 回報成 6 而不是 5 — 標準說的是四捨五入,不是截斷,而假設是截斷的學生會把邊界情況做錯。どちらも呼び出し側の単位にスケールされるが、丸めるのは $time だけである。先の例では、絶対時間 53.23 ns の事象を $time は 5 と報告し($5.323$ が 5 に丸められるため)、$realtime は 5.323000 と報告する。57 ns の事象は $time では 5 ではなく 6 と報告される — 標準が言うのは四捨五入であって切り捨てではない。切り捨てだと思い込んだ学生は境界の場合を間違える。

The practical advice: use %t with $timeformat rather than $time with %d. The combination實務上的建議:用 %t 搭配 $timeformat,而不是用 $time 搭配 %d。這個組合実務上の助言:$time と %d の組み合わせではなく、%t と $timeformat を使うこと。この組み合わせは

initial $timeformat(-12, 2, " ps", 10);   // units of 1e-12, 2 decimals, suffix, min field width
...
$display("%t : q = %b", $realtime, q);

prints absolute time in a fixed unit regardless of which module does the printing, which eliminates the entire class of "why does my log say 5 when the waveform says 53 ns" confusion.會以固定的單位印出絕對時間,不論印的是哪個模組,這就消除了「為什麼我的 log 寫 5 而波形寫 53 ns」這一整類的困惑。どのモジュールが印字しても固定の単位で絶対時間を出力する。「なぜログは 5 なのに波形は 53 ns なのか」という類の混乱がまるごと消える。

6.5 Cross-file hazards: why timescale silently changes results6.5 跨檔案的危險:為什麼 timescale 會默默改變結果6.5 ファイルをまたぐ危険:なぜタイムスケールは黙って結果を変えるのか

`timescale is a compiler directive, and compiler directives are not scoped to a file, a module, or anything else. They take effect from the point they are read until they are overridden or until `resetall. The consequences:`timescale 是一個編譯指令,而編譯指令的作用範圍不受檔案、模組或任何東西限制。它們從被讀到的那一點開始生效,直到被覆蓋,或直到遇上 `resetall。後果如下:`timescale はコンパイラ指令であり、コンパイラ指令はファイルにもモジュールにも、何にもスコープされない。読み込まれた地点から効力を持ち、上書きされるか `resetall に出会うまで続く。その帰結は次のとおりである。

House rule專案通則プロジェクトの決めごと

Put `timescale 1ns/1ps (or finer) at the top of every single file in the project, including test benches and including files you think have no delays. It costs one line and removes an entire failure class. If you are simulating a cell library characterised in picoseconds, the precision must be at least 1ps — and if the library uses three decimal places of picoseconds, make it 1fs.在專案中每一個檔案的最前面都放上 `timescale 1ns/1ps(或更細),包括測試平台,也包括你認為沒有延遲的檔案。這只花一行,卻消滅了一整類失效。如果你在模擬一座以皮秒特性化的單元庫,精度至少要是 1ps — 而如果單元庫用到皮秒的小數點後三位,就設成 1fs。プロジェクトのすべてのファイルの先頭に `timescale 1ns/1ps(かそれ以上に細かいもの)を置くこと。テストベンチも、遅延がないと思っているファイルも含めてである。一行の費用で失敗の一類型がまるごと消える。ピコ秒で特性化されたセルライブラリをシミュレートするなら精度は少なくとも 1ps であり、ライブラリがピコ秒の小数第三位まで使うなら 1fs にすること。

Practice 8 — Rounding arithmetic練習 8 — 四捨五入的算術演習 8 — 丸めの算術

Problem: (a) Under `timescale 1ns/100ps, what absolute delay results from #2.34, #0.06, and #(1.25, 3.449)? (b) Under `timescale 100ps/1ps, what does #0.455 become? (c) A module uses `timescale 1ns/1ns and instantiates a cell whose own file declares `timescale 1ps/1ps and contains #43. What is the cell delay in absolute time, and what is the simulation's global precision? (d) In a module with `timescale 10ns/1ns, an event occurs at absolute 52.3 ns, another at absolute 57.0 ns. What do $time and $realtime report for each?題目:(a) 在 `timescale 1ns/100ps 之下,#2.34、#0.06 與 #(1.25, 3.449) 各自產生多少絕對延遲? (b) 在 `timescale 100ps/1ps 之下,#0.455 會變成多少? (c) 某模組使用 `timescale 1ns/1ns,並實體化一顆單元,而那顆單元自己的檔案宣告 `timescale 1ps/1ps 且含有 #43。這個單元延遲的絕對時間是多少?整場模擬的全域精度又是多少? (d) 在一個使用 `timescale 10ns/1ns 的模組裡,有一個事件發生在絕對時間 52.3 ns,另一個在絕對時間 57.0 ns。$time 與 $realtime 對每一個會回報什麼?問題:(a) `timescale 1ns/100ps のもとで #2.34、#0.06、#(1.25, 3.449) はそれぞれどれだけの絶対遅延になるか。(b) `timescale 100ps/1ps のもとで #0.455 は何になるか。(c) あるモジュールが `timescale 1ns/1ns を使い、自身のファイルで `timescale 1ps/1ps を宣言し #43 を含むセルをインスタンス化している。セルの遅延は絶対時間で何か。またシミュレーション全体の精度は何か。(d) `timescale 10ns/1ns のモジュールで、絶対時間 52.3 ns に一つ、57.0 ns にもう一つ事象が起きる。$time と $realtime はそれぞれ何を報告するか。

Click to reveal solution點擊顯示解答クリックで解答を表示

(a) Unit 1 ns, precision 100 ps = 0.1 ns, so the grid is tenths of a nanosecond.(a) 單位 1 ns、精度 100 ps = 0.1 ns,所以格線是十分之一奈秒。(a) 単位は 1 ns、精度は 100 ps = 0.1 ns なので、グリッドは 0.1 ナノ秒刻みである。

  • #2.34 → $2.34$ rounds to $2.3$ → 2.3 ns.#2.34 → $2.34$ 捨入成 $2.3$ → 2.3 ns。#2.34 → $2.34$ は $2.3$ に丸められ → 2.3 ns。
  • #0.06 → rounds to $0.1$ → 0.1 ns. (Note it does not round to zero — 0.06 is nearer 0.1 than 0.0.)#0.06 → 捨入成 $0.1$ → 0.1 ns。(注意它並沒有捨成零 — 0.06 比起 0.0 更接近 0.1。)#0.06 → $0.1$ に丸められ → 0.1 ns。(ゼロには丸められないことに注意。0.06 は 0.0 より 0.1 に近い。)
  • #(1.25, 3.449) → rise $1.25 \to 1.3$ (round-half-up; some tools round-half-to-even and give 1.2, so avoid exact halves), fall $3.449 \to 3.4$. Rise 1.3 ns, fall 3.4 ns.#(1.25, 3.449) → 上升 $1.25 \to 1.3$(四捨五入;有些工具採銀行家捨入而給出 1.2,所以請避開剛好一半的值),下降 $3.449 \to 3.4$。上升 1.3 ns、下降 3.4 ns。#(1.25, 3.449) → 立ち上がりは $1.25 \to 1.3$(四捨五入。偶数丸めを行い 1.2 とするツールもあるので、ちょうど半分の値は避けること)、立ち下がりは $3.449 \to 3.4$。立ち上がり 1.3 ns、立ち下がり 3.4 ns。

(b) Unit 100 ps, precision 1 ps. $0.455 \times 100 = 45.5$ ps, rounded to the 1 ps grid → 46 ps (again an exact-half case, so flag it).(b) 單位 100 ps、精度 1 ps。$0.455 \times 100 = 45.5$ ps,捨入到 1 ps 的格線 → 46 ps(同樣是剛好一半的情況,請註明)。(b) 単位 100 ps、精度 1 ps。$0.455 \times 100 = 45.5$ ps を 1 ps のグリッドに丸めて → 46 ps(これもちょうど半分の場合なので、その旨を断っておくこと)。

(c) The cell's #43 is interpreted in its own file's unit of 1 ps, so it is 43 ps, and it is rounded to that file's 1 ps precision, which changes nothing. The instantiating module's 1 ns timescale is irrelevant to the cell's internal delays. The global simulation precision is the minimum over the design: 1 ps. This is exactly the right arrangement — and it is why a coarse test-bench timescale does not damage a library that carries its own directive. The damage only happens to files that carry none.(c) 那顆單元的 #43 是用它自己檔案的 1 ps 單位解讀的,所以是 43 ps,再捨入到該檔案的 1 ps 精度,結果不變。實體化它的那個模組的 1 ns 時間刻度,與這顆單元內部的延遲毫無關係。全域模擬精度取設計中的最小值:1 ps。這正是正確的安排 — 也是為什麼粗糙的測試平台時間刻度不會傷到自己帶著指令的單元庫。受害的只有完全沒帶指令的檔案。(c) セルの #43 はそのファイル自身の単位 1 ps で解釈されるので 43 ps であり、そのファイルの 1 ps 精度に丸めても変わらない。インスタンス化する側のモジュールの 1 ns タイムスケールは、セル内部の遅延とは無関係である。シミュレーション全体の精度は設計中の最小値、すなわち 1 ps である。これこそ正しい配置であり、粗いテストベンチのタイムスケールが、自前の指令を持つライブラリを壊さない理由でもある。被害を受けるのは指令をまったく持たないファイルだけだ。

(d) The module's unit is 10 ns.(d) 該模組的單位是 10 ns。(d) このモジュールの単位は 10 ns である。

  • 52.3 ns → $52.3/10 = 5.23$. $realtime = 5.23, $time = 5.52.3 ns → $52.3/10 = 5.23$。$realtime = 5.23,$time = 5。52.3 ns → $52.3/10 = 5.23$。$realtime = 5.23、$time = 5。
  • 57.0 ns → $5.70$. $realtime = 5.7, $time = 6 — rounded up, not truncated. A student who answers 5 here has made the classic error.57.0 ns → $5.70$。$realtime = 5.7,$time = 6 — 是進位,不是截斷。在這裡答 5 的學生就犯了那個經典錯誤。57.0 ns → $5.70$。$realtime = 5.7、$time = 6 — 切り捨てではなく繰り上げである。ここで 5 と答える学生が、例の典型的な誤りを犯している。

7. Net Delay: Delay That Belongs to the Wire7. net 延遲:屬於導線的延遲7. net 遅延:配線に属する遅延

7.1 Three things that look alike and are not7.1 三種長得很像、其實不同的東西7.1 似て非なる三つの書き方

A delay written near a net can mean three different things depending on where exactly it sits. Getting them confused is the single most common cause of wrong answers in this part of the course.寫在 net 附近的延遲,會依它確切坐落的位置而有三種不同的意義。把它們搞混,是課程這個部分答錯的最常見單一原因。net の近くに書かれた遅延は、正確にどこに置かれているかによって三つの異なる意味を持つ。これらを取り違えることが、この単元で答えを間違える最大の原因である。

wire #2 w;                      // (1) NET DELAY on a plain net declaration
wire #5 y = a ^ b;              // (2) NET DECLARATION ASSIGNMENT: the delay belongs to the assignment
wire z;
assign #3 z = a ^ b;            // (3) CONTINUOUS ASSIGNMENT delay: belongs to the driver
Form寫法書き方Where the delay lives延遲屬於誰遅延はどこに属するかComposes with與什麼疊加何と合成されるか
wire #2 w;The net itself. Whatever any driver puts on w appears two units later.net 本身。任何驅動器放到 w 上的東西,都會在兩個單位之後才出現。net 自身。どのドライバが w に置いたものも、2 単位後に現れる。Adds to every driver's own delay.會加到每一個驅動器自己的延遲上。各ドライバ自身の遅延に加算される。
wire #5 y = a ^ b;The implicit continuous assignment created by the declaration. There is no separate net delay.由該宣告所建立的隱含連續指定。沒有獨立的 net 延遲。その宣言が作る暗黙の連続代入。独立した net 遅延は存在しない。Nothing — the net has no delay of its own.沒有 — 這條 net 自己沒有延遲。何とも合成されない — この net 自身は遅延を持たない。
assign #3 z = ...;The continuous assignment (the driver).連續指定(也就是驅動器)。連続代入(すなわちドライバ)。Adds to any net delay declared on z.會加到宣告在 z 上的任何 net 延遲。z に宣言された net 遅延に加算される。

Form (2) is the trap. wire #5 y = a ^ b; looks as though it declares a net with a 5-unit delay and drives it, but the standard says the delay in a net declaration assignment belongs to the assignment. The net contributes zero. That is the whole content of the my_xor1 / my_xor2 comparison in §7.3.寫法 (2) 是陷阱。wire #5 y = a ^ b; 看起來好像同時宣告了一條有 5 單位延遲的 net 並且驅動它,但標準說 net 宣告指定中的延遲屬於那個指定。net 的貢獻是零。這就是 §7.3 中 my_xor1 / my_xor2 比較的全部內容。罠なのは書き方 (2) である。wire #5 y = a ^ b; は 5 単位の遅延を持つ net を宣言しかつそれを駆動しているように見えるが、標準は net 宣言代入における遅延は代入に属すると定めている。net の寄与はゼロだ。§7.3 の my_xor1 と my_xor2 の比較は、まるごとこの内容である。

Net delays are also where a subtlety about multiple drivers appears. If three drivers contend for one net, the net's resolution function runs first and the resolved value is then delayed. A net delay is not per-driver; it is one delay on one wire. A gate delay, by contrast, is per-instance.net 延遲也是多驅動器那個微妙之處浮現的地方。如果三個驅動器搶同一條 net,net 的解析函數會先執行,然後被延遲的是解析後的值。net 延遲不是逐驅動器的;它是一條導線上的一個延遲。相對地,閘延遲是逐實體的。net 遅延は、複数ドライバに関する微妙な点が表れる場所でもある。三つのドライバが一つの net を奪い合う場合、まず net の解決関数が走り、遅延されるのは解決後の値である。net 遅延はドライバごとではなく、一本の配線に対する一つの遅延である。対照的にゲート遅延はインスタンスごとである。

7.2 The slide waveform, built from first principles7.2 從第一原理重建講義的波形7.2 スライドの波形を原理から組み立てる

wire #2 y_tran;                     // the net carries its own 2-unit delay
and  #3 (y_tran,    x1, x2);        // gate delay 3, then the net adds 2
buf  #1 (buf_out,   y_tran);        // a further 1 after the net
and  #3 (y_inertial, x1, x2);       // identical gate, but on a plain net
x1x2 y_inertialy_tranbuf_out net #2 buf #1 0246 8101214 t
Both AND gates see the same input pulse [2, 6). y_inertial sits on a plain net and shows [5, 9). y_tran sits on a net declared wire #2, so the same waveform appears two units later at [7, 11). The buffer adds one more: [8, 12).兩個 AND 閘看到的是同一個輸入脈衝 [2, 6)。y_inertial 坐在一條普通的 net 上,顯示為 [5, 9)。y_tran 坐在一條宣告為 wire #2 的 net 上,所以同一個波形晚兩個單位出現,在 [7, 11)。緩衝器再加一個單位:[8, 12)。二つの AND ゲートは同じ入力パルス [2, 6) を見る。y_inertial は普通の net に乗っており [5, 9) を示す。y_tran は wire #2 と宣言された net に乗っているので、同じ波形が 2 単位遅れて [7, 11) に現れる。バッファがさらに 1 単位加えて [8, 12) となる。
Step by step逐步推演段階を追って
The logical pulse. x1 rises at t = 2 and x2 is already 1, so the AND function becomes 1 at t = 2. x2 falls at t = 6, so the function returns to 0 at t = 6. The pulse in the computed value is [2, 6), width 4.邏輯上的脈衝。x1 在 t = 2 上升,而 x2 本來就是 1,所以 AND 函數在 t = 2 變成 1。x2 在 t = 6 下降,所以函數在 t = 6 回到 0。計算出來的值中那個脈衝是 [2, 6),寬度 4。論理上のパルス。x1 が t = 2 で立ち上がり、x2 はすでに 1 なので、AND 関数は t = 2 で 1 になる。x2 が t = 6 で立ち下がるので、関数は t = 6 で 0 に戻る。計算された値におけるパルスは [2, 6)、幅 4 である。
Does it survive the gate? The gate delay is 3 and the pulse is 4 wide, so $w = 4 > d_r = 3$ and the pulse survives. If x2 had fallen at t = 4 instead, giving $w = 2 \lt 3$, both outputs would have stayed flat and the whole figure would be empty.它通得過那個閘嗎?閘延遲是 3 而脈衝寬 4,所以 $w = 4 > d_r = 3$,脈衝存活。如果 x2 改成在 t = 4 下降,$w = 2 \lt 3$,兩個輸出都會保持平坦,整張圖就空了。ゲートを通過できるか。ゲート遅延は 3 でパルス幅は 4 なので $w = 4 > d_r = 3$、パルスは生き残る。もし x2 が t = 4 で立ち下がって $w = 2 \lt 3$ であれば、どちらの出力も平坦なままで、図はまるごと空になる。
y_inertial. Rise at $2 + 3 = 5$, fall at $6 + 3 = 9$. Width 4, unchanged, because the delays are symmetric.y_inertial。在 $2 + 3 = 5$ 上升,在 $6 + 3 = 9$ 下降。寬度 4,沒有改變,因為兩個延遲是對稱的。y_inertial。$2 + 3 = 5$ で立ち上がり、$6 + 3 = 9$ で立ち下がる。遅延が対称なので幅は 4 のまま変わらない。
y_tran. Same gate, same filtering, but the net adds 2 to every edge: rise at 7, fall at 11. The gate delay and the net delay simply sum: $3 + 2 = 5$ from input to net value.y_tran。同一個閘、同樣的濾除,但 net 給每個邊緣加 2:7 上升、11 下降。閘延遲與 net 延遲就只是相加:從輸入到 net 的值是 $3 + 2 = 5$。y_tran。同じゲート、同じフィルタだが、net が各エッジに 2 を加える。立ち上がり 7、立ち下がり 11。ゲート遅延と net 遅延は単純に足し合わされ、入力から net の値までは $3 + 2 = 5$ である。
buf_out. The buffer's input is y_tran, which already carries the net delay, so the buffer sees edges at 7 and 11 and adds 1: rise at 8, fall at 12. Total from x1 to buf_out: $3 + 2 + 1 = 6$.buf_out。緩衝器的輸入是 y_tran,它已經帶著 net 延遲了,所以緩衝器看到的邊緣在 7 與 11,再加 1:8 上升、12 下降。從 x1 到 buf_out 的總延遲:$3 + 2 + 1 = 6$。buf_out。バッファの入力は y_tran で、すでに net 遅延を含んでいる。したがってバッファが見るエッジは 7 と 11 であり、そこに 1 を加えて立ち上がり 8、立ち下がり 12 となる。x1 から buf_out までの合計は $3 + 2 + 1 = 6$ である。

The figure is the clearest statement of the composition rule in the whole lecture: delays on a path add. Gate, then net, then the next gate, then the next net. There is no interaction term, no maximum, no resolution — just a sum along the path.這張圖是整場講義中對組合規則最清楚的陳述:路徑上的延遲相加。閘、然後 net、然後下一個閘、然後下一條 net。沒有交互項、沒有取最大值、沒有解析 — 就只是沿著路徑求和。この図は、講義全体を通じて合成規則を最も明確に述べたものである。経路上の遅延は加算される。ゲート、次に net、次のゲート、次の net。相互作用項もなく、最大値を取ることもなく、解決もない — 経路に沿った単なる和である。

7.3 my_xor1 versus my_xor27.3 my_xor1 與 my_xor2 的對照7.3 my_xor1 と my_xor2

my_xor1 — total 5my_xor1 — 總共 5my_xor1 — 合計 5
module my_xor1 (y_out, a, b);
  input  a, b;
  output y_out;
  parameter driver_delay = 5;

  wire #driver_delay y_out = a ^ b;
endmodule

A net declaration assignment. The 5 belongs to the assignment; the net itself has no delay. Total 5.一個 net 宣告指定。那個 5 屬於指定;net 本身沒有延遲。總共 5。net 宣言代入である。5 は代入に属し、net 自身は遅延を持たない。合計 5。

my_xor2 — total 8my_xor2 — 總共 8my_xor2 — 合計 8
module my_xor2 (y_out, a, b);
  input  a, b;
  output y_out;
  parameter wire_delay   = 5;
  parameter driver_delay = 3;

  wire #wire_delay y_out;
  assign #driver_delay y_out = a ^ b;
endmodule

Two separate constructs: a net with a 5-unit net delay, and a driver with a 3-unit assignment delay. They add: 8.兩個分開的結構:一條帶 5 單位 net 延遲的 net,以及一個帶 3 單位指定延遲的驅動器。它們相加:8。二つの別個の構文である。5 単位の net 遅延を持つ net と、3 単位の代入遅延を持つドライバ。これらは加算されて 8 となる。

Why does the language make this distinction instead of always adding? Because a net declaration assignment is syntactic sugar for a declaration plus an assign, and it is defined to expand to the version where the delay lands on the assignment. There is simply no syntax in form (2) for a separate net delay. If you want both, you must write both, which is exactly what my_xor2 does.語言為什麼要做這個區分,而不是一律相加?因為 net 宣告指定是「宣告加上一個 assign」的語法糖,而它被定義成展開為「延遲落在指定上」的那個版本。寫法 (2) 裡根本沒有任何語法可以表達獨立的 net 延遲。如果你兩者都要,就必須兩者都寫,而那正是 my_xor2 做的事。なぜ言語は常に加算するのではなく、この区別を設けたのか。net 宣言代入は「宣言+assign」の構文糖であり、遅延が代入に付く版へ展開されると定義されているからである。書き方 (2) には、独立した net 遅延を表す構文がそもそも存在しない。両方が欲しければ両方書くしかなく、それがまさに my_xor2 のしていることである。

Exam phrasing to watch for要留意的考題措辭注意すべき試験の言い回し

"Total delay of y_out from a ^ b" is deliberately worded to mean from the moment the expression value changes to the moment the net shows it. In my_xor1 that is 5. In my_xor2 it is 3 (driver) + 5 (net) = 8. If a question gives you wire #W y = expr; and asks for the total, the answer is $W$, not $2W$ and not $W$ plus anything.「y_out 相對於 a ^ b 的總延遲」這個說法是刻意的,意思是從表示式的值改變的那一刻,到 net 顯示出它的那一刻。在 my_xor1 裡那是 5。在 my_xor2 裡是 3(驅動器)+ 5(net)= 8。如果題目給你 wire #W y = expr; 並問總延遲,答案是 $W$,不是 $2W$,也不是 $W$ 加上任何東西。「a ^ b から y_out までの合計遅延」という言い方は意図的であり、式の値が変化した瞬間から net がそれを示す瞬間までを意味する。my_xor1 ではそれが 5、my_xor2 では 3(ドライバ)+ 5(net)= 8 である。問題が wire #W y = expr; を与えて合計を問うなら、答えは $W$ であって $2W$ でも、$W$ に何かを足したものでもない。

One more wrinkle worth knowing: the inertial filtering happens at the driver, with the driver's delay as the effective window. In my_xor2 a 4-unit pulse on a ^ b survives the 3-unit assignment delay and then is shifted by the net, arriving as a 4-unit pulse at t+8. A 2-unit pulse is swallowed by the assignment delay and never reaches the net at all. The net's own 5 units play no part in deciding what survives.還有一個值得知道的皺摺:慣性濾除發生在驅動器處,有效視窗是驅動器的延遲。在 my_xor2 裡,a ^ b 上一個 4 單位的脈衝通得過 3 單位的指定延遲,接著被 net 平移,在 t+8 以 4 單位的脈衝抵達。2 單位的脈衝則被指定延遲吞掉,根本到不了那條 net。net 自己的那 5 個單位對「什麼能存活」毫無發言權。もう一つ知っておく価値のある細部がある。慣性フィルタはドライバのところで起こり、有効な窓はドライバの遅延である。my_xor2 では、a ^ b 上の 4 単位のパルスは 3 単位の代入遅延を通過し、その後 net によって平行移動され、t+8 に 4 単位のパルスとして到着する。2 単位のパルスは代入遅延に飲み込まれ、net には届きもしない。net 自身の 5 単位は、何が生き残るかの判断に一切関与しない。

7.4 Net delay in a real post-layout netlist7.4 真實佈局後網表中的 net 延遲7.4 実際のレイアウト後ネットリストにおける net 遅延

Open a netlist that a place-and-route tool has written and you will find no net delays at all. Every wire is a plain wire. This confuses people who expect the interconnect RC delay to be in the Verilog.打開一份佈局繞線工具寫出來的網表,你會發現裡面根本沒有 net 延遲。每一條導線都是普通的 wire。這讓期待在 Verilog 裡看到繞線 RC 延遲的人很困惑。配置配線ツールが書き出したネットリストを開いても、net 遅延は一つも見つからない。配線はすべて素の wire である。配線 RC 遅延が Verilog の中にあると思っている人は、ここで戸惑う。

It is not, because it cannot be: the netlist is written once and must serve every corner and every annotation scenario. The interconnect delays live in a separate SDF (Standard Delay Format) file, as INTERCONNECT entries:它不在裡面,而且也不可能在:網表只寫一次,卻必須服務每一個角落與每一種標註情境。繞線延遲活在另一個 SDF(Standard Delay Format)檔案裡,以 INTERCONNECT 條目的形式存在:入っていないし、入れられもしない。ネットリストは一度だけ書き出され、しかもあらゆるコーナーとあらゆるアノテーションの場面に供しなければならないからだ。配線遅延は別の SDF(Standard Delay Format)ファイルに INTERCONNECT 項目として存在する。

// fragment of an SDF file
(CELL (CELLTYPE "top")
  (INSTANCE)
  (DELAY (ABSOLUTE
    (INTERCONNECT u1/Z u2/A (0.012:0.018:0.031) (0.014:0.021:0.036))
    (INTERCONNECT u1/Z u3/A (0.009:0.013:0.022) (0.010:0.015:0.026))
  ))
)

When the simulator reads this through $sdf_annotate, it installs a delay on the path from one driver pin to one load pin. Notice that u1/Z drives both u2/A and u3/A with different delays — a single net with two different arrival times at its two loads. A Verilog wire #d cannot express that; it is one delay for the whole net. The simulator implements SDF interconnect delays with a mechanism called MIPD (module input port delay), which attaches the delay to the receiving module's input port rather than to the net.當模擬器透過 $sdf_annotate 讀進這份檔案時,它會在某個驅動腳位到某個負載腳位的路徑上裝上一個延遲。注意 u1/Z 同時驅動 u2/A 與 u3/A,而兩者的延遲不同 — 同一條 net 在它的兩個負載上有兩個不同的到達時間。Verilog 的 wire #d 無法表達這件事;它是整條 net 一個延遲。模擬器用一種稱為 MIPD(module input port delay,模組輸入埠延遲)的機制來實作 SDF 的繞線延遲,把延遲掛在接收端模組的輸入埠上,而不是掛在 net 上。シミュレータが $sdf_annotate を通じてこれを読み込むと、あるドライバピンからある負荷ピンまでの経路に遅延が取り付けられる。u1/Z が u2/A と u3/A を異なる遅延で駆動していることに注意 — 一本の net が二つの負荷で二つの異なる到着時刻を持つのである。Verilog の wire #d はこれを表現できない。net 全体に対する一つの遅延だからだ。シミュレータは SDF の配線遅延を MIPD(module input port delay、モジュール入力ポート遅延)と呼ばれる仕組みで実装し、遅延を net ではなく受け側モジュールの入力ポートに付ける。

The practical hierarchy實務上的層級実務上の階層
  • Hand-written models and course exercises: wire #d is the net delay mechanism, and it composes by addition.手寫的模型與課堂習題:wire #d 就是 net 延遲的機制,而且以相加方式組合。手書きのモデルと授業の演習:wire #d が net 遅延の仕組みであり、加算によって合成される。
  • Vendor cell libraries: no net delays inside a cell — the cell's whole timing is in its specify block (§8).供應商的單元庫:單元內部沒有 net 延遲 — 單元的全部時序都在它的 specify 區塊裡(§8)。ベンダのセルライブラリ:セル内部に net 遅延はない — セルのタイミングはすべて specify ブロックにある(§8)。
  • Post-layout netlists: no net delays in the Verilog at all; everything arrives by SDF annotation at simulation start.佈局後網表:Verilog 裡完全沒有 net 延遲;一切都在模擬開始時由 SDF 標註送進來。レイアウト後ネットリスト:Verilog には net 遅延が一切ない。すべてはシミュレーション開始時に SDF アノテーションで与えられる。
Practice 9 — Total delay through nets and drivers練習 9 — 通過 net 與驅動器的總延遲演習 9 — net とドライバを通した合計遅延

Problem: Compute the delay from a change in the right-hand expression to the final observable value in each case, and state what happens to a 2-unit pulse on the expression.題目:對每一種情況,算出從右手邊表示式發生變化到最終可觀測值出現的延遲,並說明表示式上一個 2 單位的脈衝會發生什麼事。問題:各場合について、右辺の式が変化してから最終的に観測できる値が現れるまでの遅延を求め、式上の 2 単位のパルスに何が起こるかを述べよ。

(a) wire #4 p = a & b;

(b) wire #4 q;
    assign #6 q = a & b;

(c) wire #4 r;
    and  #6 (r, a, b);
    buf  #1 (s, r);

(d) wire #(3, 7) t;
    assign #(2, 5) t = a & b;
Click to reveal solution點擊顯示解答クリックで解答を表示

(a) 4. Net declaration assignment: the delay belongs to the assignment and the net adds nothing. A 2-unit pulse is swallowed ($2 < 4$ against the rising delay).(a) 4。net 宣告指定:延遲屬於指定,net 不增加任何東西。2 單位的脈衝會被吞掉(相對於上升延遲,$2 < 4$)。(a) 4。net 宣言代入である。遅延は代入に属し、net は何も加えない。2 単位のパルスは飲み込まれる(立ち上がり遅延に対して $2 < 4$)。

(b) 10. Driver 6 plus net 4. A 2-unit pulse is swallowed by the driver's 6-unit delay; it never reaches the net.(b) 10。驅動器 6 加上 net 4。2 單位的脈衝會被驅動器的 6 單位延遲吞掉;它根本到不了那條 net。(b) 10。ドライバの 6 に net の 4 を加える。2 単位のパルスはドライバの 6 単位の遅延に飲み込まれ、net には届かない。

(c) 11 to s, 10 to r. Gate 6, then net 4, giving 10 at r; the buffer adds 1, giving 11 at s. Note s itself has no net delay (it is an implicitly declared net, default wire, no delay). A 2-unit pulse is swallowed at the AND gate.(c) 到 s 是 11,到 r 是 10。閘 6,再加 net 4,在 r 得到 10;緩衝器再加 1,在 s 得到 11。注意 s 自己沒有 net 延遲(它是隱含宣告的 net,預設 wire,沒有延遲)。2 單位的脈衝在那個 AND 閘就被吞掉了。(c) s までは 11、r までは 10。ゲートの 6 に net の 4 を加えて r で 10、バッファが 1 を加えて s で 11 となる。s 自身には net 遅延がないことに注意(暗黙に宣言された net であり、既定の wire、遅延なし)。2 単位のパルスは AND ゲートで飲み込まれる。

(d) rise 5, fall 12. Rise and fall compose independently: the rising edge takes the driver's 2 plus the net's 3, the falling edge takes the driver's 5 plus the net's 7.(d) 上升 5、下降 12。上升與下降各自獨立地組合:上升緣取驅動器的 2 加上 net 的 3,下降緣取驅動器的 5 加上 net 的 7。(d) 立ち上がり 5、立ち下がり 12。立ち上がりと立ち下がりは独立に合成される。立ち上がりエッジはドライバの 2 と net の 3、立ち下がりエッジはドライバの 5 と net の 7 を取る。

For the 2-unit positive pulse, the deciding comparison is against the driver's rising delay, $d_r = 2$. With $w = d_r$ exactly, the fall event is computed in the same time slot in which the pending rise is due to execute — implementation-defined, so say so rather than guess. A 1-unit pulse is definitely swallowed. A 3-unit pulse definitely survives, and it stretches twice: at the driver it becomes $3 + 5 - 2 = 6$ units wide, and the net then shifts its rising edge by 3 and its falling edge by 7, making it $6 + 7 - 3 = 10$ units wide at t. Asymmetric delays stretch (or squeeze) a pulse at every stage it passes through, which is why a glitch that is harmless at the first gate can grow into a full-width pulse four cells later.對那個 2 單位的正脈衝而言,決勝的比較對象是驅動器的上升延遲 $d_r = 2$。$w = d_r$ 剛好相等時,下降事件會在待決的上升本該執行的同一個時間槽被算出來 — 屬於實作定義,所以請註明而不要猜。1 單位的脈衝一定被吞掉。3 單位的脈衝一定存活,而且會被拉長兩次:在驅動器處變成 $3 + 5 - 2 = 6$ 單位寬,接著 net 把它的上升緣平移 3、下降緣平移 7,到 t 時變成 $6 + 7 - 3 = 10$ 單位寬。不對稱的延遲會在脈衝經過的每一級把它拉長(或壓窄),這就是為什麼在第一個閘還無害的毛刺,四顆單元之後可以長成一個全寬的脈衝。この 2 単位の正パルスについて、比較の相手はドライバの立ち上がり遅延 $d_r = 2$ である。$w = d_r$ とちょうど等しい場合、立ち下がりのイベントは保留中の立ち上がりが実行されるはずの同じ時間スロットで算出される — 実装依存なので、推測せずその旨を書くこと。1 単位のパルスは確実に飲み込まれる。3 単位のパルスは確実に生き残り、しかも二度引き伸ばされる。ドライバで $3 + 5 - 2 = 6$ 単位幅になり、次に net が立ち上がりエッジを 3、立ち下がりエッジを 7 だけ動かすので、t では $6 + 7 - 3 = 10$ 単位幅になる。非対称な遅延は、パルスが通過する段ごとにそれを引き伸ばし(あるいは縮め)る。最初のゲートでは無害なグリッチが、四つ先のセルでフル幅のパルスに育ちうるのはこのためである。

8. Module Path Delay and the specify Block8. 模組路徑延遲與 specify 區塊8. モジュールパス遅延と specify ブロック

8.1 Why a third layer exists at all8.1 為什麼需要第三層8.1 そもそもなぜ第三の層があるのか

If gate and net delays already add up along a path, why does the language need a third mechanism? Three reasons, and they are all about the gap between how a model is written and how a cell is characterised.如果閘延遲與 net 延遲本來就會沿路徑相加,語言為什麼還需要第三種機制?有三個理由,而且全都關於「模型是怎麼寫的」與「單元是怎麼特性化的」之間的落差。ゲート遅延と net 遅延がすでに経路に沿って加算されるのなら、なぜ言語に第三の仕組みが要るのか。理由は三つあり、いずれもモデルの書かれ方とセルの特性化のされ方のあいだの隔たりに関わる。

First, a behavioural model has no gates to hang delays on. The lecture states this directly: "in structural descriptions, module delays can be obtained by tracing gate and net delays; in behavioural descriptions, module paths can be described separately". A RAM model written as an array and an always block has no internal structure at all, yet it still has an access time from address to data-out. A module path is the only way to express that.第一,行為式模型沒有閘可以掛延遲。講義直接說了:「在結構化描述中,模組延遲可以由追蹤閘延遲與 net 延遲得到;在行為描述中,模組路徑必須另外描述。」一個寫成陣列加上 always 區塊的 RAM 模型,內部完全沒有結構,但它仍然有一個從位址到資料輸出的存取時間。模組路徑是表達那件事的唯一方式。第一に、動作記述のモデルには遅延を掛ける先のゲートがない。講義は直截にこう述べている。「構造記述ではモジュールの遅延はゲート遅延と net 遅延をたどって得られる。動作記述ではモジュールパスを別途記述する」。配列と always ブロックで書かれた RAM モデルには内部構造がまったくないが、それでもアドレスからデータ出力までのアクセス時間は存在する。それを表現できるのはモジュールパスだけである。

Second, characterisation is pin-to-pin. When a library vendor characterises a NAND cell in SPICE, the measured quantity is "time from the 50% point of A to the 50% point of Z, with B held at 1, under a specified input slew and output load". That is a pin-to-pin number — a timing arc — and it is what appears in the .lib file, in the SDF, and in every static-timing report. Module paths are the Verilog spelling of exactly the same object, which is why the mapping from SDF to specify is one to one.第二,特性化本來就是腳對腳的。當單元庫供應商用 SPICE 特性化一顆 NAND 單元時,量到的是「在指定的輸入斜率與輸出負載下,B 維持 1,從 A 的 50% 點到 Z 的 50% 點的時間」。那是一個腳對腳的數字 — 一條時序弧(timing arc) — 也正是出現在 .lib 檔、SDF 以及每一份靜態時序報告裡的東西。模組路徑就是同一個物件在 Verilog 裡的寫法,這也是為什麼 SDF 與 specify 的對應是一對一的。第二に、特性化はもともとピン間で行われる。ライブラリベンダが NAND セルを SPICE で特性化するとき、測られるのは「指定された入力スルーと出力負荷のもと、B を 1 に保ったまま、A の 50% 点から Z の 50% 点までの時間」である。これはピン間の数値 — 一本のタイミングアーク — であり、.lib ファイルにも SDF にも、あらゆる静的タイミングレポートにも現れるものだ。モジュールパスはまったく同じ対象の Verilog における綴りであり、だからこそ SDF と specify の対応は一対一になる。

Third, back-annotation needs a named handle. An SDF IOPATH A Z (...) entry has to find something in the simulation model to modify. It finds the (A => Z) path in the specify block. A cell model without a specify block is invisible to SDF annotation: the annotator issues "no matching path" warnings and the cell simulates at whatever delay its internal gates happen to carry, which for a typical library model is zero. A post-layout simulation of a netlist whose cells lack specify blocks is a zero-delay simulation wearing a disguise.第三,反標註需要一個具名的把手。一條 SDF 的 IOPATH A Z (...) 條目必須在模擬模型裡找到某個東西來修改。它找到的就是 specify 區塊裡的 (A => Z) 路徑。沒有 specify 區塊的單元模型對 SDF 標註而言是隱形的:標註器會丟出「找不到對應路徑」的警告,而那顆單元就以它內部閘恰好帶著的延遲去模擬 — 對典型的單元庫模型來說那是零。一份單元缺少 specify 區塊的網表,它的佈局後模擬只是一場喬裝過的零延遲模擬。第三に、バックアノテーションには名前の付いた取っ手が要る。SDF の IOPATH A Z (...) 項目は、書き換える対象をシミュレーションモデルの中に見つけなければならない。見つけるのが specify ブロック内の (A => Z) パスである。specify ブロックを持たないセルモデルは SDF アノテーションから見えない。アノテータは「一致するパスがない」と警告を出し、そのセルは内部のゲートがたまたま持っている遅延で — 典型的なライブラリモデルではゼロで — シミュレートされる。セルに specify ブロックのないネットリストのレイアウト後シミュレーションは、変装したゼロ遅延シミュレーションにすぎない。

8.2 specify, endspecify and specparam8.2 specify、endspecify 與 specparam8.2 specify、endspecify、specparam

module norf201 (O, A1, B1);
  output O;
  input  A1, B1;

  nor (O, A1, B1);                 // the function: a zero-delay primitive

  specify                          // the timing: module paths
    (A1, B1 *> O) = (0.1:0.3:0.8, 0.1:0.2:0.5);
  //                 ^ rising (min:typ:max)  ^ falling (min:typ:max)
  endspecify
endmodule

Note the division of labour, which is the standard-cell modelling idiom: the primitive carries the function with no delay, and the specify block carries all the timing. That separation is what makes SDF annotation work — the annotator rewrites the specify numbers and never touches the function.注意這裡的分工,那正是標準單元建模的慣用法:原型只承載功能而沒有延遲,specify 區塊承載全部的時序。那個切分正是讓 SDF 標註能運作的原因 — 標註器改寫 specify 裡的數字,而從不碰功能。ここでの分業に注意すること。これが標準セルモデリングの定石である。プリミティブは遅延を持たず機能だけを担い、specify ブロックがすべてのタイミングを担う。この分離こそ SDF アノテーションを成立させている — アノテータは specify の数値を書き換え、機能には決して触れない。

A specify block may contain only four kinds of item: specparam declarations, module path declarations, pulse-control declarations (pulsestyle_*, showcancelled), and timing checks ($setup and friends). No always, no assign, no instances.一個 specify 區塊只能包含四類項目:specparam 宣告、模組路徑宣告、脈衝控制宣告(pulsestyle_*、showcancelled),以及時序檢查($setup 那一家人)。不能有 always、不能有 assign、不能有實體。specify ブロックに置けるのは四種類の項目だけである。specparam 宣言、モジュールパス宣言、パルス制御宣言(pulsestyle_*、showcancelled)、そしてタイミングチェック($setup とその仲間)である。always も assign もインスタンスも置けない。

module nand1 (O, A, B);
  input  A, B;
  output O;
  nand (O, A, B);
  specify
    specparam                      // local to this specify block
      T01 = 1.13:3.09:7.75,        // 0->1 at the output
      T10 = 0.93:2.50:7.34;        // 1->0 at the output
    (A => O) = (T01, T10);
    (B => O) = (T01, T10);
  endspecify
endmodule

specparam versus parameterspecparam 與 parameter 的對照specparam と parameter の対比

specparamparameter
Where declared宣告的位置宣言する場所Inside specify ... endspecify (and, since Verilog-2001, in the module body too)在 specify ... endspecify 之內(自 Verilog-2001 起,也可以寫在模組本體)specify ... endspecify の内側(Verilog-2001 以降はモジュール本体にも書ける)In the module body在模組本體モジュール本体
Visible to可見範圍見える範囲The specify blockspecify 區塊specify ブロックThe whole module整個模組モジュール全体
Overridden by被什麼覆蓋何に上書きされるかSDF annotationSDF 標註SDF アノテーションdefparam or #(...) at instantiation實體化時的 defparam 或 #(...)インスタンス化時の defparam または #(...)
Overridden by instantiation?可以在實體化時覆蓋嗎?インスタンス化で上書きできるかNo不行できないYes可以できる
Intended for設計用途想定される用途Timing values時序數值タイミングの値Structural values: widths, depths, counts結構性數值:位寬、深度、個數構造的な値:ビット幅、深さ、個数

The important row is "overridden by SDF annotation". Timing numbers are meant to be replaced wholesale after layout, and specparam is the construct that is designed to be replaced. Writing path delays in terms of ordinary parameters works in a pure simulation but puts you outside the annotation path, which is why no library does it.重要的是「被 SDF 標註覆蓋」那一列。時序數字本來就該在佈局後被整批取代,而 specparam 正是為了被取代而設計的結構。用普通的 parameter 來寫路徑延遲在純模擬裡行得通,卻會讓你脫離標註路徑,這就是為什麼沒有任何單元庫那樣做。重要なのは「SDF アノテーションに上書きされる」の行である。タイミングの数値はレイアウト後にまとめて置き換えられるべきものであり、specparam はまさに置き換えられるために設計された構文である。通常の parameter でパスの遅延を書いても純粋なシミュレーションでは動くが、アノテーションの経路から外れてしまう。どのライブラリもそうしないのはこのためだ。

The PATHPULSE$ family (§8.6) are also specparams — they are declared with the specparam keyword and have a reserved name. That is why the slide shows them inside a specparam declaration list.PATHPULSE$ 家族(§8.6)同樣是 specparam — 它們以 specparam 關鍵字宣告,並帶有保留名稱。這就是為什麼投影片把它們畫在 specparam 宣告串列裡。PATHPULSE$ の一族(§8.6)も specparam である。specparam キーワードで宣言され、予約された名前を持つ。スライドがそれらを specparam 宣言のリストの中に示しているのはこのためである。

8.3 Parallel (=>) versus full (*>) connection8.3 平行連接(=>)與全連接(*>)8.3 並列接続(=>)と全接続(*>)

Parallel connection — one arc per bit平行連接 — 每個位元一條弧並列接続 — ビットごとに一本のアーク
(A1 => OA) = (2, 3);
(B1 => OB) = (2, 3);

// for vectors, bit i to bit i:
//   A1[3] -> OA[3]
//   A1[2] -> OA[2]  ... etc
// source and destination MUST be
// the same width
Full connection — every source to every destination全連接 — 每個來源對每個目的全接続 — すべての始点からすべての終点へ
(A1, B1 *> O) = (2, 3);

// every bit of every source to
// every bit of every destination:
//   A1[i] -> O[j] for all i, j
//   B1[i] -> O[j] for all i, j
// widths need not match
Number of timing arcs created建立出來的時序弧數目生成されるタイミングアークの本数 $$\texttt{=>}\;:\; n \text{ arcs (requires } w_{\text{src}} = w_{\text{dst}} = n)\qquad\qquad \texttt{*>}\;:\; \Big(\textstyle\sum_k w_{\text{src},k}\Big)\times\Big(\textstyle\sum_k w_{\text{dst},k}\Big) \text{ arcs}$$

A concrete consequence: (a[7:0] *> y[7:0]) = 1; creates 64 arcs, whereas (a[7:0] => y[7:0]) = 1; creates 8. For an 8-bit buffer the parallel form is correct and the full form is both wrong (it claims bit 0 of the input affects bit 7 of the output) and 8 times more expensive. For an 8-bit adder the full form is right, because every input bit really can affect every output bit through the carry chain. Choosing the wrong one does not usually change simulation results — the delay numbers are often identical — but it bloats the model and it corrupts any tool that reads the arc structure.一個具體的後果:(a[7:0] *> y[7:0]) = 1; 會建立 64 條弧,而 (a[7:0] => y[7:0]) = 1; 只建立 8 條。對一個 8 位元緩衝器而言平行形式才正確,全連接形式既錯誤(它主張輸入的第 0 位元會影響輸出的第 7 位元)又貴上 8 倍。對一個 8 位元加法器而言全連接形式才對,因為每一個輸入位元確實可以經由進位鏈影響每一個輸出位元。選錯通常不會改變模擬結果 — 延遲數字往往一樣 — 但它會把模型撐大,也會把任何讀取弧結構的工具搞壞。具体的な帰結がある。(a[7:0] *> y[7:0]) = 1; は 64 本のアークを作り、(a[7:0] => y[7:0]) = 1; は 8 本しか作らない。8 ビットのバッファには並列形式が正しく、全接続形式は誤りであり(入力のビット 0 が出力のビット 7 に影響すると主張してしまう)、しかも 8 倍高価である。8 ビット加算器には全接続形式が正しい。キャリーチェーンを通じて、どの入力ビットもどの出力ビットに本当に影響しうるからだ。選択を誤ってもシミュレーション結果はたいてい変わらない — 遅延の数値は同じであることが多い — が、モデルは肥大し、アーク構造を読むツールは壊れる。

The slide also states the legality rules for path endpoints, and one of them is wrong:投影片同時也給出了路徑端點的合法性規則,而其中一條是錯的:スライドはパス端点の適法性の規則も述べているが、そのうち一つは誤っている。

Slide correction — path source must be an input or inout投影片勘誤 — 路徑來源必須是 input 或 inoutスライドの訂正 — パスの始点は input か inout でなければならない

The slide says: "Source of a path must be a net declared as input or output." That is a typo. IEEE 1364 requires the source of a module path to be a net declared as input or inout of the module; the destination must be a net or reg declared as output or inout.投影片寫著:「路徑的來源必須是宣告為 input 或 output 的 net。」那是筆誤。IEEE 1364 要求模組路徑的來源必須是宣告為模組 input 或 inout 的 net;目的則必須是宣告為 output 或 inout 的 net 或 reg。スライドにはこうある。「パスの始点は input または output と宣言された net でなければならない」。これは誤記である。IEEE 1364 はモジュールパスの始点をモジュールの input または inout と宣言された net と定め、終点を output または inout と宣言された net か reg と定めている。

The underlying principle makes the correct version obvious: a module path describes timing across the module, so it must start at something that enters the module and end at something that leaves it. A path from an output to an output would not describe anything. Note also that a path destination may be a reg — this is the one place where a reg is allowed as a path endpoint, and it exists so that behavioural models (whose outputs are regs driven from always blocks) can still carry path delays.背後的原則讓正確版本變得顯而易見:模組路徑描述的是橫跨模組的時序,所以它必須從進入模組的東西開始,到離開模組的東西結束。一條從輸出到輸出的路徑什麼都描述不了。另外也請注意,路徑的目的可以是 reg — 這是唯一允許 reg 當路徑端點的地方,它存在的目的是讓行為式模型(其輸出是由 always 區塊驅動的 reg)也能帶上路徑延遲。背後の原理を見れば正しい版は自明である。モジュールパスはモジュールを横断するタイミングを記述するのだから、モジュールに入るものから始まり、モジュールを出るもので終わらなければならない。出力から出力へのパスは何も記述しない。パスの終点が reg でもよいことにも注意。reg がパスの端点として許される唯一の場所であり、出力が always ブロックで駆動される reg である動作記述モデルにもパス遅延を付けられるようにするために存在する。

8.4 State-dependent paths8.4 狀態相依路徑8.4 状態依存パス

A state-dependent path exists only while a condition holds:狀態相依路徑只有在某個條件成立時才存在:状態依存パスは、ある条件が成り立っているあいだだけ存在する。

specify
  if (enable)     (data *> q) = (3, 4);   // transparent: data reaches q in 3/4
  if (!enable)    (data *> q) = (0, 0);   // opaque: no path (or model it away entirely)
endspecify

Why a latch needs this: a transparent latch has a data-to-Q arc only while the enable is high. When the enable is low there is no arc at all, because the data input cannot influence the output. Static timing analysis makes exactly the same distinction, and the .lib file records it as a "when" condition on the arc. The if in a specify block is the Verilog spelling of the Liberty when attribute.為什麼閂鎖器需要它:透通的閂鎖器只有在致能為高時才有一條 data 到 Q 的弧。致能為低時根本沒有弧,因為資料輸入無法影響輸出。靜態時序分析做的正是同樣的區分,而 .lib 檔把它記錄成弧上的一個 "when" 條件。specify 區塊裡的 if 就是 Liberty when 屬性在 Verilog 裡的寫法。ラッチにこれが要る理由はこうだ。透過ラッチに data から Q へのアークがあるのはイネーブルが高いあいだだけである。イネーブルが低いときはアーク自体が存在しない。データ入力が出力に影響できないからだ。静的タイミング解析もまったく同じ区別をしており、.lib ファイルはそれをアークの "when" 条件として記録する。specify ブロックの if は、Liberty の when 属性の Verilog における綴りである。

The condition may use module inputs, inouts, locally declared nets and registers, and constants, combined with the usual operators. It may not reference anything outside the module, and it is evaluated continuously — if the condition changes while a transition is in flight, the delay that was selected when the transition started is the one that is used.這個條件可以使用模組的輸入、inout、區域宣告的 net 與暫存器,以及常數,並用一般的運算子組合起來。它不能引用模組以外的任何東西,而且是持續求值的 — 如果某個轉態還在飛行途中而條件改變了,實際採用的是那個轉態開始時所選中的延遲。この条件にはモジュールの入力、inout、ローカルに宣言された net とレジスタ、および定数を、通常の演算子で組み合わせて使える。モジュールの外にあるものは参照できない。条件は継続的に評価され、遷移が飛行中に条件が変わった場合は、遷移が始まった時点で選ばれた遅延が使われる。

Verilog-2001 adds ifnone for the default case:Verilog-2001 為預設情況加上了 ifnone:Verilog-2001 は既定の場合のために ifnone を追加した。

specify
  if (sel)  (a => y) = 2;
  if (!sel) (a => y) = 4;
  ifnone    (a => y) = 3;    // used when sel is x or z: no state-dependent path matches
endspecify

Without ifnone, a condition that evaluates to x leaves no applicable path and the simulator falls back to whatever distributed delay the module has — usually zero. ifnone closes that hole. A specify block may contain at most one ifnone path per source/destination pair, and it may not coexist with a plain unconditional path for the same pair.沒有 ifnone 時,一個求值為 x 的條件會使得沒有任何路徑適用,於是模擬器退回使用模組所具有的分布延遲 — 通常是零。ifnone 把那個洞補起來。一個 specify 區塊中,每一對來源/目的最多只能有一條 ifnone 路徑,而且它不能與同一對端點的無條件路徑並存。ifnone がないと、条件が x に評価された場合に適用できるパスがなくなり、シミュレータはモジュールが持つ分布遅延 — たいていゼロ — に戻ってしまう。ifnone はその穴を塞ぐ。一つの specify ブロックにおいて、始点/終点の組ごとに ifnone パスは高々一つであり、同じ組に対する無条件パスと共存させることはできない。

Path polarity路徑極性パスの極性

(siga +*> q1) = delay_to_q1;   // positive polarity: output moves the same way as the input
(sigb -*> q2) = delay_to_q2;   // negative polarity: output moves the opposite way
(sigc  *> q3) = delay_to_q3;   // unknown polarity (the default)

Polarity tells the simulator which of the two delay numbers to apply. With positive polarity a rising input edge is a rising output transition and uses the first number; with negative polarity a rising input edge produces a falling output and uses the second number. With unknown polarity the simulator does not try to correlate the edges and simply uses the delay appropriate to the observed output transition. For a buffer you would write +=>; for an inverter, -=>. In practice unknown polarity gives the right answer in almost every case and that is why most models omit the sign; the sign matters for tools that read the model structurally, and for the pulse-handling decisions of §8.6.極性告訴模擬器該套用兩個延遲數字中的哪一個。正極性下,輸入的上升緣對應輸出的上升轉態,用第一個數字;負極性下,輸入的上升緣產生輸出的下降,用第二個數字。未知極性下,模擬器不試圖把兩邊的邊緣關聯起來,就直接依實際觀察到的輸出轉態選用對應的延遲。對緩衝器你會寫 +=>;對反相器寫 -=>。實務上未知極性在幾乎所有情況都給出正確答案,這也是多數模型省略符號的原因;符號對那些以結構方式讀取模型的工具才重要,對 §8.6 的脈衝處理決策也重要。極性は、二つの遅延値のどちらを適用するかをシミュレータに伝える。正の極性では入力の立ち上がりエッジが出力の立ち上がり遷移に対応し、第一の数値を使う。負の極性では入力の立ち上がりが出力の立ち下がりを生み、第二の数値を使う。不明の極性では、シミュレータは両者のエッジを対応づけようとせず、観測された出力遷移に応じた遅延を単純に使う。バッファなら +=>、インバータなら -=> と書く。実務上は不明の極性でもほとんどの場合正しい答えが得られ、多くのモデルが符号を省くのはそのためである。符号が効くのは、モデルを構造的に読むツールと、§8.6 のパルス処理の判断においてである。

8.5 Edge-sensitive paths8.5 邊緣敏感路徑8.5 エッジ感応パス

specify
  (posedge clock => (q : data)) = (3, 4);    // clock-to-Q, parallel form
  (posedge clock *> (q : data)) = (3, 4);    // same thing, full form
endspecify

Read it as: "on a rising edge of clock, a path is established to q, and the value that flows along it is data; the path takes 3 to produce a 1 and 4 to produce a 0."把它讀成:「在 clock 的上升緣上,建立一條通往 q 的路徑,沿著它流動的值是 data;這條路徑產生 1 要 3,產生 0 要 4。」こう読む。「clock の立ち上がりエッジで q へ向かうパスが確立され、そこを流れる値は data である。このパスは 1 を生じるのに 3、0 を生じるのに 4 かかる」。

Three parts deserve separate attention.有三個部分值得分開說明。三つの部分を分けて見ておく価値がある。

The edge qualifier. posedge or negedge, or nothing at all (in which case any edge establishes the path). This is what makes the arc a clock-to-output arc rather than a combinational one, and it is what SDF annotates with (IOPATH (posedge CK) Q ...).邊緣修飾子。posedge、negedge,或者什麼都不寫(此時任何邊緣都會建立這條路徑)。這就是讓這條弧成為時脈到輸出的弧、而不是組合弧的東西,也是 SDF 用 (IOPATH (posedge CK) Q ...) 去標註的對象。エッジ修飾子。posedge、negedge、あるいは何も書かない(この場合どのエッジでもパスが確立する)。これがこのアークを組み合わせのアークではなくクロックから出力へのアークにしているものであり、SDF が (IOPATH (posedge CK) Q ...) でアノテートする対象でもある。

The data source expression (q : data). The part after the colon names the value that propagates. For a flip-flop it is the D input, because that is what ends up on Q. You may write (q +: data) or (q -: data) to declare non-inverting or inverting polarity, exactly as for the +*> / -*> forms. For QN you would write (qn -: data).資料來源表示式 (q : data)。冒號後面那部分指名了會傳播過去的值。對正反器而言那是 D 輸入,因為那就是最後會出現在 Q 上的東西。你可以寫 (q +: data) 或 (q -: data) 來宣告非反相或反相極性,寫法與 +*> / -*> 完全一致。對 QN 你會寫 (qn -: data)。データソース式 (q : data)。コロンの後ろの部分が、伝搬していく値を指名する。フリップフロップならそれは D 入力である。最終的に Q に現れるのがそれだからだ。(q +: data) や (q -: data) と書いて非反転/反転の極性を宣言できる。+*> / -*> の形式とまったく同じである。QN なら (qn -: data) と書く。

Why the data source is needed at all. The simulator must decide whether a given clock edge produces a rising or a falling output transition before it has computed the new output, so that it can pick the right delay number. Naming the data source lets it look at the value being captured.為什麼需要指明資料來源。模擬器必須在算出新的輸出之前就決定某個時脈邊緣會產生上升還是下降的輸出轉態,這樣才能挑對延遲數字。指名資料來源讓它可以去看正在被擷取的那個值。なぜデータソースの指定が要るのか。シミュレータは、正しい遅延値を選ぶために、新しい出力を計算する前に、そのクロックエッジが立ち上がりの出力遷移を生むのか立ち下がりを生むのかを決めなければならない。データソースを指名しておけば、取り込まれつつある値を見にいける。

Mapping the three path kinds onto the three cell types三種路徑與三類單元的對應三種類のパスと三種類のセルの対応
  • Simple path (A => Z) = ...  →  combinational cells: inverters, NANDs, AOIs, multiplexers.簡單路徑 (A => Z) = ...  →  組合邏輯單元:反相器、NAND、AOI、多工器。単純パス (A => Z) = ...  →  組み合わせセル:インバータ、NAND、AOI、マルチプレクサ。
  • State-dependent path if (G) (D *> Q) = ...  →  transparent latches, and the data-dependent arcs of XORs and adders.狀態相依路徑 if (G) (D *> Q) = ...  →  透通閂鎖器,以及 XOR 與加法器中與資料相依的弧。状態依存パス if (G) (D *> Q) = ...  →  透過ラッチ、および XOR や加算器のデータ依存アーク。
  • Edge-sensitive path (posedge CK => (Q : D)) = ...  →  flip-flops, registers, synchronous RAM read ports.邊緣敏感路徑 (posedge CK => (Q : D)) = ...  →  正反器、暫存器、同步 RAM 的讀取埠。エッジ感応パス (posedge CK => (Q : D)) = ...  →  フリップフロップ、レジスタ、同期 RAM の読み出しポート。

8.6 PATHPULSE$: pulse control on module paths8.6 PATHPULSE$:模組路徑上的脈衝控制8.6 PATHPULSE$:モジュールパスにおけるパルス制御

A module path has the same pulse problem as a gate. By default, a module path behaves inertially with its own delay as the window: a pulse narrower than the path delay is rejected. But that default is often wrong for a characterised cell, because the real pulse-rejection width of a cell is not equal to its propagation delay — it is typically a good deal smaller. PATHPULSE$ lets you set the two thresholds explicitly.模組路徑有著與閘相同的脈衝問題。預設情況下,模組路徑以自己的延遲為視窗、依慣性方式行為:比路徑延遲窄的脈衝會被拒絕。但那個預設對一顆被特性化過的單元來說常常是錯的,因為單元真正的脈衝拒絕寬度並不等於它的傳播延遲 — 它通常小得多。PATHPULSE$ 讓你明確地設定那兩個門檻。モジュールパスにはゲートと同じパルスの問題がある。既定では、モジュールパスは自身の遅延を窓として慣性的に振る舞い、パス遅延より狭いパルスは拒否される。しかし特性化されたセルにとってこの既定はしばしば誤りである。セルの実際のパルス拒否幅は伝搬遅延と等しくなく、たいていかなり小さいからだ。PATHPULSE$ は二つのしきい値を明示的に設定させてくれる。

specify
  (clk => q)          = 10;
  (data => q)         = 7;
  (clr, preset *> q)  = 3;

  specparam
    PATHPULSE$clk$q = (3, 8);   // (reject limit, error limit) for the clk->q path only
    PATHPULSE$clr$q = (0, 5);   // reject nothing; x between 0 and 5
    PATHPULSE$      = 4;        // one value = both limits; applies to every other path
endspecify
The three regimes三個區間三つの領域 $$w \lt r \;\Rightarrow\; \text{pulse rejected} \qquad r \le w \lt e \;\Rightarrow\; \text{output goes } x \qquad w \ge e \;\Rightarrow\; \text{pulse propagates}$$

with $r$ the reject limit and $e$ the error limit. If only one value is given, $r = e$ and the middle regime vanishes. If the specified $r$ exceeds $e$, the error limit is raised to equal the reject limit. If no PATHPULSE$ appears at all, $r = e = $ the path delay.其中 $r$ 是 reject 界限、$e$ 是 error 界限。如果只給一個值,則 $r = e$,中間那個區間消失。如果指定的 $r$ 超過 $e$,error 界限會被提高到與 reject 界限相等。如果完全沒有出現 PATHPULSE$,則 $r = e = $ 路徑延遲。ここで $r$ は reject 限界、$e$ は error 限界である。値を一つだけ与えると $r = e$ となり、中間の領域は消える。指定した $r$ が $e$ を超える場合、error 限界は reject 限界と等しくなるまで引き上げられる。PATHPULSE$ がまったく現れなければ $r = e = $ パス遅延である。

PATHPULSE$clk$q = (3, 8) — reject limit 3, error limit 8 rejected nothing appears output forced to x "I cannot tell what silicon would do" propagates delayed by the path delay 0246 81012 incoming pulse width w r = 3 e = 8 w = 2gone w = 5x pulse w = 9passes
The two PATHPULSE$ limits split the pulse-width axis into three regimes. The middle band is the one that distinguishes module paths from gates: instead of guessing, the model says "unknown" and lets the x propagate so that a human sees it.兩個 PATHPULSE$ 界限把脈衝寬度軸切成三個區間。中間那一帶正是模組路徑與閘的分野:模型不去猜,而是說「未知」,並讓那個 x 傳播出去,好讓人看見它。二つの PATHPULSE$ 限界がパルス幅の軸を三つの領域に分ける。中央の帯こそモジュールパスをゲートから区別するものである。モデルは推測せず「不定」と言い、その x を伝搬させて人間の目に触れさせる。

The middle regime is the clever part and it has no counterpart in the gate-delay model. A gate makes a binary decision — pass or swallow — and both answers can be wrong for a pulse near the boundary. A module path can say I do not know, and that is almost always the honest answer for a pulse whose width is comparable to the cell's switching time. Setting PATHPULSE$ with a gap between the limits turns marginal pulses into visible x, which is what you want during verification.中間那個區間是巧妙之處,而且在閘延遲模型裡沒有對應物。閘做的是二元決定 — 通過或吞掉 — 而對一個落在邊界附近的脈衝來說,兩種答案都可能是錯的。模組路徑可以說我不知道,而對一個寬度與單元切換時間相當的脈衝來說,那幾乎永遠是誠實的答案。把 PATHPULSE$ 設成兩個界限之間留有間隙,會把邊緣脈衝變成看得見的 x,而那正是驗證期間你想要的。中央の領域が巧妙なところであり、ゲート遅延モデルには対応物がない。ゲートは通すか飲み込むかの二値判断をするが、境界付近のパルスについてはどちらの答えも誤りでありうる。モジュールパスは分からないと言うことができ、セルのスイッチング時間と同程度の幅のパルスについては、それがほぼ常に誠実な答えである。PATHPULSE$ の二つの限界に隙間を設けておけば、際どいパルスは目に見える x になる。検証中に欲しいのはまさにそれだ。

Pulse style: where the x appears脈衝樣式:x 會出現在哪裡パルススタイル:x はどこに現れるか

specify
  (clk => q) = 10;
  pulsestyle_onevent  q;   // default: the x appears at the scheduled output time
  // pulsestyle_ondetect q;   // the x appears the moment the offending pulse is detected
  showcancelled       q;   // show an x where a cancelled transition would have been
  // noshowcancelled    q;   // default: just drop the cancelled transition silently
endspecify

8.7 Timing checks live here too8.7 時序檢查也住在這裡8.7 タイミングチェックもここに住んでいる

The lecture does not cover timing checks, but they occupy the same specify block and you will meet them the first time you run a gate-level simulation, so it is worth knowing what they are.講義沒有涵蓋時序檢查,但它們佔據的是同一個 specify 區塊,而且你第一次跑閘層級模擬就會遇到它們,所以值得知道它們是什麼。講義はタイミングチェックを扱っていないが、それらは同じ specify ブロックに置かれ、ゲートレベルシミュレーションを初めて走らせればすぐに出会うものなので、何であるかを知っておく価値がある。

specify
  (posedge CK => (Q : D)) = (0.142, 0.158);
  $setup  (D, posedge CK, 0.071, notifier);
  $hold   (posedge CK, D, 0.034, notifier);
  $width  (posedge CK, 0.120, 0, notifier);
  $period (posedge CK, 0.400, notifier);
  $recovery (posedge RN, posedge CK, 0.090, notifier);
endspecify

Each check watches two events and reports a violation if the time between them is less than the stated limit. The fourth argument, the notifier, is a reg declared in the module: on a violation the simulator toggles it, and the UDP inside the cell has a row watching for that toggle (? ? * : ? : x;) which drives the flip-flop output to x. That is the mechanism by which a setup violation becomes visible as data corruption rather than just a line in a log file — and it is why you must never simply suppress timing-check messages in a gate-level run.每個檢查都盯著兩個事件,若兩者之間的時間小於所述的界限就回報一次違規。第四個引數 notifier 是宣告在模組內的一個 reg:發生違規時模擬器會把它翻轉,而單元內部的 UDP 有一列在監看那個翻轉(? ? * : ? : x;),把正反器的輸出驅動成 x。這就是 setup 違規變成看得見的資料毀損、而不只是 log 檔裡一行字的機制 — 也是為什麼你絕對不能在閘層級模擬中隨手把時序檢查訊息關掉。各チェックは二つの事象を見張り、その間隔が指定された限界より小さければ違反を報告する。第四引数の notifier はモジュール内で宣言された reg である。違反が起きるとシミュレータがこれをトグルし、セル内部の UDP にはそのトグルを見張る行(? ? * : ? : x;)があって、フリップフロップの出力を x に駆動する。セットアップ違反がログの一行ではなく目に見えるデータ破壊として現れるのは、この仕組みによる。ゲートレベル実行でタイミングチェックのメッセージを安易に抑止してはならないのも、このためである。

Practice 10 — Reject and error limits練習 10 — reject 與 error 界限演習 10 — reject と error の限界

Problem: Using the specify block of §8.6, state what appears on q for each of the following, and at what time relative to the start of the pulse. (a) a 2-unit pulse on the clk→q path; (b) a 5-unit pulse on clk→q; (c) a 9-unit pulse on clk→q; (d) a 3-unit pulse on data→q; (e) a 1-unit pulse on clr→q; (f) a 4-unit pulse on preset→q. (g) What would change if the PATHPULSE$clk$q line were deleted?題目:以 §8.6 的 specify 區塊為準,對下列各情況說明 q 上會出現什麼,以及相對於脈衝開始的時刻是什麼時候。(a) clk→q 路徑上的 2 單位脈衝;(b) clk→q 上的 5 單位脈衝;(c) clk→q 上的 9 單位脈衝;(d) data→q 上的 3 單位脈衝;(e) clr→q 上的 1 單位脈衝;(f) preset→q 上的 4 單位脈衝。(g) 如果把 PATHPULSE$clk$q 那一行刪掉,會有什麼改變?問題:§8.6 の specify ブロックに基づき、次の各場合に q に何が現れるか、そしてパルス開始時刻からいつ現れるかを答えよ。(a) clk→q パス上の 2 単位パルス、(b) clk→q 上の 5 単位パルス、(c) clk→q 上の 9 単位パルス、(d) data→q 上の 3 単位パルス、(e) clr→q 上の 1 単位パルス、(f) preset→q 上の 4 単位パルス。(g) PATHPULSE$clk$q の行を削除すると何が変わるか。

Click to reveal solution點擊顯示解答クリックで解答を表示

(a) $w = 2 < r = 3$: rejected. Nothing appears on q at all.(a) $w = 2 < r = 3$:被拒絕。q 上完全不會出現任何東西。(a) $w = 2 < r = 3$:拒否される。q には何も現れない。

(b) $3 \le 5 < 8$: an x pulse on q, delayed by the path delay of 10.(b) $3 \le 5 < 8$:q 上出現一個 x 脈衝,延遲為路徑延遲 10。(b) $3 \le 5 < 8$:q にx のパルスが、パス遅延 10 だけ遅れて現れる。

(c) $9 \ge 8$: propagates, delayed by 10, with its 9-unit width intact.(c) $9 \ge 8$:通過,延遲 10,並保有它原來 9 單位的寬度。(c) $9 \ge 8$:通過する。遅延は 10 で、9 単位の幅はそのまま保たれる。

(d) The data→q path has no specific PATHPULSE$data$q, so it uses the catch-all PATHPULSE$ = 4, which sets $r = e = 4$. With $w = 3 < 4$ the pulse is rejected, and because $r = e$ there is no x regime — the pulse simply vanishes.(d) data→q 路徑沒有專屬的 PATHPULSE$data$q,所以它用包山包海的 PATHPULSE$ = 4,也就是 $r = e = 4$。由於 $w = 3 < 4$,脈衝被拒絕;而因為 $r = e$,沒有 x 的區間 — 脈衝就直接消失。(d) data→q パスには専用の PATHPULSE$data$q がないので、総括的な PATHPULSE$ = 4、すなわち $r = e = 4$ を使う。$w = 3 < 4$ なのでパルスは拒否される。$r = e$ なので x の領域はなく、パルスはそのまま消える。

(e) PATHPULSE$clr$q = (0, 5) means $r = 0$, so nothing is ever rejected on this path. With $0 \le 1 < 5$ the result is an x pulse, delayed by the 3-unit path delay. This is a deliberately paranoid setting: an asynchronous clear is exactly where you do not want a glitch silently discarded.(e) PATHPULSE$clr$q = (0, 5) 表示 $r = 0$,所以這條路徑上永遠不會有東西被拒絕。由於 $0 \le 1 < 5$,結果是一個 x 脈衝,延遲為 3 單位的路徑延遲。這是刻意偏執的設定:非同步清除正是你最不希望毛刺被默默丟掉的地方。(e) PATHPULSE$clr$q = (0, 5) は $r = 0$ を意味するので、このパスでは何も拒否されない。$0 \le 1 < 5$ なので結果はx のパルスであり、3 単位のパス遅延だけ遅れて現れる。これは意図的に神経質な設定である。非同期クリアこそ、グリッチを黙って捨てられたくない場所だからだ。

(f) preset→q is covered by the same path declaration as clr but has no PATHPULSE$preset$q, so it falls back to PATHPULSE$ = 4. With $w = 4 \ge e = 4$ it propagates, delayed by 3. Note how close this is to (d) — the same limits, one unit of width apart, opposite outcomes.(f) preset→q 與 clr 由同一條路徑宣告涵蓋,但沒有 PATHPULSE$preset$q,所以退回 PATHPULSE$ = 4。由於 $w = 4 \ge e = 4$,它通過,延遲 3。注意這與 (d) 有多接近 — 相同的界限、寬度只差一個單位,結果卻相反。(f) preset→q は clr と同じパス宣言に覆われるが、PATHPULSE$preset$q がないので PATHPULSE$ = 4 に戻る。$w = 4 \ge e = 4$ なので通過する。遅延は 3 である。(d) との近さに注意。限界は同じで幅は 1 単位違うだけなのに、結果は正反対である。

(g) Deleting the specific line makes clk→q fall back to PATHPULSE$ = 4, so $r = e = 4$: the 2-unit pulse is still rejected, the 5-unit pulse now propagates cleanly instead of producing x, and the 9-unit pulse is unchanged. The model has become less informative: a marginal pulse that used to raise a visible unknown now sails through as though it were fine. If the catch-all were absent as well, the default would be $r = e = 10$, the path delay, and the 5- and 9-unit pulses would both be rejected.(g) 刪掉那一行專屬設定後,clk→q 退回 PATHPULSE$ = 4,於是 $r = e = 4$:2 單位的脈衝仍然被拒絕,5 單位的脈衝現在乾淨地通過,而不再產生 x,9 單位的脈衝不變。這個模型變得比較沒有資訊量:一個原本會舉起可見未知值的邊緣脈衝,現在像沒事一樣直接通過。如果連那個包山包海的設定也不存在,預設會是 $r = e = 10$(路徑延遲),5 單位與 9 單位的脈衝就都會被拒絕。(g) 専用の行を削除すると clk→q は PATHPULSE$ = 4 に戻り、$r = e = 4$ となる。2 単位のパルスは依然拒否され、5 単位のパルスは x を出さずにそのまま通過するようになり、9 単位のパルスは変わらない。モデルは情報量が減った。以前なら目に見える不定を立てていた際どいパルスが、何事もないかのように通り抜ける。総括的な設定もなければ既定は $r = e = 10$(パス遅延)となり、5 単位も 9 単位のパルスも拒否されることになる。

Practice 11 — Write a specify block練習 11 — 寫一個 specify 區塊演習 11 — specify ブロックを書く

Problem: Write the specify block for a positive-level transparent latch LATCH (Q, D, G) and for a negative-edge flip-flop DFFN (Q, QN, D, CKN). Use these characterised numbers: latch D→Q 0.21 rising / 0.19 falling (only while G is high), latch G→Q 0.26 / 0.24; flip-flop CKN→Q 0.31 / 0.28 and CKN→QN 0.34 / 0.30. Add a setup check of 0.08 on D against the active clock edge, and a pulse-reject limit of 0.05 with an error limit of 0.12 on all flip-flop paths.題目:為一個高準位透通的閂鎖器 LATCH (Q, D, G) 與一個負緣觸發的正反器 DFFN (Q, QN, D, CKN) 寫出 specify 區塊。使用這些特性化數字:閂鎖器 D→Q 上升 0.21/下降 0.19(只在 G 為高時成立),閂鎖器 G→Q 0.26/0.24;正反器 CKN→Q 0.31/0.28、CKN→QN 0.34/0.30。再加上 D 相對於作用時脈邊緣的 0.08 setup 檢查,以及對所有正反器路徑的 0.05 脈衝拒絕界限與 0.12 error 界限。問題:高レベル透過ラッチ LATCH (Q, D, G) と立ち下がりエッジフリップフロップ DFFN (Q, QN, D, CKN) の specify ブロックを書け。特性化値は次のとおり。ラッチ D→Q は立ち上がり 0.21/立ち下がり 0.19(G が高いあいだのみ)、ラッチ G→Q は 0.26/0.24、フリップフロップ CKN→Q は 0.31/0.28、CKN→QN は 0.34/0.30。さらに、有効クロックエッジに対する D のセットアップチェック 0.08 と、すべてのフリップフロップパスに対する拒否限界 0.05・error 限界 0.12 を加えよ。

Click to reveal solution點擊顯示解答クリックで解答を表示
module LATCH (Q, D, G);
  output Q;  input D, G;
  // ... functional model (UDP instance) ...
  specify
    // data to Q exists only while the latch is transparent
    if (G)  (D => Q) = (0.21, 0.19);
    // the enable itself always has an arc: it can open the latch onto new data
    (G => Q) = (0.26, 0.24);
  endspecify
endmodule

module DFFN (Q, QN, D, CKN);
  output Q, QN;  input D, CKN;
  reg notifier;
  // ... functional model (UDP instance with notifier) ...
  specify
    (negedge CKN => (Q  :  D)) = (0.31, 0.28);
    (negedge CKN => (QN : ~D)) = (0.34, 0.30);
    $setup (D, negedge CKN, 0.08, notifier);
    specparam PATHPULSE$ = (0.05, 0.12);
  endspecify
endmodule

Points the marker is looking for. The latch's D→Q arc is conditional on G and the G→Q arc is not — the enable always has an arc because opening the latch can itself change the output. The flip-flop uses negedge because it is negative-edge triggered, and the QN path's data source is ~D (or equivalently (QN -: D)) because the inverted output carries the complement. The notifier must be a reg declared in the module body, not in the specify block. PATHPULSE$ with no $src$dst suffix covers every path in the module, which is what "all flip-flop paths" asks for.改題者在看的重點。閂鎖器的 D→Q 弧是條件式的、取決於 G,而 G→Q 弧不是 — 致能永遠有一條弧,因為打開閂鎖器這件事本身就可能改變輸出。正反器用 negedge,因為它是負緣觸發。QN 路徑的資料來源是 ~D(或等價地寫 (QN -: D)),因為反相輸出帶的是補數。notifier 必須是宣告在模組本體裡的 reg,不是宣告在 specify 區塊裡。不帶 $src$dst 後綴的 PATHPULSE$ 涵蓋模組中的每一條路徑,而那正是「所有正反器路徑」所要求的。採点者が見ている点。ラッチの D→Q アークは G に条件づけられ、G→Q アークは条件づけられない — イネーブルには常にアークがある。ラッチを開けること自体が出力を変えうるからだ。フリップフロップは立ち下がりエッジトリガなので negedge を使う。QN パスのデータソースは ~D(あるいは等価に (QN -: D))である。反転出力は補数を運ぶからだ。notifier は specify ブロックではなくモジュール本体で宣言された reg でなければならない。$src$dst の接尾辞を持たない PATHPULSE$ はモジュール内のすべてのパスを覆い、それが「すべてのフリップフロップパス」の要求に合う。

A trap: writing (D => Q) unconditionally for the latch gives the latch a data arc even when it is opaque. Static timing would then report a false combinational path straight through the latch, and in simulation the D input would reach Q with the latch closed.一個陷阱:把閂鎖器的 (D => Q) 寫成無條件的,會讓閂鎖器即使在不透通時也有一條資料弧。靜態時序分析接著就會回報一條穿過閂鎖器的假組合路徑,而在模擬中,閂鎖器關著的時候 D 輸入仍然會到達 Q。一つの罠:ラッチの (D => Q) を無条件に書くと、不透過のときにもラッチにデータアークがあることになる。静的タイミング解析はラッチを突き抜ける偽の組み合わせパスを報告し、シミュレーションではラッチが閉じていても D 入力が Q に届いてしまう。

9. How the Three Delay Layers Compose9. 三層延遲如何組合起來9. 三つの遅延層はどう合成されるか

9.1 The three-layer picture9.1 三層的全貌9.1 三層の全体像

module my_cell (Y, A); module path: (A *> Y) = 9 A Y G1 #3 wire #2 w1 G2 #4 distributed delay along the path: 3 + 2 + 4 = 9 If both are present, the simulator uses the larger of the two for that path. gate delay net delay module path delay
The same input-to-output route described twice: once as a chain of gate and net delays inside the module, and once as a single pin-to-pin module path in the specify block. Gate and net delays add along the route; the module path replaces the whole route with one number.同一條從輸入到輸出的路線被描述了兩次:一次是模組內部閘延遲與 net 延遲串成的鏈,一次是 specify 區塊裡單一的腳對腳模組路徑。閘延遲與 net 延遲沿路線相加;模組路徑則用一個數字取代整條路線。同じ入力から出力への経路が二通りに記述されている。一度はモジュール内部のゲート遅延と net 遅延の連鎖として、もう一度は specify ブロック内の単一のピン間モジュールパスとして。ゲート遅延と net 遅延は経路に沿って加算され、モジュールパスは経路全体を一つの数値で置き換える。

Three statements summarise everything in sections 5 to 8:三句話就能總結第 5 到第 8 節的全部:三つの文で 5 節から 8 節までのすべてが要約できる。

The composition rules組合規則合成の規則
  1. Gate and net delays add along a path. A chain of a 3-unit gate, a 2-unit net and a 4-unit gate delays a transition by 9.閘延遲與 net 延遲沿路徑相加。一條由 3 單位的閘、2 單位的 net 與 4 單位的閘串成的鏈,會把轉態延遲 9。ゲート遅延と net 遅延は経路に沿って加算される。3 単位のゲート、2 単位の net、4 単位のゲートの連鎖は、遷移を 9 だけ遅らせる。
  2. Each layer filters with its own window at the point where it is applied. A pulse is swallowed by the first gate whose leading-edge delay exceeds it, and never reaches the rest of the chain.每一層都在它被套用的那一點用自己的視窗做濾除。一個脈衝會被第一個前緣延遲超過它的閘吞掉,之後鏈上的其他部分根本見不到它。各層は、適用される地点で自分の窓によってフィルタする。パルスは、先行エッジの遅延がそれを上回る最初のゲートで飲み込まれ、鎖の残りには届かない。
  3. A module path does not add to the distributed delay — it competes with it. Where both describe the same input-to-output route, the simulator uses the larger of the two.模組路徑不會加到分布延遲上 — 它與分布延遲競爭。當兩者描述的是同一條輸入到輸出的路線時,模擬器採用兩者中較大的那個。モジュールパスは分布遅延に加算されるのではなく、分布遅延と競合する。両者が同じ入力から出力への経路を記述している場合、シミュレータは大きいほうを採用する。

9.2 Path delay versus distributed delay9.2 路徑延遲與分布延遲的對照9.2 パス遅延と分布遅延

Rule 3 catches people out, because the intuitive guess is that the specify delay is added on top of the gate delays. It is not. The specify path and the internal gate chain are two descriptions of the same physical thing, and the standard resolves the conflict by taking the larger value for each path.第 3 條規則常絆倒人,因為直覺的猜測是 specify 延遲會疊加在閘延遲之上。並不會。specify 路徑與內部的閘鏈是同一個物理事物的兩種描述,而標準解決這個衝突的方式是:對每一條路徑取兩者中較大的值。規則 3 は人をつまずかせる。specify の遅延がゲート遅延の上に加算されるというのが直感的な推測だからだ。そうはならない。specify のパスと内部のゲート連鎖は同一の物理的事物についての二つの記述であり、標準はパスごとに大きいほうの値を取ることでこの衝突を解決する。

In practice the rule almost never bites, because library models are written so that it cannot. Look again at the shape of a cell model:實務上這條規則幾乎從不咬人,因為單元庫的模型就是寫成讓它不可能咬人。再看一次單元模型的形狀:実務ではこの規則がほとんど牙を剥かない。ライブラリのモデルが、そうならないように書かれているからである。セルモデルの形をもう一度見てみる。

module norf201 (O, A1, B1);
  output O;  input A1, B1;

  nor (O, A1, B1);                             // NO delay on the primitive

  specify
    (A1, B1 *> O) = (0.1:0.3:0.8, 0.1:0.2:0.5);
  endspecify
endmodule

The primitive carries no delay, so the distributed delay is zero and $\max(9, 0) = 9$: the path delay is the entire timing of the cell. That is deliberate. It keeps the function and the timing in separate places so that SDF can rewrite the timing without touching the function, and it removes any ambiguity about which number wins.原型不帶延遲,所以分布延遲是零,而 $\max(9, 0) = 9$:路徑延遲就是這顆單元全部的時序。那是刻意的。它把功能與時序放在不同的地方,讓 SDF 可以在不碰功能的情況下改寫時序,也消除了「哪個數字會贏」的任何歧義。プリミティブは遅延を持たないので分布遅延はゼロであり、$\max(9, 0) = 9$ となる。パス遅延がそのセルのタイミングのすべてである。これは意図的だ。機能とタイミングを別々の場所に置くことで、SDF は機能に触れずにタイミングを書き換えられ、どちらの数値が勝つかという曖昧さも消える。

What happens if you ignore this忽略這一點會發生什麼事これを無視するとどうなるか

Write a model with nand #5 (O, A, B); and (A => O) = 2; and the cell will simulate with a delay of 5, not 2 and not 7. Then back-annotate an SDF that sets the path to 0.3 and the cell still simulates at 5, because SDF rewrites the specify path and the distributed 5 is still larger. The annotation log shows the path being annotated successfully; the waveform shows the wrong delay. This is a genuinely nasty bug and the reason the "zero-delay primitive plus specify block" idiom is universal.寫一個同時含有 nand #5 (O, A, B); 與 (A => O) = 2; 的模型,這顆單元會以 5 的延遲模擬,不是 2,也不是 7。接著反標註一份把該路徑設成 0.3 的 SDF,這顆單元仍然以 5 模擬,因為 SDF 改寫的是 specify 路徑,而分布的那個 5 依然比較大。標註的 log 顯示路徑成功被標註;波形顯示的卻是錯的延遲。這是個真正討厭的錯誤,也是「零延遲原型加上 specify 區塊」這個慣用法之所以舉世通用的原因。nand #5 (O, A, B); と (A => O) = 2; の両方を含むモデルを書くと、そのセルは 2 でも 7 でもなく 5 の遅延でシミュレートされる。続いてそのパスを 0.3 に設定する SDF をバックアノテートしても、セルは依然として 5 でシミュレートされる。SDF が書き換えるのは specify のパスであり、分布遅延の 5 のほうが依然として大きいからだ。アノテーションのログはパスが正常にアノテートされたと示し、波形は誤った遅延を示す。これは本当に厄介なバグであり、「遅延を持たないプリミティブ+specify ブロック」という定石が普遍的である理由でもある。

9.3 A worked post-layout number9.3 佈局後數字的實例計算9.3 レイアウト後の数値例

Take a simple launch-to-capture path at the slow corner: a flip-flop, two combinational cells, and the interconnect between them.取一條慢角下簡單的發射到擷取路徑:一顆正反器、兩顆組合邏輯單元,以及它們之間的繞線。低速コーナーにおける単純なローンチからキャプチャまでの経路をとる。フリップフロップ一つ、組み合わせセル二つ、そしてそれらのあいだの配線である。

Adding up a timing path把一條時序路徑加起來タイミングパスを足し合わせる
Clock-to-Q of the launching flip-flop. Its model has (posedge CK => (Q : D)) = (0.142, 0.158), annotated at the max corner to (0.291, 0.318). For a rising transition on Q, $t_{CQ} = 0.291$ ns.發射端正反器的時脈到 Q。它的模型有 (posedge CK => (Q : D)) = (0.142, 0.158),在最大角落被標註成 (0.291, 0.318)。對 Q 上的上升轉態而言,$t_{CQ} = 0.291$ ns。ローンチ側フリップフロップのクロックから Q まで。そのモデルは (posedge CK => (Q : D)) = (0.142, 0.158) を持ち、最大コーナーで (0.291, 0.318) にアノテートされる。Q の立ち上がり遷移については $t_{CQ} = 0.291$ ns である。
Interconnect to the first cell. SDF INTERCONNECT u_ff/Q u_nand/A (0.031), so $t_{net1} = 0.031$ ns.到第一顆單元的繞線。SDF INTERCONNECT u_ff/Q u_nand/A (0.031),所以 $t_{net1} = 0.031$ ns。最初のセルまでの配線。SDF の INTERCONNECT u_ff/Q u_nand/A (0.031) により $t_{net1} = 0.031$ ns。
First combinational cell. NAND, (A => Z) annotated to 0.118 ns for the relevant transition. $t_{c1} = 0.118$.第一顆組合邏輯單元。NAND,(A => Z) 對相關轉態被標註成 0.118 ns。$t_{c1} = 0.118$。一つ目の組み合わせセル。NAND であり、該当する遷移について (A => Z) は 0.118 ns にアノテートされる。$t_{c1} = 0.118$。
Interconnect. $t_{net2} = 0.044$.繞線。$t_{net2} = 0.044$。配線。$t_{net2} = 0.044$。
Second combinational cell. Inverter, $t_{c2} = 0.063$.第二顆組合邏輯單元。反相器,$t_{c2} = 0.063$。二つ目の組み合わせセル。インバータで $t_{c2} = 0.063$。
Interconnect to the capture flip-flop's D pin. $t_{net3} = 0.027$.到擷取端正反器 D 腳位的繞線。$t_{net3} = 0.027$。キャプチャ側フリップフロップの D ピンまでの配線。$t_{net3} = 0.027$。
Total data arrival:資料抵達總時間:データ到着時刻の合計: $$t_{\text{arrival}} = 0.291 + 0.031 + 0.118 + 0.044 + 0.063 + 0.027 = 0.574\ \text{ns}$$
Required time for a 1 GHz clock with zero skew and a 0.071 ns setup: $T - t_{\text{setup}} = 1.000 - 0.071 = 0.929$ ns.在零偏斜、setup 為 0.071 ns 的 1 GHz 時脈下的需求時間:$T - t_{\text{setup}} = 1.000 - 0.071 = 0.929$ ns。スキューゼロ、セットアップ 0.071 ns の 1 GHz クロックにおける要求時刻:$T - t_{\text{setup}} = 1.000 - 0.071 = 0.929$ ns。
Slack $= 0.929 - 0.574 = +0.355$ ns. The path passes.時序餘裕(slack) $= 0.929 - 0.574 = +0.355$ ns。這條路徑通過。スラック $= 0.929 - 0.574 = +0.355$ ns。このパスは満たされている。

Every term in that sum is a Lecture 3 construct. $t_{CQ}$ is an edge-sensitive module path. The three interconnect terms are net delays (installed as MIPDs by the annotator). The two cell terms are simple module paths. The setup limit is a $setup timing check inside a specify block. A gate-level simulation computes exactly this sum, event by event, and if the arrival exceeds the required time, the $setup check fires, toggles the notifier, and the capture flip-flop's UDP drives x.那個總和裡的每一項都是第 3 講的結構。$t_{CQ}$ 是一條邊緣敏感的模組路徑。三個繞線項是 net 延遲(由標註器以 MIPD 的形式裝上去)。兩個單元項是簡單模組路徑。setup 界限是 specify 區塊裡的一個 $setup 時序檢查。閘層級模擬逐事件算的正是這個總和,而如果抵達時間超過需求時間,$setup 檢查就會觸發、翻轉 notifier,然後擷取端正反器的 UDP 把輸出驅動成 x。この和の各項はいずれも第 3 講の構文である。$t_{CQ}$ はエッジ感応のモジュールパス。三つの配線項は net 遅延(アノテータが MIPD として取り付ける)。二つのセル項は単純モジュールパス。セットアップ限界は specify ブロック内の $setup タイミングチェックである。ゲートレベルシミュレーションはこの和をイベントごとに計算し、到着時刻が要求時刻を超えれば $setup チェックが発火して notifier をトグルし、キャプチャ側フリップフロップの UDP が出力を x に駆動する。

That last sentence is the answer to "why bother with gate-level simulation when static timing analysis already checked every path?" STA checks every path but assumes the design is synchronous and the constraints are right. Gate-level simulation with SDF checks the paths that actually toggle under real stimulus, catches the asynchronous interfaces and the false-path exceptions you declared carelessly, and turns a timing failure into visible data corruption rather than a number in a report you may not read.最後那句話就是「既然靜態時序分析已經檢查過每一條路徑,為什麼還要做閘層級模擬?」的答案。STA 檢查每一條路徑,但它假設設計是同步的、而且約束是對的。帶 SDF 的閘層級模擬檢查的是在真實激勵下實際會翻轉的那些路徑,抓得到非同步介面以及你隨手宣告的 false path 例外,並且把時序失效變成看得見的資料毀損,而不是一份你可能根本不會讀的報告裡的一個數字。最後の一文が、「静的タイミング解析がすでに全パスを検査しているのに、なぜゲートレベルシミュレーションをするのか」への答えである。STA は全パスを検査するが、設計が同期式であり制約が正しいことを前提にしている。SDF 付きのゲートレベルシミュレーションは、実際の刺激のもとで本当にトグルするパスを検査し、非同期インタフェースや不用意に宣言したフォルスパス例外を捕まえ、タイミング違反を、読まないかもしれないレポートの数値ではなく目に見えるデータ破壊に変える。

9.4 SDF back-annotation9.4 SDF 反標註9.4 SDF バックアノテーション

The link between layout and simulation is one system task:佈局與模擬之間的連結,就是一個系統任務:レイアウトとシミュレーションをつなぐのは一つのシステムタスクである。

initial
  $sdf_annotate("post_route.sdf", dut, , "sdf.log", "MAXIMUM");
//              file            scope  cfg  log       corner
SDF constructSDF 結構SDF の構文Verilog construct it rewrites它改寫的 Verilog 結構書き換える Verilog の構文
(IOPATH A Z (r) (f))The simple module path (A => Z) in that cell's specify block該單元 specify 區塊中的簡單模組路徑 (A => Z)そのセルの specify ブロック内の単純モジュールパス (A => Z)
(IOPATH (posedge CK) Q (r) (f))The edge-sensitive path (posedge CK => (Q : D))邊緣敏感路徑 (posedge CK => (Q : D))エッジ感応パス (posedge CK => (Q : D))
(COND enable (IOPATH D Q ...))The state-dependent path if (enable) (D => Q)狀態相依路徑 if (enable) (D => Q)状態依存パス if (enable) (D => Q)
(INTERCONNECT u1/Z u2/A (d))A module input port delay on u2's A pin — the net-delay layeru2 的 A 腳位上的模組輸入埠延遲 — 也就是 net 延遲那一層u2 の A ピンにおけるモジュール入力ポート遅延 — すなわち net 遅延の層
(SETUP D (posedge CK) (v))The limit argument of the $setup timing check$setup 時序檢查的界限引數$setup タイミングチェックの限界引数
(PATHPULSE ...)The PATHPULSE$ specparamsPATHPULSE$ specparamPATHPULSE$ の specparam

Read that table from right to left and the design of the specify block makes complete sense. Every construct the lecture introduces exists because there is an SDF entry that needs somewhere to land. Module paths exist because IOPATH exists. State-dependent paths exist because COND exists. PATHPULSE$ exists because PATHPULSE exists. The Verilog side was designed as the target of the annotation, not the other way round.把那張表由右往左讀,specify 區塊的設計就完全說得通了。講義介紹的每一個結構之所以存在,都是因為有某個 SDF 條目需要一個落腳處。模組路徑存在是因為 IOPATH 存在。狀態相依路徑存在是因為 COND 存在。PATHPULSE$ 存在是因為 PATHPULSE 存在。Verilog 這一側是被設計成標註的目標,而不是反過來。その表を右から左へ読めば、specify ブロックの設計は完全に筋が通る。講義が紹介する構文はどれも、受け皿を必要とする SDF の項目があるから存在する。モジュールパスが存在するのは IOPATH があるから。状態依存パスが存在するのは COND があるから。PATHPULSE$ が存在するのは PATHPULSE があるからだ。Verilog 側はアノテーションの受け手として設計されたのであって、その逆ではない。

What to check in the annotation log, every time每一次都要檢查的標註 log 項目毎回確認すべきアノテーションログの項目
  • Zero "no matching path" warnings. Each one is a cell whose timing was silently not applied.零個「找不到對應路徑」警告。每一個警告都代表一顆單元的時序被默默地沒有套用。「一致するパスなし」の警告がゼロであること。警告一つにつき、タイミングが黙って適用されなかったセルが一つある。
  • The corner you asked for. "MAXIMUM" for setup, "MINIMUM" for hold. Running a hold check at the max corner proves nothing.你要的那個角落。setup 用 "MAXIMUM",hold 用 "MINIMUM"。在最大角落跑 hold 檢查什麼都證明不了。意図したコーナーであること。セットアップには "MAXIMUM"、ホールドには "MINIMUM"。最大コーナーでホールドを検査しても何も証明できない。
  • The scope argument. Annotating at the wrong hierarchy level annotates nothing and reports success.scope 引數。在錯誤的階層層級標註,等於什麼都沒標註,而且還回報成功。スコープ引数。誤った階層レベルでアノテートすると、何もアノテートされないまま成功と報告される。
  • The simulator's time precision. If the SDF has three decimal places of picoseconds and the simulation precision is 1 ps, every number is rounded and your 0.0318 ns delay becomes 0.032 ns. Harmless here, fatal when the SDF is in femtoseconds.模擬器的時間精度。如果 SDF 的皮秒帶三位小數而模擬精度是 1 ps,每一個數字都會被捨入,你的 0.0318 ns 延遲會變成 0.032 ns。在這裡無害,但當 SDF 的單位是飛秒時就是致命的。シミュレータの時間精度。SDF がピコ秒の小数第三位まで持ち、シミュレーション精度が 1 ps なら、すべての数値が丸められ、0.0318 ns の遅延は 0.032 ns になる。ここでは無害だが、SDF がフェムト秒単位のときは致命的である。
Practice 12 — Annotate and add up練習 12 — 反標註並加總演習 12 — アノテートして足し合わせる

Problem: A cell is modelled as題目:某顆單元的模型寫成問題:あるセルが次のようにモデル化されている。

module inv1 (Z, A);
  output Z;  input A;
  not #(0.4, 0.5) (Z, A);
  specify
    specparam Tr = 0.2, Tf = 0.25;
    (A => Z) = (Tr, Tf);
  endspecify
endmodule

(a) What rising delay does an instance show before any annotation? (b) An SDF sets (IOPATH A Z (0.31) (0.36)). What rising delay does the instance show now? (c) How would you fix the model? (d) After the fix and the same SDF, a chain of three such inverters is connected by nets annotated with INTERCONNECT delays of 0.02, 0.03 and 0.01. What is the total delay from the first input to the last output for a transition that rises at the final output? (e) A 0.25 ns pulse arrives at the first inverter's input. Does it reach the end of the chain?(a) 在任何標註之前,它的實體顯示出多少上升延遲? (b) 某份 SDF 設定 (IOPATH A Z (0.31) (0.36))。現在這個實體顯示多少上升延遲? (c) 你會怎麼修這個模型? (d) 修好之後、用同一份 SDF,三顆這樣的反相器串成一鏈,彼此以標註了 INTERCONNECT 延遲 0.02、0.03、0.01 的 net 相連。對一個在最後輸出端為上升的轉態而言,從第一個輸入到最後一個輸出的總延遲是多少? (e) 一個 0.25 ns 的脈衝抵達第一顆反相器的輸入。它到得了鏈的末端嗎?(a) アノテーション前、このインスタンスはどれだけの立ち上がり遅延を示すか。(b) ある SDF が (IOPATH A Z (0.31) (0.36)) を設定する。いまインスタンスはどれだけの立ち上がり遅延を示すか。(c) このモデルをどう直すか。(d) 修正後、同じ SDF のもとで、このインバータ三つが INTERCONNECT 遅延 0.02、0.03、0.01 をアノテートされた net で連結されている。最終出力で立ち上がる遷移について、最初の入力から最後の出力までの合計遅延はいくらか。(e) 0.25 ns のパルスが最初のインバータの入力に到着する。それは鎖の末端まで届くか。

Click to reveal solution點擊顯示解答クリックで解答を表示

(a) 0.4. The distributed delay (the not primitive's 0.4 rising) and the module path (0.2 rising) both describe A→Z, and the simulator takes the larger: $\max(0.4, 0.2) = 0.4$.(a) 0.4。分布延遲(not 原型的 0.4 上升)與模組路徑(0.2 上升)描述的都是 A→Z,模擬器取較大者:$\max(0.4, 0.2) = 0.4$。(a) 0.4。分布遅延(not プリミティブの立ち上がり 0.4)とモジュールパス(立ち上がり 0.2)はどちらも A→Z を記述しており、シミュレータは大きいほうを取る。$\max(0.4, 0.2) = 0.4$ である。

(b) Still 0.4. SDF rewrites the specify path to 0.31, so the comparison becomes $\max(0.4, 0.31) = 0.4$. The annotation succeeded, the log is clean, and the delay is wrong. This is exactly the failure mode described in §9.2.(b) 仍然是 0.4。SDF 把 specify 路徑改寫成 0.31,所以比較變成 $\max(0.4, 0.31) = 0.4$。標註成功了、log 是乾淨的,而延遲是錯的。這正是 §9.2 描述的失效模式。(b) やはり 0.4。SDF は specify のパスを 0.31 に書き換えるので、比較は $\max(0.4, 0.31) = 0.4$ になる。アノテーションは成功し、ログはきれいで、遅延は誤っている。§9.2 が述べた失敗の形そのものである。

(c) Remove the delay from the primitive: not (Z, A);. The distributed delay becomes zero, the path delay wins unconditionally, and annotation takes full effect. If you want the model to have sensible pre-annotation timing, put the numbers in the specparams (Tr = 0.4, Tf = 0.5), not on the primitive.(c) 把延遲從原型上拿掉:not (Z, A);。分布延遲變成零,路徑延遲無條件勝出,標註就完全生效。如果你希望模型在標註前也有合理的時序,就把數字放進 specparam(Tr = 0.4, Tf = 0.5),而不是放在原型上。(c) プリミティブから遅延を外す:not (Z, A);。分布遅延がゼロになり、パス遅延が無条件に勝ち、アノテーションが完全に効くようになる。アノテーション前にも妥当なタイミングを持たせたいなら、数値はプリミティブではなく specparam(Tr = 0.4, Tf = 0.5)に入れること。

(d) A rising transition at the final output means the last inverter is rising, so its input is falling, so the middle inverter is rising, so its input is falling, so the first inverter is rising. Reading the chain from the input: the first inverter rises (0.31), the second falls (0.36), the third rises (0.31). Adding the three interconnect delays:(d) 最後一個輸出是上升,表示最後那顆反相器在上升,所以它的輸入在下降,所以中間那顆在上升,所以它的輸入在下降,所以第一顆在上升。由輸入端讀起這條鏈:第一顆反相器上升(0.31)、第二顆下降(0.36)、第三顆上升(0.31)。再加上三個繞線延遲:(d) 最終出力が立ち上がるということは、最後のインバータが立ち上がっており、その入力は立ち下がり、真ん中のインバータは立ち上がり、その入力は立ち下がり、最初のインバータは立ち上がっている。入力側から鎖を読むと、第一のインバータが立ち上がり(0.31)、第二が立ち下がり(0.36)、第三が立ち上がる(0.31)。これに三つの配線遅延を加える。 $$t = 0.31 + 0.02 + 0.36 + 0.03 + 0.31 + 0.01 = \mathbf{1.04}\ \text{ns}$$ Note that you must alternate rise and fall along an inverting chain; using 0.31 three times is the classic error and gives 0.99 ns.注意在反相鏈上必須讓上升與下降交替;用三次 0.31 是經典錯誤,會得到 0.99 ns。反転する鎖では立ち上がりと立ち下がりを交互にしなければならない点に注意。0.31 を三回使うのが典型的な誤りで、0.99 ns という答えになる。

(e) The pulse is positive at the input, so the deciding comparison at the first inverter is against the delay of the leading output edge — the output falls first, so $d_f = 0.36$. With $w = 0.25 < 0.36$ the pending output fall is cancelled by the arriving input fall and the pulse never leaves the first inverter. It does not reach the end of the chain; it does not even reach the first net. (If the input pulse had been 0.40 ns, it would survive the first stage and emerge $0.40 + 0.31 - 0.36 = 0.35$ ns wide — narrower than it started, and therefore at risk of being swallowed by the next stage. Pulses that shrink stage by stage are how a glitch quietly disappears mid-chain in simulation while surviving in silicon.)(e) 這個脈衝在輸入端是正脈衝,所以在第一顆反相器處決勝的比較對象是前緣輸出邊緣的延遲 — 輸出先下降,所以是 $d_f = 0.36$。由於 $w = 0.25 < 0.36$,待決的輸出下降被抵達的輸入下降取消,脈衝根本離不開第一顆反相器。它到不了鏈的末端;它連第一條 net 都到不了。(如果輸入脈衝是 0.40 ns,它會通過第一級,並以 $0.40 + 0.31 - 0.36 = 0.35$ ns 的寬度出現 — 比原來更窄,因此有被下一級吞掉的風險。脈衝一級一級變窄,正是毛刺在模擬中悄悄消失於鏈中、卻能在矽晶片裡存活下來的方式。)(e) 入力側では正のパルスなので、最初のインバータでの比較相手は先行する出力エッジの遅延である — 出力はまず立ち下がるので $d_f = 0.36$ となる。$w = 0.25 < 0.36$ なので、保留中の出力の立ち下がりは到着した入力の立ち下がりによって取り消され、パルスは最初のインバータから出られない。鎖の末端どころか、最初の net にすら届かない。(入力パルスが 0.40 ns なら第一段を通過し、$0.40 + 0.31 - 0.36 = 0.35$ ns の幅で現れる — 出発時より狭く、したがって次の段に飲み込まれる危険がある。段ごとに狭くなるパルスこそ、グリッチがシミュレーションでは鎖の途中で静かに消えながら、シリコンでは生き延びる仕組みである。)

10. BNF, System Tasks and Compiler Directives10. BNF、系統任務與編譯指令10. BNF、システムタスク、コンパイラ指令

10.1 Reading BNF10.1 讀懂 BNF10.1 BNF の読み方

The Verilog standard defines the language in Backus-Naur Form, and once you can read it you never again have to guess whether a construct is legal. The whole notation is four symbols:Verilog 標準以 Backus-Naur Form 定義這個語言,而一旦你讀得懂它,就再也不必猜某個結構合不合法。整套記法只有四個符號:Verilog 標準はこの言語をバッカス・ナウア記法で定義しており、これが読めるようになれば、ある構文が合法かどうかを推測する必要は二度となくなる。記法は全部で四つの記号からなる。

Notation記號記法Meaning意義意味Example範例例
::="is defined as"「定義為」「〜と定義される」module_keyword ::= module | macromodule
|alternative擇一選択肢の一つeither module or macromodule, not bothmodule 或 macromodule 其中之一,不能兩者都要module か macromodule のいずれか一方で、両方ではない
[ ... ]optional: appears once or not at all可省略:出現一次或完全不出現省略可能:一度現れるか、まったく現れないか[ list_of_ports ] — a module may have no port list[ list_of_ports ] — 模組可以沒有埠列表[ list_of_ports ] — モジュールはポートリストを持たなくてよい
{ ... }repetition: zero or more times重複:零次或多次繰り返し:ゼロ回以上{ module_item } — a module may be empty{ module_item } — 模組可以是空的{ module_item } — モジュールは空でもよい

Two cautions. First, the braces in BNF are metasyntax; they are not the Verilog concatenation operator, even though the same characters appear in the language. Second, "zero or more" genuinely includes zero: module empty; endmodule is a legal Verilog module with no ports and no items, and the BNF says so.兩點提醒。第一,BNF 裡的大括號是後設語法;它們不是 Verilog 的串接運算子,儘管語言裡也用同樣的字元。第二,「零次或多次」真的包含零次:module empty; endmodule 是一個合法的 Verilog 模組,沒有埠也沒有項目,而 BNF 就是這麼說的。二つ注意しておく。第一に、BNF の波括弧はメタ構文であり、同じ文字が言語中にも現れるとはいえ、Verilog の連結演算子ではない。第二に、「ゼロ回以上」には本当にゼロ回が含まれる。module empty; endmodule はポートも項目もない合法な Verilog モジュールであり、BNF はそう述べている。

10.2 The grammar skeleton, annotated10.2 文法骨架,加上註解10.2 文法の骨格に注釈をつける

source_text        ::= { description }
description        ::= module_declaration | udp_declaration
module_declaration ::= module_keyword module_identifier [ list_of_ports ] ;
                       { module_item }
                       endmodule
module_keyword     ::= module | macromodule
list_of_ports      ::= ( port { , port } )
module_item        ::= module_item_declaration | parameter_override
                     | continuous_assign     | gate_instantiation
                     | udp_instantiation     | module_instantiation
                     | specify_block         | initial_construct
                     | always_construct
continuous_assign  ::= assign [ drive_strength ] [ delay3 ] list_of_net_assignments ;
drive_strength     ::= ( strength0 , strength1 )
delay3             ::= # delay_value
                     | # ( delay_value [ , delay_value [ , delay_value ] ] )
list_of_net_assignments ::= net_assignment { , net_assignment }
net_assignment     ::= net_lvalue = expression

Five things this fragment tells you that the prose of the course does not.這段文法告訴你五件課程的文字說明沒說的事。この断片は、授業の文章が述べていない五つのことを教えてくれる。

A file is a list of modules and UDPs, nothing else. description ::= module_declaration | udp_declaration. There is no file-level variable, no top-level statement, no main. Compiler directives are not in the grammar at all because they are removed by the preprocessor before the parser sees anything — which is precisely why they are not scoped (§6.5).一個檔案就是一串模組與 UDP,別無他物。description ::= module_declaration | udp_declaration。沒有檔案層級的變數、沒有頂層敘述、沒有 main。編譯指令根本不在文法裡,因為它們在剖析器看到任何東西之前就已經被前處理器移除了 — 這正是它們不受作用範圍限制的原因(§6.5)。ファイルとはモジュールと UDP の並びであり、それ以外の何物でもない。description ::= module_declaration | udp_declaration。ファイルレベルの変数も、トップレベルの文も、main も存在しない。コンパイラ指令はそもそも文法に含まれていない。パーサが何かを見る前にプリプロセッサが取り除くからであり、それがスコープを持たない理由でもある(§6.5)。

UDPs are first-class, at the same level as modules. That is the formal statement of "a UDP is not a module and cannot be nested inside one".UDP 是第一級公民,與模組同一層級。這就是「UDP 不是模組,而且不能巢狀寫在模組裡面」的形式化陳述。UDP は第一級の存在であり、モジュールと同じレベルにある。これが「UDP はモジュールではなく、モジュールの内側に入れ子にできない」の形式的な表明である。

The port list is optional. [ list_of_ports ]. This is why a test bench may be written module tb; with no parentheses at all, and why module modB(); with empty parentheses is also legal.埠列表可以省略。[ list_of_ports ]。這就是為什麼測試平台可以寫成完全沒有括號的 module tb;,也是為什麼帶空括號的 module modB(); 同樣合法。ポートリストは省略できる。[ list_of_ports ]。テストベンチを括弧なしの module tb; と書けるのも、空の括弧を持つ module modB(); が合法なのも、このためである。

delay3 is the delay syntax of §5.1, written formally. # delay_value for one value, or parentheses with one, two or three values — the nesting of the square brackets is what encodes "the second is optional, and the third is only possible if the second is present". You cannot write #( , 3).delay3 就是 §5.1 的延遲語法,以形式化方式寫出來。一個值用 # delay_value,或用括號包一個、兩個或三個值 — 方括號的巢狀結構編碼的正是「第二個可省略,而第三個只有在第二個存在時才可能出現」。你不能寫 #( , 3)。delay3 は §5.1 の遅延構文を形式的に書いたものである。値が一つなら # delay_value、あるいは括弧で一つ・二つ・三つの値を囲む — 角括弧の入れ子が「二つ目は省略可能で、三つ目は二つ目があるときにのみ現れうる」を符号化している。#( , 3) とは書けない。

A continuous assignment may carry a drive strength as well as a delay, and the strength comes first. assign (strong1, pull0) #3 y = a; is legal and is how bus models express wired-OR behaviour. Writing them in the other order is a syntax error, and the BNF is where you look to find that out.連續指定除了延遲之外還可以帶驅動強度,而且強度寫在前面。assign (strong1, pull0) #3 y = a; 是合法的,匯流排模型就是這樣表達 wired-OR 行為。順序寫反就是語法錯誤,而 BNF 正是你用來查出這件事的地方。連続代入は遅延に加えてドライブ強度も持て、強度が先に来る。assign (strong1, pull0) #3 y = a; は合法であり、バスのモデルはこうして wired-OR の振る舞いを表現する。順序を逆に書けば構文エラーであり、それを調べる先が BNF である。

10.3 System tasks and functions10.3 系統任務與系統函式10.3 システムタスクとシステム関数

Anything beginning with $ is a system task (a statement) or a system function (returns a value). Not synthesisable They exist for simulation, debugging and test-bench control, and no synthesiser will touch them. Four facts first:凡是以 $ 開頭的都是系統任務(一個敘述)或系統函式(會回傳值)。不可合成 它們是為了模擬、除錯與測試平台控制而存在的,沒有任何合成器會碰它們。先記四件事:$ で始まるものはシステムタスク(文)かシステム関数(値を返す)である。合成不可 これらはシミュレーション、デバッグ、テストベンチ制御のために存在し、合成器は一切触らない。まず四つの事実を押さえる。

Task / function任務/函式タスク/関数What it does它做什麼何をするかWatch out for要注意的地方注意すべき点
$timeCurrent simulation time, 64-bit integer, scaled to the caller's unit and rounded目前的模擬時間,64 位元整數,換算到呼叫者的單位並四捨五入現在のシミュレーション時刻。64 ビット整数で、呼び出し側の単位にスケールされ丸められるRounds, does not truncate (§6.4)是四捨五入,不是截斷(§6.4)切り捨てではなく四捨五入(§6.4)
$realtimeSame, as a real number, not rounded同上,但以實數表示,不四捨五入同じものを実数で返し、丸めないPrint with %f or %t, never %d用 %f 或 %t 印,絕不要用 %d%f か %t で印字する。%d は使わない
$displayPrints once, immediately, appending a newline立刻印出一次,並附上換行直ちに一度印字し、改行を付けるPrints the values at the instant it executes, which in the presence of non-blocking assignments may be the old values印的是它執行那一瞬間的值,而在有非阻斷指定時那可能是舊值実行したその瞬間の値を印字するので、ノンブロッキング代入があると古い値になりうる
$writeLike $display but without the newline與 $display 相同,但不換行$display と同じだが改行しないUseful for building a line in a loop在迴圈中逐段組出一行時很有用ループで一行を組み立てるときに便利
$strobePrints at the end of the current time step, after all assignments have settled在當前時間步的結尾印出,此時所有指定都已塵埃落定現在の時間ステップの最後に、すべての代入が落ち着いてから印字するThe right tool when you want "the final values at this time"當你要的是「此刻的最終值」時,這才是對的工具「この時刻での最終的な値」が欲しいときに正しい道具
$monitorPrints whenever any listed argument changes, at the end of that time step只要列出的任一引數改變就印出,印在該時間步的結尾列挙した引数のいずれかが変化するたびに、その時間ステップの最後に印字するOnly one monitor may be active; a second $monitor replaces the first. Toggle with $monitoron / $monitoroff同時只能有一個 monitor 生效;第二個 $monitor 會取代第一個。用 $monitoron/$monitoroff 切換有効な monitor は同時に一つだけ。二つ目の $monitor は一つ目を置き換える。$monitoron/$monitoroff で切り替える
$finishEnds the simulation and returns to the shell結束模擬並回到 shellシミュレーションを終了してシェルに戻る$finish(2) also prints statistics$finish(2) 還會印出統計資訊$finish(2) は統計情報も印字する
$stopSuspends the simulation and drops into the interactive prompt暫停模擬並進入互動提示字元シミュレーションを中断して対話プロンプトに入るExecution can be resumed; in a batch regression it hangs the job forever之後可以繼續執行;但在批次回歸測試中它會讓整個工作永遠卡住あとで再開できるが、バッチのリグレッションではジョブを永遠に止めてしまう
$randomSigned 32-bit pseudo-random number有號的 32 位元虛擬亂數符号付き 32 ビットの擬似乱数Seed it explicitly or the run is not reproducible要明確給種子,否則這次執行無法重現明示的にシードを与えないと実行が再現できない

The $display versus $strobe versus $monitor distinction is the one that costs time in practice, and it is really a Lecture 2 topic (the stratified event queue) surfacing here. All three print the same values most of the time; they differ exactly when a signal is updated by a non-blocking assignment in the same time step. $display executes in the active region and sees the pre-update value; $strobe and $monitor execute in the monitor region, after the non-blocking update region, and see the post-update value. If your log and your waveform disagree by one clock cycle, this is why.$display、$strobe 與 $monitor 的區別是實務上最花時間的那一個,而它其實是第 2 講的題目(分層事件佇列)在這裡浮出水面。三者大多數時候印出相同的值;它們的差別恰好出現在「某個訊號在同一個時間步被非阻斷指定更新」的時候。$display 在 active 區執行,看到的是更新前的值;$strobe 與 $monitor 在 monitor 區執行,也就是在非阻斷更新區之後,看到的是更新後的值。如果你的 log 和波形差了一個時脈週期,原因就在這裡。$display、$strobe、$monitor の違いは、実務で最も時間を食う区別であり、実のところ第 2 講の話題(階層化イベントキュー)がここに顔を出したものである。三つはたいてい同じ値を印字するが、同じ時間ステップでノンブロッキング代入によって信号が更新される場合にちょうど食い違う。$display はアクティブ領域で実行され更新前の値を見る。$strobe と $monitor はノンブロッキング更新領域のあとのモニタ領域で実行され、更新後の値を見る。ログと波形が 1 クロックずれるのはこれが理由である。

always @(posedge clk) begin
  q <= d;                             // non-blocking: q updates later in this time step
  $display("display: q = %b", q);     // prints the OLD q
  $strobe ("strobe : q = %b", q);     // prints the NEW q
end

Format specifiers follow C with Verilog additions: %b binary, %d decimal, %h hex, %o octal, %c character, %s string, %f/%e/%g real, %t time (formatted by $timeformat), %m the hierarchical name of the enclosing scope, %v net strength. Prefixing with 0, as in %0d, removes the leading-space padding and is almost always what you want in a log.格式指定子沿用 C 並加上 Verilog 的擴充:%b 二進位、%d 十進位、%h 十六進位、%o 八進位、%c 字元、%s 字串、%f/%e/%g 實數、%t 時間(由 $timeformat 決定格式)、%m 所在範圍的階層名稱、%v net 強度。前面加上 0,像 %0d 這樣,會去掉前導空白的補位,而那幾乎永遠是你在 log 裡想要的。書式指定子は C を踏襲し、Verilog の拡張が加わる。%b は 2 進、%d は 10 進、%h は 16 進、%o は 8 進、%c は文字、%s は文字列、%f/%e/%g は実数、%t は時刻(書式は $timeformat が決める)、%m は囲んでいるスコープの階層名、%v は net の強度である。%0d のように 0 を前置すると先頭の空白詰めがなくなる。ログではほぼ常にそれが望ましい。

10.4 File I/O10.4 檔案 I/O10.4 ファイル I/O

module test_bench ();
  reg        x;
  wire       y;
  reg [31:0] cd;                       // multichannel descriptor
  assign y = ~x;

  initial begin
    cd = $fopen("test.dat");           // returns a channel bit, not a FILE*
    #100 x = 0;
    $fdisplay(cd, "%b %b", x, y);
    $fclose(cd);
  end
endmodule

The reg [31:0] in the lecture's example is not arbitrary. The Verilog-1995 $fopen returns a multichannel descriptor: a 32-bit mask in which exactly one bit is set, identifying the opened file. Bit 0 is permanently assigned to the standard output. Because it is a mask, you can OR descriptors together and write to several destinations in one call:講義例子裡的 reg [31:0] 不是隨便寫的。Verilog-1995 的 $fopen 回傳的是一個多通道描述子(multichannel descriptor):一個只有一個位元被設起來的 32 位元遮罩,用來識別那個被開啟的檔案。位元 0 永久分配給標準輸出。因為它是遮罩,你可以把多個描述子 OR 起來,一次呼叫就寫到好幾個目的地:講義の例にある reg [31:0] は恣意的な幅ではない。Verilog-1995 の $fopen が返すのはマルチチャネル記述子であり、ちょうど一つのビットが立った 32 ビットのマスクで、開かれたファイルを識別する。ビット 0 は標準出力に恒久的に割り当てられている。マスクであるから、記述子を OR でまとめて一度の呼び出しで複数の宛先に書ける。

reg [31:0] f1, f2, both;
initial begin
  f1   = $fopen("a.log");
  f2   = $fopen("b.log");
  both = f1 | f2 | 32'h1;              // both files AND the console
  $fdisplay(both, "this line goes to three places");
end

That design limits you to 31 open files. Verilog-2001 added a second form: $fopen("name", "r") with a mode string returns an ordinary file descriptor with its most significant bit set, usable with $fgets, $fscanf, $fread, $feof and the rest of the C-like family. The two forms are distinguished by whether you passed a mode argument. Mixing them up — passing a file descriptor where a multichannel descriptor is expected — writes to a nonsensical set of channels and is a classic confusing bug.這個設計把你限制在 31 個開啟的檔案。Verilog-2001 加上了第二種形式:帶模式字串的 $fopen("name", "r") 回傳一個最高位元被設起來的普通檔案描述子,可以搭配 $fgets、$fscanf、$fread、$feof 以及其餘類 C 的家族成員使用。兩種形式是由「你有沒有傳模式引數」來區分的。把它們搞混 — 在該傳多通道描述子的地方傳了檔案描述子 — 會寫到一組毫無意義的通道,是一個經典的惱人錯誤。この設計は同時に開けるファイルを 31 個に制限する。Verilog-2001 は第二の形式を追加した。モード文字列を伴う $fopen("name", "r") は最上位ビットの立った通常のファイル記述子を返し、$fgets、$fscanf、$fread、$feof など C 風の一族とともに使える。二つの形式はモード引数を渡したかどうかで区別される。これらを取り違え、マルチチャネル記述子を期待する場所にファイル記述子を渡すと、意味をなさないチャネル群に書き込むことになり、典型的な分かりにくいバグになる。

The output family mirrors the display family exactly: $fdisplay, $fwrite, $fstrobe, $fmonitor, each taking the descriptor as the first argument and otherwise behaving identically to its console counterpart.輸出家族與顯示家族完全對應:$fdisplay、$fwrite、$fstrobe、$fmonitor,每一個都以描述子為第一個引數,其餘行為與它在主控台的對應者完全相同。出力の一族は表示の一族と完全に対応する。$fdisplay、$fwrite、$fstrobe、$fmonitor はいずれも記述子を第一引数に取り、それ以外はコンソール版とまったく同じに振る舞う。

10.5 $readmemh and $readmemb10.5 $readmemh 與 $readmemb10.5 $readmemh と $readmemb

parameter ram_file = "ram_data_file";
reg [15:0] RAM_1 [0:1023];                  // 1024 words of 16 bits

initial $readmemh(ram_file, RAM_1);         // file holds hex digits
initial $readmemb(ram_file, RAM_1);         // file holds binary digits

initial $readmemh("boot.hex", RAM_1, 16'h100, 16'h1FF);  // load only that range

The file format is simple and worth knowing exactly, because most $readmem problems are file-format problems:檔案格式很簡單,值得精確地記住,因為大多數 $readmem 的問題其實都是檔案格式的問題:ファイル形式は単純だが正確に覚えておく価値がある。$readmem の問題のほとんどは、実はファイル形式の問題だからである。

Two corrections to the slide投影片的兩處勘誤スライドの二つの訂正

"Verilog only supports 2-D array." What the lecture means is that a Verilog-1995 memory is a one-dimensional array of vectors — reg [15:0] M [0:1023] — which when drawn on paper looks like a 1024×16 rectangle. Verilog-2001 removed the restriction and allows genuinely multidimensional arrays, reg [15:0] M [0:3][0:255]. The rule that still holds is the one that matters here: $readmemh and $readmemb load a one-dimensional array of regs, and pointing them at a multidimensional array or at a packed vector is an error.「Verilog 只支援二維陣列。」講義想說的是:Verilog-1995 的記憶體是一維的向量陣列 — reg [15:0] M [0:1023] — 畫在紙上看起來像一個 1024×16 的長方形。Verilog-2001 已經解除了這個限制,允許真正的多維陣列 reg [15:0] M [0:3][0:255]。仍然成立、而且在這裡才是重點的規則是:$readmemh 與 $readmemb 載入的是一維的 reg 陣列,把它們指向多維陣列或指向一個壓縮向量都是錯誤。「Verilog は 2 次元配列しかサポートしない。」講義が言いたいのはこうである。Verilog-1995 のメモリはベクタの一次元配列 — reg [15:0] M [0:1023] — であり、紙に描くと 1024×16 の長方形に見える。Verilog-2001 はこの制限を外し、真に多次元の配列 reg [15:0] M [0:3][0:255] を許した。いまも成り立ち、ここで肝心な規則はこちらである。$readmemh と $readmemb が読み込むのは reg の一次元配列である。多次元配列やパックされたベクタを指すのは誤りだ。

The example file is binary. The slide shows 0110_1101_0100_1011 and offers both $readmemh and $readmemb for it. Only $readmemb is correct. Read with $readmemh, each of those 16 characters is a hex digit, so the token is a 64-bit value being loaded into a 16-bit word — the simulator truncates and you silently get the wrong contents. There is no check that the file matches the task, because a file of 0s and 1s is perfectly valid hex.範例檔案是二進位的。投影片顯示 0110_1101_0100_1011,並同時提供 $readmemh 與 $readmemb 兩種讀法。只有 $readmemb 是正確的。用 $readmemh 讀的話,那 16 個字元每一個都是十六進位數字,於是那個記號是一個 64 位元的值要被載入到 16 位元的字裡 — 模擬器會截斷,而你默默得到錯誤的內容。不會有任何檢查告訴你檔案與任務不相符,因為一個全是 0 與 1 的檔案本來就是完全合法的十六進位。例のファイルは 2 進である。スライドは 0110_1101_0100_1011 を示し、それに対して $readmemh と $readmemb の両方を挙げている。正しいのは $readmemb だけだ。$readmemh で読むと、その 16 文字はそれぞれ 16 進の桁になるので、トークンは 64 ビットの値となり 16 ビットのワードに読み込まれる — シミュレータは切り詰め、内容は黙って誤ったものになる。ファイルとタスクの不一致を検査する仕組みはない。0 と 1 だけのファイルは完全に正しい 16 進でもあるからだ。

10.6 Compiler directives10.6 編譯指令10.6 コンパイラ指令

Directives begin with a backtick, are processed by the preprocessor before parsing, and — this is the point to internalise — are not scoped. They take effect from where they are read until something changes them, across module boundaries and across file boundaries, in compilation order.指令以反引號開頭,在剖析之前由前處理器處理,而且 — 這一點要內化 — 不受作用範圍限制。它們從被讀到的地方開始生效,直到有東西改變它們為止,跨越模組邊界、跨越檔案邊界,依編譯順序而定。指令はバッククォートで始まり、構文解析の前にプリプロセッサが処理する。そして — これを体に染み込ませること — スコープを持たない。読み込まれた地点から、何かが変えるまで効力を持ち、モジュールの境界もファイルの境界も越え、コンパイル順序に従う。

Directive指令指令Example範例例Effect and caveat作用與注意事項効果と注意点
`timescale`timescale 1ns/10psTime unit / precision for every module that follows. See §6.5 for the hazards.其後每一個模組的時間單位/精度。危險之處見 §6.5。以降のすべてのモジュールの時間単位/精度。危険については §6.5 を参照。
`include`include "defs.vh"Literal textual insertion, like C. Include guards are your problem; Verilog has no #pragma once.像 C 一樣以文字方式插入。include guard 得你自己處理;Verilog 沒有 #pragma once。C と同様の字句的な挿入。インクルードガードは自分で用意する必要がある。Verilog に #pragma once はない。
`define / `undef`define WAIT_ST 3'b010Text macro, expanded as `WAIT_ST. Global from the point of definition — a macro defined in one file is visible in every file compiled after it.文字巨集,使用時展開為 `WAIT_ST。從定義的那一點起就是全域的 — 在某個檔案裡定義的巨集,在它之後編譯的每一個檔案中都看得見。テキストマクロ。`WAIT_ST として展開される。定義された地点から全域であり、あるファイルで定義したマクロは、以後コンパイルされるすべてのファイルから見える。
`ifdef / `ifndef / `elsif / `else / `endif`ifdef SYNTHESIS ... `endifConditional compilation. The standard way to hide system tasks and assertions from the synthesiser.條件編譯。把系統任務與斷言藏起來不讓合成器看到的標準做法。条件コンパイル。システムタスクやアサーションを合成器から隠す標準的な手段。
`default_nettype`default_nettype noneType given to an implicitly declared net. none disables implicit declaration entirely.隱含宣告之 net 所採用的型態。none 會完全停用隱含宣告。暗黙に宣言される net に与えられる型。none は暗黙宣言を完全に無効にする。
`celldefine / `endcelldefinewraps a library cell包住一顆單元庫單元ライブラリセルを囲むMarks the enclosed modules as library cells. Tools use the flag to exclude cells from coverage, to speed up annotation, and to decide what counts as a leaf.把被包住的模組標記為單元庫單元。工具用這個旗標把單元排除在覆蓋率之外、加速標註,以及決定什麼算是葉節點。囲まれたモジュールをライブラリセルとして印づける。ツールはこの印を使ってセルをカバレッジから除外し、アノテーションを速め、何を葉とみなすかを決める。
`resetalltop of a library file單元庫檔案的最前面ライブラリファイルの先頭Restores every directive to its default, including undefining all macros. Safe at the top of a self-contained file, dangerous anywhere else.把每一個指令都還原為預設值,包括取消所有巨集的定義。放在自給自足的檔案最前面是安全的,放在其他任何地方都危險。すべての指令を既定値に戻す。定義済みマクロをすべて取り消すことも含む。自己完結したファイルの先頭なら安全だが、それ以外の場所では危険である。
`unconnected_drive / `nounconnected_drive`unconnected_drive pull1Decides what an unconnected module input is tied to. Used in library files where leaving an input floating would produce x.決定一個未接的模組輸入會被綁到什麼。用在單元庫檔案裡,因為在那裡讓輸入浮接會產生 x。未接続のモジュール入力を何に結ぶかを決める。ライブラリファイルで使われる。そこでは入力を浮かせると x になるからだ。
`line`line 42 "src.v" 0Rewrites the file/line the compiler reports. Emitted by generators so that error messages point at the original source.改寫編譯器回報的檔名/行號。由產生器輸出,好讓錯誤訊息指向原始的來源。コンパイラが報告するファイル名/行番号を書き換える。エラーメッセージが元のソースを指すように、ジェネレータが出力する。

`default_nettype: the most useful line in the table`default_nettype:表中最有用的一行`default_nettype:表の中で最も有用な一行

By default, using an undeclared identifier in a port connection or on the right of a continuous assignment creates a one-bit wire silently. That rule exists for Verilog-1995 convenience and it is a disaster for typos:預設情況下,在埠連接中或在連續指定的右側使用一個未宣告的識別字,會默默建立一條一位元的 wire。那條規則是為了 Verilog-1995 的方便而存在,但對打錯字來說是場災難:既定では、ポート接続や連続代入の右辺で未宣言の識別子を使うと、1 ビットの wire が黙って作られる。この規則は Verilog-1995 の利便のために存在するが、タイプミスにとっては災厄である。

Bad — the typo compiles壞 — 打錯字照樣編譯過悪い例 — タイプミスがコンパイルを通る
module m (input clk, output q);
  wire data_valid;
  ...
  assign q = data_vaild & clk;
  // data_vaild is implicitly created
  // as a 1-bit wire, never driven,
  // so q is x forever. No warning
  // from most tools.
endmodule
Good — the typo is an error好 — 打錯字會變成錯誤良い例 — タイプミスがエラーになる
`default_nettype none
module m (input wire clk, output wire q);
  wire data_valid;
  ...
  assign q = data_vaild & clk;
  // ERROR: 'data_vaild' is not declared.
  // Caught at compile time.
endmodule
`default_nettype wire   // restore before the next file

Two practical notes. With none in force, every module port must have an explicit type — input wire clk, not input clk — because the port declaration itself relies on the implicit rule. And because the directive is not scoped, always restore it with `default_nettype wire at the end of your file, or the next file compiled (perhaps someone else's IP) will fail for reasons that have nothing to do with it.兩點實務說明。在 none 生效期間,每一個模組埠都必須有明確的型態 — 要寫 input wire clk,不能寫 input clk — 因為埠宣告本身就依賴那條隱含規則。另外,由於這個指令不受作用範圍限制,請務必在你的檔案結尾用 `default_nettype wire 還原,否則下一個被編譯的檔案(也許是別人的 IP)會因為與它毫無關係的理由而失敗。実務上の注意が二つある。none が有効なあいだ、モジュールのポートはすべて明示的な型を持たねばならない — input clk ではなく input wire clk と書く。ポート宣言自体が暗黙の規則に依存しているからだ。もう一つ、この指令はスコープを持たないので、ファイルの末尾で必ず `default_nettype wire に戻すこと。さもないと、次にコンパイルされるファイル(他人の IP かもしれない)が、それとは無関係な理由で失敗する。

Slide corrections — spelling and advice投影片勘誤 — 拼法與建議スライドの訂正 — 綴りと助言

The slide writes `defaultnettype wor. Two problems.投影片寫的是 `defaultnettype wor。有兩個問題。スライドには `defaultnettype wor とある。問題が二つある。

The spelling. The directive is `default_nettype, with an underscore. `defaultnettype is not a Verilog directive and will be reported as an unknown directive.拼法。這個指令是 `default_nettype,中間有底線。`defaultnettype 不是 Verilog 的指令,會被回報為未知指令。綴り。この指令は `default_nettype であり、アンダースコアが入る。`defaultnettype は Verilog の指令ではなく、未知の指令として報告される。

The argument. wor is legal but is close to the worst possible choice. It makes every implicitly declared net a wired-OR, so two drivers on a mistyped net quietly OR together instead of resolving to x. The failure mode you most want to see — a net with two drivers going unknown — is suppressed by exactly the mechanism meant to help you. Use none during development; use wire (the default) if you must allow implicit nets. Reserve wor, wand, tri and friends for nets you declare explicitly, where the resolution is intentional.引數。wor 合法,但幾乎是最糟的選擇。它會讓每一條隱含宣告的 net 都變成 wired-OR,於是一條打錯字的 net 上的兩個驅動器會悄悄 OR 在一起,而不是解析成 x。你最希望看到的那個失效模式 — 一條有兩個驅動器的 net 變成未知 — 恰好被那個號稱要幫你的機制壓了下去。開發期間用 none;若你非得允許隱含 net,就用 wire(預設值)。把 wor、wand、tri 這些留給你明確宣告、解析行為是刻意為之的 net。引数。wor は合法だが、ほぼ最悪の選択である。暗黙に宣言されるすべての net が wired-OR になるので、綴りを誤った net 上の二つのドライバは x に解決されず静かに OR される。最も見たい失敗の形 — 二つのドライバを持つ net が不定になること — が、助けになるはずの仕組みによってちょうど抑え込まれる。開発中は none を使い、暗黙の net をどうしても許す必要があるなら wire(既定値)にすること。wor、wand、tri の類は、明示的に宣言し解決が意図的である net のために取っておく。

Why `celldefine matters for this lecture為什麼 `celldefine 對本講很重要なぜ `celldefine が本講で重要か

Every standard-cell model is wrapped in `celldefine ... `endcelldefine, and the reason connects straight back to section 8. The flag tells downstream tools "this module is a leaf of the physical hierarchy; its timing comes from its specify block and its SDF annotation, and you should not look inside it". SDF annotators use it to decide where the CELL scopes in the SDF file map to. Coverage tools use it to exclude library cells from toggle coverage, which would otherwise be dominated by a million inverters. Profilers use it to account cell evaluation separately. It costs two lines and it is the difference between a library that integrates cleanly and one that fights every tool in the flow.每一個標準單元的模型都被包在 `celldefine ... `endcelldefine 裡,而理由直接連回第 8 節。這個旗標告訴下游工具:「這個模組是實體階層的葉節點;它的時序來自它的 specify 區塊與 SDF 標註,你不該往裡面看。」SDF 標註器用它來決定 SDF 檔案裡的 CELL 範圍要對應到哪裡。覆蓋率工具用它把單元庫單元排除在翻轉覆蓋率之外,否則那會被上百萬顆反相器淹沒。效能分析器用它把單元求值分開計帳。它只要兩行,卻是「一座能乾淨整合的單元庫」與「一座跟流程中每個工具打架的單元庫」之間的差別。標準セルのモデルはどれも `celldefine ... `endcelldefine に包まれており、その理由は第 8 節に直結する。この印は下流のツールにこう告げる。「このモジュールは物理階層の葉である。そのタイミングは specify ブロックと SDF アノテーションから来るのだから、中を覗くべきではない」。SDF アノテータはこれを使って、SDF ファイルの CELL スコープの対応先を決める。カバレッジツールはこれを使ってライブラリセルをトグルカバレッジから除く。さもなければ百万個のインバータに埋もれてしまう。プロファイラはセルの評価を別勘定にする。費用は二行、そしてそれが、流れの中のすべてのツールときれいに統合されるライブラリと、ことごとく喧嘩するライブラリの差である。

11. Summary, Checklist and Cross-References11. 總結、檢查表與交叉參照11. まとめ、チェックリスト、相互参照

11.1 Exam checklist11.1 考前檢查表11.1 試験前チェックリスト

UDP structureUDP 的結構UDP の構造
  • One output, always first; no inout; output never z.只有一個輸出,而且一定排在最前面;不能有 inout;輸出永遠不會是 z。出力は一つだけで必ず先頭。inout は不可。出力が z になることはない。
  • Combinational: n+1 columns, output is a net.組合邏輯:n+1 欄,輸出是 net。組み合わせ:n+1 列、出力は net。
  • Sequential: n+2 columns, output is a reg, state = current output.循序邏輯:n+2 欄,輸出是 reg,狀態 = 目前的輸出。順序:n+2 列、出力は reg、状態 = 現在の出力。
  • At most 10 inputs combinational, 9 sequential.組合邏輯最多 10 個輸入,循序邏輯最多 9 個。入力は組み合わせで最大 10 個、順序で最大 9 個。
  • Unlisted combination → x. A z input → x.未列出的組合 → x。輸入的 z → x。列挙されていない組み合わせ → x。入力の z → x。
Table symbols表格符號テーブルの記号
  • ? = {0,1,x}; b = {0,1}; never in the output field.? = {0,1,x};b = {0,1};兩者都絕不可用於輸出欄。? = {0,1,x}、b = {0,1}。どちらも出力フィールドには使えない。
  • - = hold; output field of a sequential UDP only.- = 維持;只能用於循序邏輯 UDP 的輸出欄。- = 保持。順序 UDP の出力フィールドのみ。
  • (vw), r=(01), f=(10), p, n, *=(??).
  • One edge specifier per row; one input changes per evaluation.每一列只能有一個邊緣指定子;每次求值只有一個輸入變化。一行につきエッジ指定子は一つ。評価ごとに変化する入力は一つ。
  • Level rows beat edge rows; otherwise no priority.準位列勝過邊緣列;除此之外沒有優先順序。レベルの行はエッジの行に勝つ。それ以外に優先順位はない。
Gate delay閘延遲ゲート遅延
  • #(rise, fall, turn-off), each optionally min:typ:max.#(上升, 下降, 關斷),每一個都可以寫成 min:typ:max。#(立ち上がり, 立ち下がり, ターンオフ)。それぞれ min:typ:max と書いてよい。
  • The destination value selects the delay.由目的地的值決定採用哪個延遲。行き先の値が遅延を選ぶ。
  • To x: the minimum. x→z: $d_z$ with three delays, else the minimum.變成 x:取最小值。x→z:有三個延遲時用 $d_z$,否則取最小值。x へ:最小値。x→z:遅延が三つなら $d_z$、それ以外は最小値。
  • Inertial: a pending output event is cancelled by a new one.慣性:待決的輸出事件會被新的事件取消。慣性:保留中の出力イベントは新しいイベントに取り消される。
  • Survival: $w > $ the leading-edge delay.存活條件:$w > $ 前緣的延遲。生存条件:$w > $ 先行エッジの遅延。
Time時間時間
  • `timescale unit / precision; only 1, 10, 100.`timescale 單位 / 精度;只能用 1、10、100。`timescale 単位 / 精度。使えるのは 1、10、100 のみ。
  • Rounding happens once, at compile time, in the local module.四捨五入只發生一次,在編譯期,在本地模組裡。丸めは一度だけ、コンパイル時に、ローカルなモジュールで起こる。
  • Global precision = the finest in the design.全域精度 = 設計中最細的那一個。全体の精度 = 設計中で最も細かいもの。
  • $time rounds; $realtime does not; both use the caller's unit.$time 會四捨五入;$realtime 不會;兩者都用呼叫者的單位。$time は丸め、$realtime は丸めない。どちらも呼び出し側の単位を使う。
Net and path delaynet 延遲與路徑延遲net 遅延とパス遅延
  • wire #d y; adds to the driver's delay.wire #d y; 會加到驅動器的延遲上。wire #d y; はドライバの遅延に加算される。
  • wire #d y = expr; is only the assignment delay.wire #d y = expr; 只是指定的延遲。wire #d y = expr; は代入の遅延だけである。
  • => parallel (equal widths), *> full (all to all).=> 平行(位寬相同),*> 全連接(全部對全部)。=> は並列(幅が同じ)、*> は全接続(すべて対すべて)。
  • if (c) state-dependent, ifnone default, posedge edge-sensitive.if (c) 是狀態相依、ifnone 是預設、posedge 是邊緣敏感。if (c) は状態依存、ifnone は既定、posedge はエッジ感応。
  • Path vs distributed: the larger wins.路徑延遲對分布延遲:較大者勝。パス遅延と分布遅延:大きいほうが勝つ。
PATHPULSE$
  • PATHPULSE$src$dst = (reject, error).
  • PATHPULSE$ = v covers all other paths; one value sets both limits.PATHPULSE$ = v 涵蓋其餘所有路徑;只給一個值就同時設定兩個界限。PATHPULSE$ = v は他のすべてのパスを覆う。値を一つ与えると両方の限界が設定される。
  • $w<r$ rejected; $r \le w < e$ gives x; $w \ge e$ passes.$w<r$ 被拒絕;$r \le w < e$ 給出 x;$w \ge e$ 通過。$w<r$ は拒否、$r \le w < e$ は x、$w \ge e$ は通過。
  • No PATHPULSE$ at all: both limits equal the path delay.完全沒有 PATHPULSE$:兩個界限都等於路徑延遲。PATHPULSE$ が一切なければ、両方の限界がパス遅延に等しい。
System tasks系統任務システムタスク
  • Never synthesisable.永遠不可合成。決して合成できない。
  • $display now, $strobe end of step, $monitor on change (one at a time).$display 當下印、$strobe 在時間步結尾印、$monitor 在變化時印(同時只能有一個)。$display はその場で、$strobe は時間ステップの最後に、$monitor は変化時に印字する(同時に一つだけ)。
  • $fopen returns a channel mask (1995) or a descriptor (2001).$fopen 回傳通道遮罩(1995)或檔案描述子(2001)。$fopen はチャネルマスク(1995)かファイル記述子(2001)を返す。
  • $readmemh/$readmemb load a 1-D array of regs; @addr relocates.$readmemh/$readmemb 載入一維的 reg 陣列;@addr 可重新定位。$readmemh/$readmemb は reg の一次元配列を読み込む。@addr で位置を変えられる。
  • $finish ends, $stop pauses.$finish 結束、$stop 暫停。$finish は終了、$stop は中断。
Directives編譯指令コンパイラ指令
  • Not scoped; order of compilation matters.不受作用範圍限制;編譯順序會影響結果。スコープを持たない。コンパイルの順序が効いてくる。
  • `default_nettype none catches typos; the slide's spelling is wrong.`default_nettype none 能抓到打錯的字;投影片的拼法是錯的。`default_nettype none はタイプミスを捕まえる。スライドの綴りは誤りである。
  • `celldefine marks library cells.`celldefine 用來標記單元庫單元。`celldefine はライブラリセルを印づける。
  • `resetall also undefines every macro.`resetall 也會取消所有巨集的定義。`resetall はすべてのマクロ定義も取り消す。
  • `ifdef SYNTHESIS hides simulation-only code.`ifdef SYNTHESIS 可以把只在模擬時用的程式碼藏起來。`ifdef SYNTHESIS はシミュレーション専用のコードを隠す。

11.2 Traps that cost marks11.2 會扣分的陷阱11.2 点を落とす落とし穴

  1. Putting ? in the output column of a UDP table, or - in an input column. Both are syntax errors and both come from Karnaugh-map habits.把 ? 放進 UDP 表的輸出欄,或把 - 放進輸入欄。兩者都是語法錯誤,而且都來自卡諾圖的習慣。UDP テーブルの出力列に ? を置く、あるいは入力列に - を置くこと。どちらも構文エラーであり、どちらもカルノー図の習慣から来る。
  2. Forgetting the catch-all hold rows in a sequential UDP. Without (?0) ? : ? : -; and ? (??) : ? : -; the flip-flop is destroyed by the first falling clock edge or the first data change.在循序邏輯 UDP 中忘記寫包山包海的保持列。少了 (?0) ? : ? : -; 與 ? (??) : ? : -;,正反器會被第一個時脈下降緣或第一次資料變化毀掉。順序 UDP で総括的な保持行を忘れること。(?0) ? : ? : -; と ? (??) : ? : -; がなければ、フリップフロップは最初のクロック立ち下がりか最初のデータ変化で壊れる。
  3. Forgetting the clear-release row. When an asynchronous clear deasserts, that is an input event, and if no row matches it the output goes to x the instant you come out of reset.忘記清除解除那一列。非同步清除解除時那是一個輸入事件,如果沒有任何一列對應它,輸出會在你剛離開重置的瞬間變成 x。クリア解除の行を忘れること。非同期クリアのディアサートは入力イベントであり、一致する行がなければ、リセットを抜けた瞬間に出力が x になる。
  4. Choosing the delay by the source value. It is always the destination: x→1 uses the rising delay.依來源值選延遲。永遠是依目的地:x→1 用上升延遲。遷移元の値で遅延を選ぶこと。選ぶのは常に行き先である。x→1 は立ち上がり遅延を使う。
  5. Forgetting that transitions to x use the minimum delay. This is the only non-obvious row of the mapping table and it is examined every year.忘記變成 x 的轉態取最小延遲。這是對應表中唯一不顯然的一列,而且年年考。x への遷移が最小の遅延を使うことを忘れること。マッピング表で唯一自明でない行であり、毎年出題される。
  6. Assuming wire #5 y = a ^ b; gives 10. It gives 5.以為 wire #5 y = a ^ b; 給出 10。它給出 5。wire #5 y = a ^ b; が 10 を与えると思い込むこと。答えは 5 である。
  7. Assuming a specify path adds to the gate delays. The larger is used, not the sum.以為 specify 路徑會疊加到閘延遲上。採用的是較大者,不是總和。specify のパスがゲート遅延に加算されると思い込むこと。使われるのは和ではなく大きいほうである。
  8. Truncating instead of rounding in $time. 5.7 prints as 6.在 $time 做截斷而不是四捨五入。5.7 會印成 6。$time で四捨五入せず切り捨てること。5.7 は 6 と印字される。
  9. Letting a delay round to zero because the precision is coarser than the delay.讓延遲被捨成零,因為精度比那個延遲還粗。精度が遅延より粗いために遅延がゼロに丸められること。
  10. Reading a binary memory image with $readmemh — no error, wrong contents.用 $readmemh 讀一份二進位的記憶體映像檔 — 不會報錯,但內容是錯的。2 進のメモリイメージを $readmemh で読むこと — エラーは出ないが内容が誤る。
  11. Confusing => and *> on vector ports. Parallel needs equal widths; full creates $n \times m$ arcs.在向量埠上混淆 => 與 *>。平行連接要求位寬相同;全連接會建立 $n \times m$ 條弧。ベクタのポートで => と *> を取り違えること。並列は幅が等しいことを要求し、全接続は $n \times m$ 本のアークを作る。
  12. Writing an unconditional (D => Q) path for a latch, creating a combinational path straight through a closed latch.為閂鎖器寫出無條件的 (D => Q) 路徑,於是造出一條直接穿過關閉中閂鎖器的組合路徑。ラッチに無条件の (D => Q) パスを書くこと。閉じたラッチを突き抜ける組み合わせパスを作ってしまう。

11.3 Where this lecture connects11.3 本講與其他講的連結11.3 この講義がつながる先

One sentence to carry away一句話帶走一文だけ持ち帰るなら

A UDP says what a cell computes, a specify block says when, and the compiler directives say under what assumptions — and a post-layout simulation is only meaningful when all three have been got right at the same time.UDP 說的是一顆單元算什麼,specify 區塊說的是什麼時候,而編譯指令說的是在什麼假設之下 — 而佈局後模擬只有在這三者同時都做對的時候才有意義。UDP はセルが何を計算するかを述べ、specify ブロックはいつかを述べ、コンパイラ指令はどのような前提のもとでかを述べる — そしてレイアウト後シミュレーションは、この三つが同時に正しいときにのみ意味を持つ。