- alwaysblock 1:
這里需要了解一下always過程塊中的語法點:
其賦值情況與assign語句的對比情況
assign out1 = a & b | c ^ d;
always @(*) out2 = a & b | c ^ d;
并且對于賦值情況而言,assign連續(xù)賦值語句中左側(cè)的類型必須是線網(wǎng)類型(wire),而always語句塊中的左側(cè)類型必須是變量類型(reg)
- Build an AND gate using both an assign statement and a combinational always block.題目一要求使用assign語句與綜合always塊描述一個與門:
// synthesis verilog_input_version verilog_2001
module top_module(
input a,
input b,
output wire out_assign,
output reg out_alwaysblock
);
assign out_assign = a & b;
always @(*) out_alwaysblock = a & b;
endmodule
- always 2
對于硬件綜合來說,有兩種相關(guān)的always塊: - 組合 : always@(*);
- 計時: always@(posedge clk);
時鐘always塊創(chuàng)建一個組合邏輯塊,就像組合always塊一樣,但也在組合邏輯塊的輸出處創(chuàng)建一組觸發(fā)器(或“寄存器”)。不是立即可見邏輯塊的輸出,而是僅僅在下一個(posedge clk)之后立即可見輸出。 - Blocking vs. Non-Blocking Assignment (阻塞與非阻塞賦值)
- 連續(xù)賦值(assign x = y;)。只能在不在過程中使用(“always block”總是阻塞)
- 程序阻塞賦值:(x=y)。只能在程序內(nèi)部使用。
- 程序非阻塞賦值:(x<=y)。只能在程序內(nèi)部使用
在組合的always塊中,使用阻塞賦值;而在時鐘控制的always塊中,使用非阻塞賦值。這個點還是很重要的,尤其是涉及時序電路分析時。 -
練習(xí):使用三種賦值方式構(gòu)建一個異或門:
練習(xí)圖.png
// synthesis verilog_input_version verilog_2001
module top_module(
input clk,
input a,
input b,
output wire out_assign,
output reg out_always_comb,
output reg out_always_ff );
assign out_assign = a ^ b; // Continuous assignments
always@(*) out_always_comb = a ^ b; // Procedural blocking assignment
always@(posedge clk) // Procedural non-blocking assignment
out_always_ff <= a ^ b;
endmodule
-
Always if
if語句經(jīng)常創(chuàng)建一個2 對 1 多路復(fù)用器,如果條件為真則選擇一個輸入,如果條件為假則選擇另一個輸入。
image.png
always @(*) begin
if (condition) begin
out = x;
end
else begin
out = y;
end
end
上述代碼等價于使用帶有條件運算符的連續(xù)賦值語句:
assign out = (condition) ? x : y ;
但是,程序if語句提供了一種新的出錯方式,僅當(dāng)總是為out分配一個值,該電路才是組合電路。
- 構(gòu)建一個在a和b之間進(jìn)行選擇的 2 對 1 多路復(fù)用器。如果sel_b1和sel_b2都為真,則選擇b 。否則,選擇. 做同樣的事情兩次,一次使用分配語句,一次使用過程 if 語句。
// synthesis verilog_input_version verilog_2001
module top_module(
input a,
input b,
input sel_b1,
input sel_b2,
output wire out_assign,
output reg out_always );
assign out_assign = (sel_b1 & sel_b2) ? b : a;
always@(*) begin
if(sel_b1 & sel_b2) begin
out_always = b ;
end
else begin
out_always = a ;
end
end
endmodule
- always if 2
A common source of errors: How to avoid making latches常見的錯誤來源:如何避免鎖存的產(chǎn)生
設(shè)計電路時,首先要從電路方面考慮:
我想要這個邏輯門
我想要一個具有這些輸入并產(chǎn)生這些輸出的組合邏輯塊
我想要一個組合的邏輯塊,然后是一組觸發(fā)器
你不能做的是先寫代碼,然后希望它生成一個合適的電路。
- 如果 (cpu_overheated) 然后shut_off_computer = 1;
- 如果 (~arrived) 那么 keep_driving = ~gas_tank_empty;
語法正確的代碼不一定會產(chǎn)生合理的電路(組合邏輯 + 觸發(fā)器)。通常的原因是:“在您指定的情況以外的情況下會發(fā)生什么?”。Verilog 的回答是:保持輸出不變。
這種“保持輸出不變”的行為意味著需要記住當(dāng)前狀態(tài),從而產(chǎn)生一個鎖存器。組合邏輯(例如邏輯門)不能記住任何狀態(tài)。注意警告(10240):...推斷鎖存器“消息。除非鎖存器是故意的,否則它幾乎總是表示錯誤。組合電路必須在所有條件下為所有輸出分配一個值。這通常意味著您總是需要else子句或分配給輸出的默認(rèn)值。
-
示例:
The following code contains incorrect behaviour that creates a latch. Fix the bugs so that you will shut off the computer only if it's really overheated, and stop driving if you've arrived at your destination or you need to refuel.
image.png
題目描述翻譯過來就是這么個意思:
以下代碼包含創(chuàng)建閂鎖的不正確行為。修復(fù)錯誤,以便您僅在計算機(jī)確實過熱時關(guān)閉計算機(jī),并在您到達(dá)目的地或需要加油時停止駕駛。所以當(dāng)你arrived的時候,那就需要停止駕駛了.本題的初衷是為了避免鎖存的產(chǎn)生,也就是將兩個if語句的其他情況補(bǔ)齊。
// synthesis verilog_input_version verilog_2001
module top_module (
input cpu_overheated,
output reg shut_off_computer,
input arrived,
input gas_tank_empty,
output reg keep_driving ); //
always @(*) begin
if (cpu_overheated)
shut_off_computer = 1;
else
shut_off_computer = 0;
end
always @(*) begin
if (~arrived)
keep_driving = ~gas_tank_empty;
else
keep_driving = 0;
end
endmodule
- Always case
Verilog 中的 case 語句幾乎等同于將一個表達(dá)式與其他表達(dá)式進(jìn)行比較的 if-elseif-else 序列。它的語法和功能不同于 C 中的switch語句。
always @(*) begin // This is a combinational circuit
case (in)
1'b1: begin
out = 1'b1; // begin-end if >1 statement
end
1'b0: out = 1'b0;
default: out = 1'bx;
endcase
end
- case語句以case開頭,每個“case項”以冒號結(jié)尾,沒有“switch”
- 每個case項只能執(zhí)行一個語句,這使得C中使用的“中斷”變得不必要。但這意味著如果需要多個語句,則必須使用begin...end。
- 允許重復(fù)(和部分重疊)case項,使用第一個匹配的。C不允許重復(fù)的case案例。
練習(xí):使用case語句創(chuàng)建一個6選1的多路復(fù)用器,當(dāng)sel是0到5,選擇相應(yīng)的輸出,其余情況下,輸出為0.
// synthesis verilog_input_version verilog_2001
module top_module (
input [2:0] sel,
input [3:0] data0,
input [3:0] data1,
input [3:0] data2,
input [3:0] data3,
input [3:0] data4,
input [3:0] data5,
output reg [3:0] out );//
always@(*) begin // This is a combinational circuit
case(sel)
3'd0 : out = data0;
3'd1 : out = data1;
3'd2 : out = data2;
3'd3 : out = data3;
3'd4 : out = data4;
3'd5 : out = data5;
default: out = 4'd0;
endcase
end
endmodule
------------------
/*這里需要注意一下:就是表示case項的時候,`'`要在位寬和數(shù)制之間*/
Always_case2:
A priority encoder is a combinational circuit that, when given an input bit vector, outputs the position of the first 1 bit in the vector. For example, a 8-bit priority encoder given the input 8'b10010000 would output 3'd4, because bit[4] is first bit that is high.優(yōu)先級編碼器是一種組合電路,當(dāng)給定輸入位向量時,輸出向量中第一個1位的位置。例如,給定輸入8'b100 1 0000的 8 位優(yōu)先級編碼器將輸出3'd4,因為 bit[4] 是第一個高位。
構(gòu)建一個 4 位優(yōu)先級編碼器。對于這個問題,如果沒有一個輸入位為高(即輸入為零),則輸出零。請注意,一個 4 位數(shù)字有 16 種可能的組合
// synthesis verilog_input_version verilog_2001
module top_module (
input [3:0] in,
output reg [1:0] pos );
always @(*)
case(in)
4'd1 : pos =2'd0;
4'd2 : pos =2'd1;
4'd3 : pos =2'd0;
4'd4 : pos =2'd2;
4'd5 : pos =2'd0;
4'd6 : pos =2'd1;
4'd7 : pos =2'd0;
4'd8 : pos =2'd3;
4'd9 : pos =2'd0;
4'd10 : pos =2'd1;
4'd11 : pos =2'd0;
4'd12 : pos =2'd2;
4'd13 : pos =2'd0;
4'd14 : pos =2'd1;
4'd15 : pos =2'd0;
default: pos= 2'd0;
endcase
endmodule
------------------------------官網(wǎng)答案:-------------------------------------------
module top_module (
input [3:0] in,
output reg [1:0] pos
);
always @(*) begin // Combinational always block
case (in)
4'h0: pos = 2'h0; // I like hexadecimal because it saves typing.
4'h1: pos = 2'h0;
4'h2: pos = 2'h1;
4'h3: pos = 2'h0;
4'h4: pos = 2'h2;
4'h5: pos = 2'h0;
4'h6: pos = 2'h1;
4'h7: pos = 2'h0;
4'h8: pos = 2'h3;
4'h9: pos = 2'h0;
4'ha: pos = 2'h1;
4'hb: pos = 2'h0;
4'hc: pos = 2'h2;
4'hd: pos = 2'h0;
4'he: pos = 2'h1;
4'hf: pos = 2'h0;
default: pos = 2'b0; // Default case is not strictly necessary because all 16 combinations are covered.
endcase
end
// There is an easier way to code this. See the next problem (always_casez).
endmodule
注意:
這里使用了十六進(jìn)制數(shù)進(jìn)行了case項的輸入,是一個很簡便的方式,值得借鑒,介紹了打字量
但同時我們應(yīng)該也會想到這樣的解答方式并不有效,因為代碼量有點繁多了。
- Always casez語句
為 8 位輸入構(gòu)建優(yōu)先級編碼器。給定一個 8 位向量,輸出應(yīng)報告向量中的第一位1。如果輸入向量沒有高位,則報告零。例如,輸入8'b100 1 0000應(yīng)該輸出3'd4,因為 bit[4] 是第一個高位。案例陳述中將有 256 個案例。如果 case 語句中的 case 項支持 don't-care 位,我們可以減少這個(減少到 9 個 case)。這就是z的情況:它在比較中將具有值z的位視為不關(guān)心.
例如,我們通過casez語句實現(xiàn)上題中的4輸入優(yōu)先級編碼器:
always @(*) begin
casez (in[3:0])
4'bzzz1: out = 0; // in[3:1] can be anything
4'bzz1z: out = 1;
4'bz1zz: out = 2;
4'b1zzz: out = 3;
default: out = 0;
endcase
end
本題256的case項采用casez語句實現(xiàn)形式如下:
// synthesis verilog_input_version verilog_2001
module top_module (
input [7:0] in,
output reg [2:0] pos );
always @(*)
casez(in)
8'bzzzz_zzz1: pos = 3'h0;
8'bzzzz_zz1z: pos = 3'h1;
8'bzzzz_z1zz: pos = 3'h2;
8'bzzzz_1zzz: pos = 3'h3;
8'bzzz1_zzzz: pos = 3'h4;
8'bzz1z_zzzz: pos = 3'h5;
8'bz1zz_zzzz: pos = 3'h6;
8'b1zzz_zzzz: pos = 3'h7;
default : pos = 3'h0;
endcase
endmodule
- 假設(shè)您正在構(gòu)建一個電路來處理來自游戲的 PS/2 鍵盤的掃描碼。鑒于收到的掃描碼的最后兩個字節(jié),您需要指出是否已按下鍵盤上的箭頭鍵之一。這涉及到一個相當(dāng)簡單的映射,它可以實現(xiàn)為具有四個案例的案例語句(或 if-elseif)。
image.png
您的電路有一個 16 位輸入和四個輸出。構(gòu)建識別這四個掃描碼并斷言正確輸出的電路。
為避免創(chuàng)建鎖存器,必須在所有可能的條件下為所有輸出分配一個值(另請參見always_if2)。僅僅有一個<tt style="box-sizing: border-box; font-family: "Roboto Mono", monospace;">默認(rèn)</tt>情況是不夠的。您必須為所有四種情況和默認(rèn)情況下的所有四個輸出分配一個值。這可能涉及大量不必要的輸入。解決此問題的一種簡單方法是在 case 語句 之前為輸出分配一個“默認(rèn)值” :
always@(*) begin
up = 1'b0; down = 1'b0; left = 1'b0; right = 1'b0;
case (scancode)
... // Set to 1 as necessary.
endcase
end
注意:
提前為所有可能的輸出分配一個值將有效避免鎖存現(xiàn)象。
這種代碼風(fēng)格確保在所有可能的情況下為輸出分配一個值(0),除非 case 語句覆蓋了分配。這也意味著default: case 項變得不必要了。
因此這題的代碼參考如下:
// synthesis verilog_input_version verilog_2001
module top_module (
input [15:0] scancode,
output reg left,
output reg down,
output reg right,
output reg up );
always@(*) begin
left = 1'b0; down = 1'b0; right = 1'b0; up=1'b0;
case(scancode)
16'he06b : left = 1'b1;
16'he072 : down = 1'b1;
16'he074 : right = 1'b1;
16'he075 : up = 1'b1;
default : up = 1'b0;
endcase
end
endmodule



