A FIFO-based temperature monitor system and the AMD2901 4-bit bit-slice microcontroller, built module by module in synthesizable Verilog.以 FIFO 為核心的溫度監測系統,以及 AMD2901 四位元 bit-slice 微控制器,逐一模組地用可合成的 Verilog 建構 —— 設計範例自學指南。FIFO を中核とする温度モニタシステムと、AMD2901 4 ビット・ビットスライス・マイクロコントローラを、合成可能な Verilog でモジュールごとに組み上げる。
| Port埠ポート | Direction方向方向 | Meaning意義意味 |
|---|---|---|
Data_in[11:0] | in輸入入力 | Data path into the FIFO進入 FIFO 的資料路徑FIFO への入力データパス |
Data_out[11:0] | out輸出出力 | Data path from the FIFO自 FIFO 輸出的資料路徑FIFO からの出力データパス |
stack_empty, stack_full | out輸出出力 | Flags asserted high when empty / full空/滿時被拉高的旗標空/満杯のとき High になるフラグ |
clk, rst | in輸入入力 | External clock and reset外部時脈與重置訊號外部クロックとリセット |
write_to_stack, read_from_stack | in輸入入力 | Write / read requests寫入/讀出請求書き込み/読み出し要求 |
module FIFO (Data_out, Data_in, stack_empty, stack_full,
clk, rst, write_to_stack, read_from_stack);
parameter stack_width = 12; // width of stack and data paths
parameter stack_height = 8; // height of stack (in # of words)
parameter stack_ptr_width = 3; // width of pointer to address stack
output [stack_width-1:0] Data_out;
output stack_empty, stack_full;
input [stack_width-1:0] Data_in;
input clk, rst;
input write_to_stack, read_from_stack;
reg [stack_ptr_width-1:0] read_ptr, write_ptr; // pointers for reading and writing
reg [stack_ptr_width:0] ptr_diff; // gap between ptrs (0..8 needs 4 bits)
reg [stack_width-1:0] Data_out;
reg [stack_width-1:0] stack [stack_height-1:0]; // memory array
assign stack_empty = (ptr_diff == 0) ? 1'b1 : 1'b0;
assign stack_full = (ptr_diff == stack_height) ? 1'b1 : 1'b0;
always @ (posedge clk or posedge rst) begin: data_transfer
if (rst) Data_out = 0;
else if ((read_from_stack) && (!write_to_stack) && (!stack_empty))
Data_out <= stack[read_ptr];
else if ((write_to_stack) && (!read_from_stack) && (!stack_full))
stack[write_ptr] <= Data_in;
end // data_transfer
always @ (posedge clk or posedge rst) begin: update_stack_ptrs
if (rst) begin
read_ptr <= 0;
write_ptr <= 0;
ptr_diff <= 0;
end
else if ((write_to_stack) && (!stack_full) && (!read_from_stack)) begin
write_ptr <= write_ptr + 1; // address for next clock edge
ptr_diff <= ptr_diff + 1;
end
else if ((!write_to_stack) && (!stack_empty) && (read_from_stack)) begin
read_ptr <= read_ptr + 1;
ptr_diff <= ptr_diff - 1;
end
end // update_stack_ptrs
endmodule
ptr_diff is one bit wider than the pointers so it can tell empty (0) from full (8).ptr_diff 比指標多一個位元,才能區分空(0)與滿(8)。ptr_diff はポインタより 1 ビット広く、空(0)と満杯(8)を区別できる。Data_out is registered: data appears one clock after the read request is sampled.Data_out 是暫存輸出:資料在讀出請求被取樣後的下一個時脈才出現。Data_out はレジスタ出力であり、読み出し要求がサンプルされた 1 クロック後にデータが現れる。Problem: After reset, the following requests are sampled on successive rising edges: W(10), W(20), W(30), R, R+W(40), R, R, R. Give ptr_diff, stack_empty and Data_out after each edge.問題:重置之後,下列請求依序在連續的上升邊緣被取樣:W(10)、W(20)、W(30)、R、R+W(40)、R、R、R。請寫出每個邊緣之後的 ptr_diff、stack_empty 與 Data_out。問題:リセット後、次の要求が連続する立ち上がりエッジでサンプルされる:W(10)、W(20)、W(30)、R、R+W(40)、R、R、R。各エッジ後の ptr_diff、stack_empty、Data_out を示せ。
| Edge時脈邊緣エッジ | Request請求要求 | ptr_diff | emptyempty(空)empty(空) | Data_out |
|---|---|---|---|---|
| 1 | W 10 | 1 | 0 | 0 |
| 2 | W 20 | 2 | 0 | 0 |
| 3 | W 30 | 3 | 0 | 0 |
| 4 | R | 2 | 0 | 10 |
| 5 | R + W | 2 | 0 | 10 (ignored)10(被忽略)10(無視) |
| 6 | R | 1 | 0 | 20 |
| 7 | R | 0 | 1 | 30 |
| 8 | R | 0 | 1 | 30 (empty, ignored)30(已空,被忽略)30(空のため無視) |
Word 40 is lost at edge 5 because the model discards simultaneous requests.資料 40 在第 5 個邊緣遺失,因為這個模型會丟棄同時發生的讀寫請求。この模型は同時要求を破棄するため、ワード 40 は 5 番目のエッジで失われる。
module temp_register (temp_out, temp_in, clk, rst);
output [11:0] temp_out;
input [11:0] temp_in;
input clk, rst;
reg [11:0] temp_out;
always @ (posedge clk or posedge rst)
begin
if (rst == 1) temp_out = 0;
else temp_out = temp_in;
end
endmodule
Stores (latches) the input data: twelve DFFs with asynchronous reset (the netlist shows an inverter on rst because the library flip-flops have active-low reset).把輸入資料存起來:十二個帶非同步重置的 D 型正反器(netlist 上 rst 前多了一個反相器,因為元件庫裡的正反器是低電位有效重置)。入力データを保持する。非同期リセット付きの D フリップフロップ 12 個である(ライブラリのフリップフロップがアクティブ Low リセットのため、netlist では rst にインバータが付く)。
| Description寫法記述 | Status狀態可否 |
|---|---|
assign temp_out = temp_in * 9 / 5 + 32; | Non-synthesizable (general divider)不可合成(需要通用除法器)合成不可(汎用の除算器が必要) |
assign temp_out = (9 * temp_in) / 5 + 32; | Non-synthesizable不可合成合成不可 |
parameter k1 = 2'b10; k2 = 3'b100; k32 = 6'b100000;assign temp_out = (temp_in + temp_in / k1 + temp_in / k2) + k32; | Synthesizable (divide by powers of 2) but incorrect: computes 1.75·T + 32, the common approximation in IC design可合成(除數都是 2 的冪次),但結果不精確:算出的是 1.75·T + 32,這是 IC 設計裡常用的近似合成可能(除数が 2 のべき乗)だが正確ではない。計算されるのは 1.75·T + 32 で、IC 設計でよく使われる近似である |
module temp_converter (temp_out, temp_in); input [11:0] temp_in; output [11:0] temp_out; parameter k32 = 6'b100000; // decimal 32 // F = 9/5 C + 32, 9/5 = 1.8 ~ 1 + .5 + .25 assign temp_out = temp_in + (temp_in >> 1) + (temp_in >> 2) + k32; endmodule
assign temp_out = ((temp_in << 1)
- (temp_in >> 2)) + k32;
Shifts are free wiring, so each version costs only adders / one subtractor. Alternative 2 uses one fewer adder.移位只是接線,不花面積,所以兩種寫法的成本只有加法器/一個減法器。寫法二少用一個加法器。シフトは配線だけで済むため、どちらの方式も加算器(と減算器 1 個)しか必要としない。方式 2 は加算器が 1 個少ない。
module temp_filter (av_temp, clk, rst, temp_data);
output [11:0] av_temp;
input clk;
input [11:0] temp_data;
reg [11:0] temp [3:0];
always @ (negedge clk or posedge rst)
if (rst) begin
temp[0] <= 0; temp[1] <= 0; temp[2] <= 0; temp[3] <= 0;
end
else begin: data_pipeline
temp[3] <= temp_data;
temp[2] <= temp[3];
temp[1] <= temp[2];
temp[0] <= temp[1];
end
assign av_temp = (temp[3] + temp[2] + temp[1] + temp[0]) >> 2;
endmodule
A 4-deep, 12-bit shift register (48 flip-flops, clocked on the falling edge) and an adder tree followed by >> 2 = divide by 4: a moving average of the last four samples. (rst is missing from the slide's port declarations; add input rst;.)一個 4 級、12 位元的移位暫存器(48 個正反器,以時脈下降緣觸發),後面接加法樹再做 >> 2,也就是除以 4:最近四筆樣本的移動平均。(投影片的埠宣告漏了 rst,要補上 input rst;。)4 段・12 ビットのシフトレジスタ(フリップフロップ 48 個、クロックの立ち下がりエッジ動作)に加算器ツリーを続け、最後に >> 2(4 で割る)を行う。すなわち直近 4 サンプルの移動平均である。(スライドのポート宣言には rst が抜けているので input rst; を追加する。)
The operands of >> are context-determined: the sum is computed at the width of the largest operand / target, 12 bits, so it overflows before the shift when the samples are large. Safe form: wire [13:0] sum = temp[3] + temp[2] + temp[1] + temp[0]; assign av_temp = sum[13:2];>> 的運算元寬度是由上下文決定的:總和會以最寬的運算元/目標寬度(這裡是 12 位元)計算,所以樣本一大就會在移位之前先溢位。安全的寫法是:wire [13:0] sum = temp[3] + temp[2] + temp[1] + temp[0]; assign av_temp = sum[13:2];>> のオペランド幅は文脈で決まる。総和は最も広いオペランド/代入先の幅、すなわち 12 ビットで計算されるため、サンプルが大きいとシフトの前にオーバフローする。安全な書き方は wire [13:0] sum = temp[3] + temp[2] + temp[1] + temp[0]; assign av_temp = sum[13:2]; である。
module fifo_controller (data_out, data_1, data_2, select, read, write,
empty_1, full_1, read_1, empty_2, full_2, read_2);
input [11:0] data_1, data_2;
input [1:0] select;
input read, full_1, empty_1, full_2, empty_2;
output [11:0] data_out;
output read_1, read_2, write;
reg [11:0] data_out;
reg read_1, read_2, write;
always @ (select or data_1 or data_2 or read or full_1
or empty_1 or full_2 or empty_2) begin
read_1 = 0;
read_2 = 0;
if (read == 1)
begin
write = 0;
case (select)
2'b01: begin data_out = data_1; read_1 <= 1; end
2'b10: begin data_out = data_2; read_2 <= 1; end
2'b00: begin data_out = {empty_1, empty_2, full_1, full_2}; end
default: data_out = 12'bz;
endcase
end
else if (read == 0)
begin
data_out = 12'bz;
write = 1;
end
else data_out = 12'bz;
end
endmodule
| read | select | data_out | read_1 / read_2 | write |
|---|---|---|---|---|
| 1 | 01 | data from FIFO 1來自 FIFO 1 的資料FIFO 1 からのデータ | 1 / 0 | 0 |
| 1 | 10 | data from FIFO 2來自 FIFO 2 的資料FIFO 2 からのデータ | 0 / 1 | 0 |
| 1 | 00 | status {empty_1, empty_2, full_1, full_2} (zero-extended)狀態 {empty_1, empty_2, full_1, full_2}(高位元補 0)ステータス {empty_1, empty_2, full_1, full_2}(上位をゼロ拡張) | 0 / 0 | 0 |
| 1 | 11 | high-Z高阻抗ハイインピーダンス | 0 / 0 | 0 |
| 0 | — | high-Z高阻抗ハイインピーダンス | 0 / 0 | 1 (both FIFOs write)1(兩個 FIFO 都寫入)1(両方の FIFO が書き込む) |
A combinational block: every output is assigned on every 0/1 path, so no latches. The final else only covers read = x in simulation. The 12'bz assignments become tri-state output buffers.這是一個組合邏輯區塊:每條 0/1 路徑都指定了所有輸出,所以不會推論出閂鎖(latch)。最後那個 else 只是為了模擬時的 read = x。12'bz 的指定會合成為三態輸出緩衝器。これは組合せ論理ブロックである。0/1 のどの経路でもすべての出力に代入しているため、ラッチは推論されない。最後の else はシミュレーション時の read = x に備えるだけである。12'bz の代入はトライステート出力バッファになる。
module temp_monitor_unit (channel_1, channel_2, select, clk, rst, read, data_out); output [11:0] data_out; input [11:0] channel_1, channel_2; input [1:0] select; input clk, rst, read; wire [11:0] temp1_out, temp2_out, temp_conv1, temp_conv2, av_temp1, av_temp2; wire [11:0] Data_out1, Data_out2; temp_register M1 (temp1_out, channel_1, clk, rst); // (1) temp_register M2 (temp2_out, channel_2, clk, rst); temp_converter M3 (temp_conv1, temp1_out); // (2) temp_converter M4 (temp_conv2, temp2_out); temp_filter M5 (av_temp1, clk, rst, temp_conv1); // (3) temp_filter M6 (av_temp2, clk, rst, temp_conv2); FIFO M7 (Data_out1, av_temp1, stack_empty1, stack_full1, clk, rst, write_to_stack, read_from_stack1); // (5) FIFO M8 (Data_out2, av_temp2, stack_empty2, stack_full2, clk, rst, write_to_stack, read_from_stack2); fifo_controller M9 (data_out, Data_out1, Data_out2, // (4) select, read, write_to_stack, stack_empty1, stack_full1, read_from_stack1, stack_empty2, stack_full2, read_from_stack2); endmodule
stack_empty1, write_to_stack etc. are never declared, so they become implicit 1-bit wires — fine here, but a typo would silently create a new net. Also check the positional order against each module header: M9's list is (..., write, empty_1, full_1, read_1, ...), which must match the controller's port order exactly. Named port binding (.write(write_to_stack)) avoids this class of bug.stack_empty1、write_to_stack 等從未宣告,所以會變成隱含的 1 位元 wire —— 在這裡沒問題,但打錯字就會悄悄生出一條新的 net。另外要把位置式連接的順序對照各模組的標頭:M9 的清單是 (..., write, empty_1, full_1, read_1, ...),必須與控制器的埠順序完全一致。改用具名連接(.write(write_to_stack))就能避開這一類錯誤。stack_empty1 や write_to_stack などは宣言されていないため、暗黙の 1 ビット wire になる。ここでは問題ないが、綴りを誤ると新しいネットが黙って作られてしまう。位置指定による接続順序も各モジュールのヘッダと照合すること。M9 のリストは (..., write, empty_1, full_1, read_1, ...) であり、コントローラのポート順序と完全に一致していなければならない。名前付きポート接続(.write(write_to_stack))を使えば、この種のバグは避けられる。
Problem: Compute the exact °F and the alternative-1 result for $T_C$ = 10, 37, 100 (integer arithmetic, shifts truncate). Suggest a closer shift-add approximation of 1.8.問題:對 $T_C$ = 10、37、100,分別算出精確的華氏值與寫法一的結果(整數運算,移位會無條件捨去)。並提出一個更接近 1.8 的移位相加近似。問題:$T_C$ = 10、37、100 について、正確な華氏の値と方式 1 の結果を求めよ(整数演算、シフトは切り捨て)。さらに 1.8 により近いシフト加算近似を提案せよ。
| TC | Exact精確值厳密値 | T + T>>1 + T>>2 + 32 | Error誤差誤差 |
|---|---|---|---|
| 10 | 50 | 10 + 5 + 2 + 32 = 49 | −1 |
| 37 | 98.6 | 37 + 18 + 9 + 32 = 96 | −2.6 |
| 100 | 212 | 100 + 50 + 25 + 32 = 207 | −5 |
The error grows as 0.05·T plus truncation. Closer: $1 + \tfrac12 + \tfrac14 + \tfrac1{32} + \tfrac1{64} = 1.796875$, i.e. add (temp_in >> 5) + (temp_in >> 6) — two more adders; T = 100 gives 100 + 50 + 25 + 3 + 1 + 32 = 211.誤差大約以 0.05·T 的速度增加,再加上截斷的部分。更接近的近似是 $1 + \tfrac12 + \tfrac14 + \tfrac1{32} + \tfrac1{64} = 1.796875$,也就是再加上 (temp_in >> 5) + (temp_in >> 6) —— 多兩個加法器;T = 100 時得到 100 + 50 + 25 + 3 + 1 + 32 = 211。誤差は 0.05·T に切り捨て分を加えた大きさで増えていく。より近い近似は $1 + \tfrac12 + \tfrac14 + \tfrac1{32} + \tfrac1{64} = 1.796875$、すなわち (temp_in >> 5) + (temp_in >> 6) を加える形で、加算器が 2 個増える。T = 100 では 100 + 50 + 25 + 3 + 1 + 32 = 211 となる。
Problem: All four filter registers hold 2000. What does the slide's av_temp evaluate to, and what does the 14-bit version give?問題:濾波器的四個暫存器都存著 2000。投影片版本的 av_temp 會算出多少?14 位元的版本又會得到多少?問題:フィルタの 4 つのレジスタがすべて 2000 を保持している。スライド版の av_temp はいくつになるか。14 ビット版ではどうか。
Sum = 8000, which needs 13 bits. At 12 bits: 8000 − 4096 = 3904; 3904 >> 2 = 976. With a 14-bit sum: 8000 >> 2 = 2000, the correct average.總和 = 8000,需要 13 位元。在 12 位元下:8000 − 4096 = 3904;3904 >> 2 = 976。改用 14 位元的總和:8000 >> 2 = 2000,才是正確的平均值。総和は 8000 で 13 ビットを要する。12 ビットでは 8000 − 4096 = 3904、3904 >> 2 = 976 となる。14 ビットの総和なら 8000 >> 2 = 2000 で、これが正しい平均である。
Problem: FIFO 1 is empty, FIFO 2 is full. Give data_out (hex) for read = 1, select = 2'b00, and state what happens to new samples while read = 1.問題:FIFO 1 為空、FIFO 2 已滿。請寫出 read = 1, select = 2'b00 時的 data_out(十六進位),並說明 read = 1 期間新進來的樣本會怎麼樣。問題:FIFO 1 は空、FIFO 2 は満杯である。read = 1, select = 2'b00 のときの data_out を 16 進で示し、read = 1 の間に新しく到着するサンプルがどうなるかを述べよ。
{empty_1, empty_2, full_1, full_2} = 4'b1001 → 12'h009.{empty_1, empty_2, full_1, full_2} = 4'b1001 → 12'h009。{empty_1, empty_2, full_1, full_2} = 4'b1001 → 12'h009。
While read = 1, write = 0, so the filtered samples are not stored — data arriving during reads is dropped. (And when read = 0, both FIFOs write every clock until full.)在 read = 1 期間 write = 0,所以濾波後的樣本不會被存進去 —— 讀取期間送到的資料會被丟掉。(而 read = 0 時,兩個 FIFO 會每個時脈都寫入,直到滿為止。)read = 1 の間は write = 0 なので、フィルタ後のサンプルは格納されない。すなわち読み出し中に到着したデータは捨てられる。(逆に read = 0 のときは、両方の FIFO が満杯になるまで毎クロック書き込む。)
ad and bd hold the words addressed by a and b.暫存器檔:雙位址、16 個字;輸出 ad 與 bd 分別是位址 a、b 指到的資料字。レジスタファイル:デュアルアドレス、16 ワード。出力 ad と bd は、それぞれアドレス a、b のワードを保持する。r, s.運算元選擇器:挑出 ALU 的兩個運算元 r 與 s。オペランドセレクタ:ALU の 2 つのオペランド r、s を選ぶ。c_out, ovr, carry look-ahead g_bar, p_bar; plus f_0 (zero) and f_3 (sign).ALU 旗標:c_out、ovr,以及進位預看用的 g_bar、p_bar;另有 f_0(零)與 f_3(正負號)。ALU のフラグ:c_out、ovr、桁上げ先見用の g_bar、p_bar。さらに f_0(ゼロ)と f_3(符号)がある。| Mnemonic助記符ニーモニック | Octal八進位8 進 | r | s |
|---|---|---|---|
| aq | 0 | a | q |
| ab | 1 | a | b |
| zq | 2 | 0 | q |
| zb | 3 | 0 | b |
| za | 4 | 0 | a |
| da | 5 | d | a |
| dq | 6 | d | q |
| dz | 7 | d | 0 |
| Mnemonic助記符ニーモニック | Octal八進位8 進 | Operation運算演算 | Verilog |
|---|---|---|---|
| add | 0 | r + s | r+s |
| subr | 1 | s − r | s-r |
| subs | 2 | r − s | r-s |
| or | 3 | r or s | r|s |
| and | 4 | r and s | r&s |
| notrs | 5 | (not r) and s | ~r&s |
| exor | 6 | r xor s | r^s |
| exnor | 7 | r xnor s | r~^s |
In the table a, b, q, d mean the register-file word at address a (ad), at address b (bd), the Q register and the direct data input; o/z means zero. (The slide's ALU column writes ||, &&; the bitwise operators are meant.)表中的 a、b、q、d 分別指位址 a 的暫存器檔資料字(ad)、位址 b 的資料字(bd)、Q 暫存器,以及直接資料輸入;o/z 代表零。(投影片的 ALU 欄寫成 ||、&&,其實要的是逐位元運算子。)表中の a、b、q、d は、それぞれアドレス a のレジスタファイルのワード(ad)、アドレス b のワード(bd)、Q レジスタ、直接データ入力を表し、o/z はゼロを意味する。(スライドの ALU 欄は ||、&& と書かれているが、意図はビット単位の演算子である。)
| Mnemonic助記符ニーモニック | Octal八進位8 進 | Output y輸出 y出力 y | RAM shiftRAM 移位RAM シフト | RAM loads (addr b)RAM 載入(位址 b)RAM ロード(アドレス b) | Q shiftQ 移位Q シフト | Q loadsQ 載入Q ロード | RAM bit 3 / bit 0RAM 位元 3/位元 0RAM ビット 3/ビット 0 | Q bit 3 / bit 0Q 位元 3/位元 0Q ビット 3/ビット 0 |
|---|---|---|---|---|---|---|---|---|
| qreg | 0 | alu_out | — | none無なし | none無なし | alu_out | — / — | — / — |
| nop | 1 | alu_out | — | none無なし | — | none無なし | — / — | — / — |
| rama | 2 | reg_f[a] | none無なし | alu_out | — | none無なし | — / — | — / — |
| ramf | 3 | alu_out | none無なし | alu_out | — | none無なし | — / — | — / — |
| ramqd | 4 | alu_out | down下移下へ | alu_out >> 1 | down下移下へ | Q >> 1 | ram3 / d[1] | q3 / d[1] |
| ramd | 5 | alu_out | down下移下へ | alu_out >> 1 | — | none無なし | ram3 / d[1] | n/a不適用該当なし |
| ramqu | 6 | alu_out | up上移上へ | alu_out << 1 | up上移上へ | Q << 1 | d[2] / ram0 | d[2] / q0 |
| ramu | 7 | alu_out | up上移上へ | alu_out << 1 | — | none無なし | d[2] / ram0 | n/a不適用該当なし |
The shift occurs before the load. "—" is don't care; reg_f[a] is the register-file word at address a. In the bit columns, d[i] is bit i of the data being shifted; ram3/ram0 and q3/q0 are the serial in/out pins that chain neighbouring slices.移位發生在載入之前。「—」表示不在意(don't care);reg_f[a] 是位址 a 的暫存器檔資料字。位元欄中的 d[i] 是被移位資料的第 i 位元;ram3/ram0 與 q3/q0 是把相鄰 bit slice 串接起來的序列輸入/輸出腳位。シフトはロードより先に行われる。「—」はドントケアを表し、reg_f[a] はアドレス a のレジスタファイルのワードである。ビット欄の d[i] はシフトされるデータの第 i ビットであり、ram3/ram0 と q3/q0 は隣接するスライスを連結するシリアル入出力ピンである。
// amd2901 bit slice microprocessor // operands: instruction[2:0] `define aq 3'b000 `define ab 3'b001 `define zq 3'b010 `define zb 3'b011 `define za 3'b100 `define da 3'b101 `define dq 3'b110 `define dz 3'b111 // opcodes: instruction[5:3] `define add 3'b000 `define subr 3'b001 `define subs 3'b010 `define orrs 3'b011 `define andrs 3'b100 `define notrs 3'b101 `define exor 3'b110 `define exnor 3'b111 // destination mnemonics: instruction[8:6] `define qreg 3'b000 `define nop 3'b001 `define rama 3'b010 `define ramf 3'b011 `define ramqd 3'b100 `define ramd 3'b101 `define ramqu 3'b110 `define ramu 3'b111
Compiler directives are honored by synthesis (Lecture 7), so the code reads `ramqd instead of a magic number. Note or / and are Verilog keywords, hence `orrs / `andrs.合成工具會處理編譯器指令(第 7 講),所以程式碼裡寫的是 `ramqd 而不是一個沒頭沒腦的數字。注意 or 與 and 是 Verilog 的保留字,因此才改用 `orrs 與 `andrs。コンパイラ指令は合成でも解釈される(第 7 講)ため、コードではマジックナンバーではなく `ramqd と書ける。or と and は Verilog の予約語なので、`orrs / `andrs という名前になっている点に注意する。
module Register_file (clk, rst, a, b, f, dest_ctl, ram0, ram3, ad, bd); input clk, rst; input [3:0] a, b, f; input [2:0] dest_ctl; inout ram0, ram3; output [3:0] ad, bd; reg [3:0] ab_data [15:0]; // memory: 16 elements wire [3:0] data; wire ram_en; reg [4:0] row_index; // note: size of row_index is critical assign ram_en = ((dest_ctl == `qreg) || (dest_ctl == `nop)) ? 0 : 1; always @ (posedge clk or posedge rst) if (rst == 1) for (row_index = 0; row_index < 16; row_index = row_index + 1) ab_data[row_index] = 0; else if (ram_en) ab_data[b] = data; assign data = (dest_ctl == `ramqu || dest_ctl == `ramu) ? {f[2], f[1], f[0], ram0} : (dest_ctl == `ramqd || dest_ctl == `ramd) ? {ram3, f[3], f[2], f[1]} : (dest_ctl == `rama || dest_ctl == `ramf) ? f : 4'bxxxx; assign ad = ab_data[a]; assign bd = ab_data[b]; assign ram3 = (dest_ctl == `ramu || dest_ctl == `ramqu) ? f[3] : 1'bz; assign ram0 = (dest_ctl == `ramd || dest_ctl == `ramqd) ? f[0] : 1'bz; endmodule
row_index must count to 16 to exit the reset loop, so it needs 5 bits — a 4-bit index wraps 15 → 0 and the loop never ends (Lecture 7, Type I loop).row_index 必須數到 16 才能離開重置迴圈,所以要 5 個位元 —— 4 位元的索引會從 15 繞回 0,迴圈永遠不會結束(第 7 講的 Type I 迴圈)。row_index はリセットループを抜けるために 16 まで数える必要があるので 5 ビット必要である。4 ビットでは 15 → 0 と折り返し、ループが終わらない(第 7 講の Type I ループ)。ram0 is an input (bit from the lower slice) and f[3] leaves on ram3. Shifting down: the reverse. Otherwise both pins float (z), so the bidirectional inout pins chain slices together.上移時:ram0 是輸入(來自較低位 slice 的位元),而 f[3] 由 ram3 送出;下移時剛好相反。其他情況兩支腳都浮接(z),所以這組雙向 inout 腳位可以把多片 slice 串接起來。上方向シフトでは ram0 が入力(下位スライスからのビット)となり、f[3] が ram3 から出ていく。下方向シフトではその逆である。それ以外では両ピンとも浮いた状態(z)になるため、双方向の inout ピンでスライス同士を連結できる。4'bxxxx default only occurs when ram_en = 0, so it is a true don't care.4'bxxxx 這個預設值只在 ram_en = 0 時出現,所以它確實是不在意值。4'bxxxx というデフォルト値は ram_en = 0 のときしか現れないので、本当のドントケアである。module operand_selector (d, ad, bd, q, op_sel, r, s);
input [3:0] d, ad, bd, q;
input [2:0] op_sel;
output [3:0] r, s;
reg [3:0] r, s;
// operand codes
always @ (op_sel) begin
case (op_sel)
`aq, `ab: assign r = ad;
`zq, `zb, `za: assign r = 4'b0;
default: assign r = d;
endcase
case (op_sel)
`aq, `zq, `dq: assign s = q;
`ab, `zb: assign s = bd;
`za, `da: assign s = ad;
default: assign s = 4'b0;
endcase
end
endmodule
Uses the "simulation efficient" procedural continuous assignment style from Lecture 5 §5.2: the block wakes only on op_sel, yet r/s keep following d, ad, bd, q. It synthesizes to two 4-bit muxes, but PCA support is tool dependent; always @* with plain = is the portable form.這裡用的是第 5 講 §5.2 那種「模擬效率高」的程序式連續指定(procedural continuous assignment, PCA)寫法:區塊只在 op_sel 變動時被喚醒,但 r/s 仍會持續跟著 d、ad、bd、q 改變。它會合成成兩個 4 位元多工器,不過各家工具對 PCA 的支援程度不一;用 always @* 搭配單純的 = 才是可攜的寫法。第 5 講 §5.2 の「シミュレーション効率のよい」手続き的連続代入(PCA)のスタイルを使っている。ブロックは op_sel の変化でしか起動しないが、r/s は d、ad、bd、q を追従し続ける。合成すると 4 ビットのマルチプレクサ 2 個になるが、PCA の対応はツール依存である。always @* と通常の = を使う書き方のほうが移植性が高い。
module ALU_amd2901 (r, s, c_in, ctl, alu_out, g_bar, p_bar, ovr, c_out);
input [3:0] r, s;
input c_in;
input [2:0] ctl;
output [3:0] alu_out;
output g_bar, p_bar, ovr, c_out;
reg [3:0] alu_out;
reg c_out;
always @ (r or s or c_in or ctl)
case (ctl)
`add: {c_out, alu_out} = r + s + c_in;
`subr: {c_out, alu_out} = r + ~s + c_in;
`subs: {c_out, alu_out} = s + ~r + ~c_in;
`orrs: {c_out, alu_out} = {1'b0, r | s};
`andrs: {c_out, alu_out} = {1'b0, r & s};
`notrs: {c_out, alu_out} = {1'b0, (~r) & s};
`exor: {c_out, alu_out} = {1'b0, r ^ s};
`exnor: {c_out, alu_out} = {1'b0, r ~^ s};
default: {c_out, alu_out} = 5'bx;
endcase
assign ovr = c_out ^ alu_out[3];
assign g_bar = ~( (r[3] & s[3]) |
((r[3] | s[3]) & (r[2] & s[2])) |
((r[3] | s[3]) & (r[2] | s[2]) & (r[1] & s[1])) |
((r[3] | s[3]) & (r[2] | s[2]) & (r[1] | s[1]) & (r[0] & s[0])) );
assign p_bar = ~((r[3] ^ s[3]) & (r[2] ^ s[2]) & (r[1] ^ s[1]) & (r[0] ^ s[0]));
endmodule
`subr computes $r + \bar s + c_{in} = r - s$ (when $c_{in}=1$) and `subs computes $s + \bar r + \bar c_{in}$ — the operand order is swapped relative to the table (subr = s − r), and `subs inverts the carry. Check whichever convention your exam uses.照程式碼寫的,`subr 算的是 $r + \bar s + c_{in} = r - s$(當 $c_{in}=1$),而 `subs 算的是 $s + \bar r + \bar c_{in}$ —— 運算元順序和功能表(subr = s − r)相反,而且 `subs 把進位取反。考試時要先確認採用哪一套慣例。コードのとおりなら `subr は $r + \bar s + c_{in} = r - s$($c_{in}=1$ のとき)を、`subs は $s + \bar r + \bar c_{in}$ を計算する。表(subr = s − r)に対してオペランドの順序が入れ替わっており、`subs では桁上げも反転している。試験ではどちらの規約を使うか確認すること。ovr = c_out ^ alu_out[3] is not the usual two's-complement overflow (carry into the MSB XOR carry out of it). See Practice 6.ovr = c_out ^ alu_out[3] 並不是一般的二補數溢位判斷(應該是進入 MSB 的進位 XOR 由 MSB 送出的進位)。見練習 6。ovr = c_out ^ alu_out[3] は通常の 2 の補数オーバフロー(MSB への桁上げ XOR MSB からの桁上げ)ではない。練習 6 を参照のこと。g_bar / p_bar are the inverted group generate / propagate for a 74182-style look-ahead unit across slices. p_bar here uses XOR (half-sum) propagate; the OR form r | s is also common.g_bar/p_bar 是跨 slice 的 74182 式進位預看單元所需的群組 generate/propagate 的反相訊號。這裡的 p_bar 用 XOR(半加和)形式的 propagate;用 OR 的 r | s 形式也很常見。g_bar/p_bar は、スライス間にまたがる 74182 形式の桁上げ先見ユニット向けのグループ generate/propagate を反転した信号である。ここでの p_bar は XOR(半加算)形の propagate を使っているが、OR 形の r | s もよく用いられる。module Q_register (clk, rst, data_in, dest_ctl, qs0, qs3, q); // parameterized register with shift left (up), shift right (down) // and transfer of interface bits to/from companion slices input clk, rst; input [3:0] data_in; input [2:0] dest_ctl; inout qs0, qs3; output [3:0] q; reg [3:0] data; reg q_en; reg_load_R #(4) M1 (data, clk, rst, q_en, q); bufif1 (qs3, data_in[3], (dest_ctl == `ramu || dest_ctl == `ramqu)); bufif1 (qs0, data_in[0], (dest_ctl == `ramd || dest_ctl == `ramqd)); always @ (dest_ctl) case (dest_ctl) `qreg, `ramqd, `ramqu: assign q_en = 1; default: assign q_en = 0; endcase always @ (dest_ctl or qs0 or qs3) case (dest_ctl) // shift left (up) `ramqu: assign data = {data_in[2], data_in[1], data_in[0], qs0}; // shift right (dn) `ramqd: assign data = {qs3, data_in[3], data_in[2], data_in[1]}; // pass through `qreg: assign data = data_in; default: assign data = 4'bx; endcase endmodule module reg_load_R (data, clk, reset, load, q); parameter size = 8; input [size-1:0] data; input clk, reset, load; output [size-1:0] q; reg [size-1:0] q; always @ (posedge clk or posedge reset) if (reset == 1) q = 0; else if (load == 1) q = data; endmodule
reg_load_R loads the target data when load is high and otherwise holds (an enable register, not a latch — Lecture 6 §4.4). The Q register instantiates it with #(4) to override size. bufif1 primitives drive the serial pins only while shifting in the matching direction.reg_load_R 在 load 為高時載入資料,否則保持原值(這是帶致能的暫存器,不是閂鎖 —— 第 6 講 §4.4)。Q 暫存器用 #(4) 實體化它以覆寫 size。bufif1 原語只有在對應方向的移位期間才會驅動序列腳位。reg_load_R は load が High のときデータを取り込み、それ以外では値を保持する(ラッチではなくイネーブル付きレジスタである。第 6 講 §4.4)。Q レジスタは #(4) を付けてインスタンス化し、size を上書きしている。bufif1 プリミティブは、対応する方向へシフトしている間だけシリアルピンを駆動する。
The top level connects data_in to alu_out, so on `ramqd/`ramqu this model loads alu_out shifted, whereas the table says the Q register loads Q shifted (the original AMD2901 behavior). Also the bufif1 enables include `ramu/`ramd, which the table marks "not applicable" for Q. Feeding q instead of data_in into the shift paths would match the datasheet.頂層把 data_in 接到 alu_out,所以在 `ramqd/`ramqu 時,這個模型載入的是移位後的 alu_out;但功能表說 Q 暫存器載入的應該是移位後的 Q(這才是 AMD2901 原本的行為)。另外 bufif1 的致能條件還包含 `ramu/`ramd,而功能表對 Q 欄把這兩項標為「不適用」。把 q 而不是 data_in 送進移位路徑,才會符合資料手冊。トップレベルでは data_in が alu_out に接続されているため、`ramqd/`ramqu のときこのモデルはシフトした alu_out をロードする。しかし表では、Q レジスタがロードするのはシフトした Q(AMD2901 本来の動作)である。さらに bufif1 のイネーブル条件には `ramu/`ramd も含まれるが、表では Q について「該当なし」とされている。シフト経路に data_in ではなく q を与えれば、データシートどおりになる。
module output_selector (mux_out, channel_1, channel_2, sel, enable);
input [3:0] channel_1, channel_2;
input [2:0] sel;
input enable; // active low
output [3:0] mux_out;
assign mux_out = (enable == 0 && sel == `rama) ? channel_1 :
(enable == 0) ? channel_2 : 4'bz;
endmodule
Connected as (y, ad, alu_out, instruction[8:6], enable): y = ad for destination rama, otherwise alu_out, and high-Z when enable = 1.接線為 (y, ad, alu_out, instruction[8:6], enable):目的地是 rama 時 y = ad,否則為 alu_out;而 enable = 1 時輸出高阻抗。接続は (y, ad, alu_out, instruction[8:6], enable) である。デスティネーションが rama なら y = ad、それ以外は alu_out となり、enable = 1 のときはハイインピーダンスになる。
module amd2901 (clk, rst, enable, c_in, a, b, d, instruction, ram0, ram3,
qs0, qs3, y, g_bar, p_bar, ovr, c_out, f_0, f_3);
input clk, rst, c_in, enable;
input [3:0] a, b, d;
input [8:0] instruction;
inout ram0, ram3;
inout qs0, qs3;
output [3:0] y;
output g_bar, p_bar, ovr, c_out, f_0, f_3;
wire [3:0] ad, bd, q, r, s, alu_out;
Register_file M1 (
clk, // system clock
rst, // global reset
a, b, // addresses to memory
alu_out, // data path from ALU
instruction[8:6], // micro instruction bits for destination
ram0, ram3, // LSB and MSB from memory word
ad, bd // memory output words (4 bits each)
);
ALU_amd2901 M2 (r, s, c_in, instruction[5:3], alu_out, g_bar, p_bar, ovr, c_out);
output_selector M3 (y, ad, alu_out, instruction[8:6], enable);
operand_selector M4 (d, ad, bd, q, instruction[2:0], r, s);
Q_register M5 (clk, rst, alu_out, instruction[8:6], qs0, qs3, q);
assign f_0 = (alu_out == 4'b0) ? 0 : 1'bz;
assign f_3 = alu_out[3];
endmodule
f_0 is an open-drain-style zero flag (drives 0 or floats, so several slices can share one wired line with a pull-up); f_3 is the sign bit. The overall netlist is five sub-blocks plus a 4-input OR and an inverting tri-state (invtn_c) for f_0.f_0 是開汲極式的零旗標(只會拉低或浮接,所以多片 slice 可以共用一條接上提升電阻的線);f_3 則是正負號位元。整體 netlist 就是五個子區塊,再加上一個 4 輸入 OR 與一個反相三態閘(invtn_c)來產生 f_0。f_0 はオープンドレイン形式のゼロフラグである(0 を駆動するか浮かせるかなので、複数のスライスがプルアップ付きの 1 本の線を共有できる)。f_3 は符号ビットである。全体の netlist は 5 つのサブブロックに、f_0 用の 4 入力 OR と反転トライステート(invtn_c)を加えたものになる。
`define.AMD2901:9 位元指令分成 目的地 i[8:6]、ALU 功能 i[5:3]、運算元來源 i[2:0];用 `define 定義助記符。AMD2901:9 ビット命令はデスティネーション i[8:6]、ALU 機能 i[5:3]、オペランド源 i[2:0] に分かれる。ニーモニックは `define で定義する。Problem: Describe what each instruction does (octal digits are i[8:6], i[5:3], i[2:0]): (a) 1 0 1 (b) 3 4 5 (c) 0 6 7 (d) 6 0 3.問題:說明下列各指令的作用(三個八進位數字依序是 i[8:6]、i[5:3]、i[2:0]):(a) 1 0 1 (b) 3 4 5 (c) 0 6 7 (d) 6 0 3。問題:次の各命令が何を行うかを述べよ(3 つの 8 進数字は順に i[8:6]、i[5:3]、i[2:0]):(a) 1 0 1 (b) 3 4 5 (c) 0 6 7 (d) 6 0 3。
(a) nop, add, ab: y = RAM[a] + RAM[b] + c_in; nothing stored.(a) nop、add、ab:y = RAM[a] + RAM[b] + c_in;不寫回任何地方。(a) nop、add、ab:y = RAM[a] + RAM[b] + c_in。どこにも格納しない。
(b) ramf, and, da: RAM[b] ← d & RAM[a]; y = the same value.(b) ramf、and、da:RAM[b] ← d & RAM[a];y 也是同一個值。(b) ramf、and、da:RAM[b] ← d & RAM[a]。y も同じ値になる。
(c) qreg, exor, dz: Q ← d ^ 0 = d; y = d. A way to load Q from the data bus.(c) qreg、exor、dz:Q ← d ^ 0 = d;y = d。這是從資料匯流排載入 Q 的一種做法。(c) qreg、exor、dz:Q ← d ^ 0 = d、y = d。データバスから Q をロードする手段である。
(d) ramqu, add, zb: ALU = 0 + RAM[b] + c_in; RAM[b] ← that value shifted up (ram0 enters bit 0), Q shifted up as well; y = alu_out. With c_in = 0 this doubles RAM[b] — the core step of shift-and-add multiplication.(d) ramqu、add、zb:ALU = 0 + RAM[b] + c_in;RAM[b] ← 該值上移一位(ram0 進入位元 0),Q 也同樣上移;y = alu_out。當 c_in = 0 時,這等於把 RAM[b] 乘以 2 —— 正是移位相加乘法的核心步驟。(d) ramqu、add、zb:ALU = 0 + RAM[b] + c_in。RAM[b] ← その値を 1 ビット左シフトしたもの(ram0 がビット 0 に入る)。Q も同様に左シフトされ、y = alu_out となる。c_in = 0 なら RAM[b] が 2 倍になり、これはシフト加算乗算の中心的な手順である。
Problem: With r = 4'b0011, s = 4'b0101, c_in = 1, evaluate the slide's `subr line: alu_out, c_out, ovr. Is there a real signed overflow?問題:取 r = 4'b0011、s = 4'b0101、c_in = 1,計算投影片中 `subr 那一行的 alu_out、c_out、ovr。真的發生有號數溢位了嗎?問題:r = 4'b0011、s = 4'b0101、c_in = 1 のとき、スライドの `subr の行について alu_out、c_out、ovr を求めよ。本当に符号付きオーバフローが起きているか。
$r + \bar s + 1 = 3 + 10 + 1 = 14$ → alu_out = 4'b1110 (−2 signed), c_out = 0 (borrow: r < s). Indeed 3 − 5 = −2.$r + \bar s + 1 = 3 + 10 + 1 = 14$ → alu_out = 4'b1110(有號數為 −2),c_out = 0(有借位:r < s)。確實 3 − 5 = −2。$r + \bar s + 1 = 3 + 10 + 1 = 14$ → alu_out = 4'b1110(符号付きで −2)、c_out = 0(借りが発生:r < s)。たしかに 3 − 5 = −2 である。
Slide formula: ovr = c_out ^ alu_out[3] = 0 ^ 1 = 1. But −2 fits in 4-bit two's complement, so there is no overflow. Quick check: overflow is only possible when the two adder inputs have the same sign; here $r = 0011$ is positive and $\bar s = 1010$ is negative, so overflow cannot occur. The correct flag is carry-into-MSB XOR carry-out. Bit by bit: $0011 + 1010 + 1$: bit 0: 1+0+1 = 0 c1 = 1; bit 1: 1+1+1 = 1 c2 = 1; bit 2: 0+0+1 = 1 c3 = 0; bit 3: 0+1+0 = 1 c4 = 0. $c_3 \oplus c_4 = 0$ — no overflow. The slide's ovr is really "c_out XOR sign".投影片的公式:ovr = c_out ^ alu_out[3] = 0 ^ 1 = 1。但 −2 在 4 位元二補數裡放得下,所以其實沒有溢位。快速判斷:只有當加法器的兩個輸入同號時才可能溢位;這裡 $r = 0011$ 是正數、$\bar s = 1010$ 是負數,所以不可能溢位。正確的旗標是「進入 MSB 的進位 XOR 由 MSB 送出的進位」。逐位元計算 $0011 + 1010 + 1$:位元 0:1+0+1 = 0,c1 = 1;位元 1:1+1+1 = 1,c2 = 1;位元 2:0+0+1 = 1,c3 = 0;位元 3:0+1+0 = 1,c4 = 0。$c_3 \oplus c_4 = 0$ —— 沒有溢位。投影片的 ovr 其實是「c_out XOR 正負號位元」。スライドの式では ovr = c_out ^ alu_out[3] = 0 ^ 1 = 1 となる。しかし −2 は 4 ビットの 2 の補数で表せるので、実際にはオーバフローは起きていない。簡便な判定:加算器の 2 入力が同符号のときにしかオーバフローは起こり得ない。ここでは $r = 0011$ が正、$\bar s = 1010$ が負なので起こり得ない。正しいフラグは「MSB への桁上げ XOR MSB からの桁上げ」である。$0011 + 1010 + 1$ をビットごとに見ると、ビット 0:1+0+1 = 0、c1 = 1。ビット 1:1+1+1 = 1、c2 = 1。ビット 2:0+0+1 = 1、c3 = 0。ビット 3:0+1+0 = 1、c4 = 0。$c_3 \oplus c_4 = 0$ でオーバフローはない。スライドの ovr は実質「c_out XOR 符号ビット」である。
Problem: alu_out = 4'b1011. Give the word written to RAM[b] and the values on ram3/ram0 for destinations (a) ramu with ram0 = 1, (b) ramd with ram3 = 0, (c) ramf.問題:alu_out = 4'b1011。請寫出下列各目的地寫進 RAM[b] 的資料字,以及 ram3/ram0 上的值:(a) ramu 且 ram0 = 1,(b) ramd 且 ram3 = 0,(c) ramf。問題:alu_out = 4'b1011 とする。次の各デスティネーションについて、RAM[b] に書き込まれるワードと ram3/ram0 の値を示せ。(a) ram0 = 1 の ramu、(b) ram3 = 0 の ramd、(c) ramf。
(a) {f[2], f[1], f[0], ram0} = 4'b0111; ram3 drives f[3] = 1 out to the next slice; ram0 is an input.(a) {f[2], f[1], f[0], ram0} = 4'b0111;ram3 把 f[3] = 1 送往下一片 slice;ram0 此時是輸入。(a) {f[2], f[1], f[0], ram0} = 4'b0111。ram3 は f[3] = 1 を次のスライスへ出力し、ram0 は入力となる。
(b) {ram3, f[3], f[2], f[1]} = 4'b0101; ram0 drives f[0] = 1; ram3 is an input.(b) {ram3, f[3], f[2], f[1]} = 4'b0101;ram0 送出 f[0] = 1;ram3 此時是輸入。(b) {ram3, f[3], f[2], f[1]} = 4'b0101。ram0 が f[0] = 1 を出力し、ram3 は入力となる。
(c) 4'b1011 unshifted; both pins high-Z.(c) 4'b1011 原封不動;兩支腳都是高阻抗。(c) 4'b1011 がそのまま書き込まれ、両方のピンはハイインピーダンスになる。
`define mnemonics.用 `define 定義助記符。`define でニーモニックを定義する。#(4) parameter override.用 #(4) 覆寫參數。#(4) によるパラメータの上書き。