日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

HDLBits答案(4)_如何避免生成锁存器?

發布時間:2023/12/19 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 HDLBits答案(4)_如何避免生成锁存器? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

HDLBits_Verilog Language_Procedures

HDLBits鏈接


組合邏輯的always塊

[David說]:

考慮到硬件的可綜合性,有兩種always塊是有意義的:

  • 組合:always @(*)
  • 時序:always @(posedge clk)

組合邏輯的always塊和assign賦值是等價的,使用哪一種完全看哪一種更方便。always塊內可有更豐富的狀態,如if-then,case等,但不能有連續賦值語句assign。

如果在always塊內指定了特定的信號,但沒有的話,和always(*)綜合結果相同,此時功能仿真結果和硬件結果就有所差異。

assign賦值語句的左邊一般為wire類型,always塊中左邊的變量一般為reg類型。這些類型與硬件綜合無關,僅是verilog語法的要求。

題目描述

使用賦值語句和組合always塊兩種方式構建與門。

Solution

module top_module(input a, input b,output wire out_assign,output reg out_alwaysblock );assign out_assign = a & b;always @(*) beginout_alwaysblock = a & b; end endmodule

時序邏輯的always塊

[David說]:時序always塊可像組合always塊那樣生成電路,同時也會生成一系列的觸發器,寄存器等,因為輸出要等到下個時鐘延才能輸出。

阻塞賦值 vs 非阻塞賦值

verilog中有三種賦值方式:

  • 連續賦值(assign x=y;),只能在always塊外使用。
  • 阻塞賦值(x=y;),只能在always塊內使用。
  • 非阻塞賦值(x<=y;)只能在always塊內使用。

組合邏輯的always塊中(always @(*))使用阻塞賦值語句;

時序邏輯的always塊中(always @(posedge clk))使用非阻塞賦值語句。

題目描述

使用賦值語句、組合always塊和時序always塊三種方式構建一個異或門。

Solution:

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;always @(*) beginout_always_comb = a ^ b;endalways @(posedge clk) beginout_always_ff <= a ^ b;end endmodule

IF選擇器

[David說]:

一個if語句會產生一個2選1的數據選擇器,需注意的是,并不是if選擇的那路數據才被實現成電路模式,而是if和else兩路都被實現為電路形式然后用選擇器選擇輸出。

if的兩種方式:

always @(*) beginif (condition) beginout = x;endelse beginout = y;end end assign out = (condition) ? x : y;

題目描述

2-1選擇器

sel_b1sel_b2out_assign/out_always
00a
01a
10a
11b

Solution:

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 @(*) beginif(sel_b2&sel_b1) beginout_always = b;endelse beginout_always = a;endend endmodule

IF中鎖存器問題

[David說]:

如何避免在使用if語句時生成鎖存器?

tip:鎖存器與觸發器的區別?

  • 鎖存器是一種對脈沖電平(也就是0或者1)敏感的存儲單元電路,而觸發器是一種對脈沖邊沿(即上升沿或者下降沿)敏感的存儲電路。

當我們在設計電路時,不能直接先寫成代碼然后期望它直接生成為合適的電路,如下典型錯誤所示:

  • If (cpu_overheated) then shut_off_computer = 1;
  • If (~arrived) then keep_driving = ~gas_tank_empty;

語法上正確的代碼并不意味著設計成的電路也是合理的。我們來思考這么一個問題,如上圖的錯誤示例,如果if條件不滿足,輸出如何變化呢?Verilog給出的解決方法是:保持輸出不變。因為組合邏輯電路不能記錄當前的狀態,所以就會綜合出鎖存器。

所以當我們使用if語句或者case語句時,我們必須考慮到所有情況并給對應情況的輸出進行賦值,就意味著我們要為else或者default中的輸出賦值。

題目描述:找BUG,解決下面的代碼中包含的創建鎖存器的不正確行為。

always @(*) beginif (cpu_overheated)shut_off_computer = 1; endalways @(*) beginif (~arrived)keep_driving = ~gas_tank_empty; end

Solution

module top_module (input cpu_overheated,output reg shut_off_computer,input arrived,input gas_tank_empty,output reg keep_driving ); //always @(*) beginif (cpu_overheated) beginshut_off_computer = 1;endelse beginshut_off_computer = 0;endendalways @(*) beginif (~arrived) beginkeep_driving = ~gas_tank_empty;endelse beginkeep_driving = 0;endendendmodule

Case語句

[David說]:

在Verilog中,case語句與if-elseif-else相近,與c語言中的switch差別較大,示例如下:

always @(*) begin // This is a combinational circuitcase (in)1'b1: begin out = 1'b1; // begin-end if statement >1end1'b0: out = 1'b0;default: out = 1'bx;endcase end
  • case語句以case開始,每個case的選項以分號結束。
  • 每個case的選項中只能執行一個statement,所以就無需break語句。但如果我們想在一個case選項中執行多個statement,就需要使用begin...end
  • case中可以有重復的case item,首次匹配的將會被執行。

題目描述:6-1數據選擇器

Be careful of inferring latches!(需添加Default)

Solution

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 circuitcase(sel)3'b000:out=data0;3'b001:out=data1;3'b010:out=data2;3'b011:out=data3;3'b100:out=data4;3'b101:out=data5;default:out=3'b0;endcaseendendmodule

優先編碼器

題目描述:優先編碼器是一種組合電路,當給定輸入位向量時,輸出該向量中第一個1的位置。 例如,給定輸入8’b10010000的8位優先級編碼器將輸出3’d4,因為bit [4]是高的第一位。

構建一個4位優先級編碼器。 對于此問題,如果所有輸入位都不為高(即輸入為零),則輸出零。 請注意,一個4位數字具有16種可能的組合。

Solution

1、根據慣性思維使用case語法,列出每一種情況,然后列出其對應的輸出。

case(input)4'b0001: output = 1;.......4'b1111:output = 1;default: output = 0;endcase

2、如果按上述思路來寫,那么更多位的優先編碼器如何實現呢?其實有更簡單的方法,目前既不用casex也不用casez,這兩位要在后面出場。來看另一種思路:

module top_module (input [3:0] in,output reg [1:0] pos );always @(*) begincase(1)in[0]:pos = 0;in[1]:pos = 1;in[2]:pos = 2;in[3]:pos = 3;default:pos = 0;endcaseend endmodule

根據上面所說的case的性質,case中可以有重復的case item,但首次匹配的才會被執行。再看上面的case語句,即從低到高位去比較in中是否有數據位為1,下面即使有重復為1的也只會執行首次匹配的操作。如此,N位優先編碼器只需要N個case分支即可,大大簡化代碼量。


casez實現優先編碼器

題目描述

為8位輸入構建優先級編碼器。當給定輸入位向量時,輸出該向量中第一個1的位置。如果輸入向量沒有高位,則報告為零。 例如,給定輸入8’b10010000的8位優先級編碼器將輸出3’d4,因為bit [4]是高的第一位。

[David說]:由上一個練習我們知道,case語句中將有256種case item,使用casez以后,我們可以減少需比較的case item,這就是casez的目的,在比較中,將值z的位視作無關位。

所以上題的另一種解法為:

always @(*) begincasez (in[3:0])4'bzzz1: out = 0; // in[3:1] can be anything4'bzz1z: out = 1;4'bz1zz: out = 2;4'b1zzz: out = 3;default: out = 0;endcase end

case語句的行為就好像是按順序檢查每個項一樣(實際上,它所做的事情更像是生成一個巨大的真值表,然后進行門操作)。注意某些輸入(例如4’b1111)是如何匹配多個case項的。選擇第一個匹配項(因此4’b1111匹配第一個項目,out = 0,但不匹配后面的任何項目)。

  • 還有類似的casex,它將x和z均無視掉,但用它來代替casez的意義不大。
  • 符號?是z的同義詞,所以2‘bz0=2’b?0

Solution

module top_module (input [7:0] in,output reg [2:0] pos );always @(*) begincasez(in)8'bzzzzzzz1:pos=0;8'bzzzzzz1z:pos=1;8'bzzzzz1zz:pos=2;8'bzzzz1zzz:pos=3;8'bzzz1zzzz:pos=4;8'bzz1zzzzz:pos=5;8'bz1zzzzzz:pos=6;8'b1zzzzzzz:pos=7;default:pos=0;endcaseend endmodule

該題也可以按Priority encoder中的解法2來寫,復雜度甚至更低。


去除鎖存器

題目描述

假設構建一個電路來處理游戲的PS/2鍵盤上的掃描代碼。對于收到的最后兩個字節的掃描碼,我們需要指示是否按下了鍵盤上的一個方向鍵。這涉及到一個相當簡單的映射,它可以實現為一個case語句(或if-elseif),包含四個case。

電路有一個16位輸入和四個輸出。建立能識別這四種掃描碼并正確輸出的電路。

Scancode[15:0]Arrow key
16’he06bleft arrow
16’he072down arrow
16’he074right arrow
16’he075up arrow
Anything elsenone

[David說]:為避免生成鎖存器,所有的輸入情況必須要被考慮到。但僅有一個簡單的default是不夠的,我們必須在case item和default中為4個輸出進行賦值,這會導致很多不必要的代碼編寫。

一種簡單的方式就是對輸出先進行賦初值的操作,這種類型的代碼確保在所有可能的情況下輸出都被賦值,除非case語句覆蓋了賦值。這也意味著不再需要缺省的default項。如下面示例代碼:

always @(*) beginup = 1'b0; down = 1'b0; left = 1'b0; right = 1'b0;case (scancode)... // Set to 1 as necessary.endcase end

Solution

module top_module (input [15:0] scancode,output reg left,output reg down,output reg right,output reg up ); always @(*) beginleft=0;down=0;right=0;up=0;case(scancode)16'he06b:left=1;16'he072:down=1;16'he074:right=1;16'he075:up=1;endcaseend endmodule

總結:

  • 學習了組合和時序兩種always塊,并知曉了兩種類型的always塊中變量的類型即賦值方式。何時使用wire與reg,何時用阻塞和非阻塞賦值。

  • 學習了如何在使用if和case語句時避免生成鎖存器

    將所有狀態均考慮到并為其輸出賦值,考慮不全可用else/default。

    在case之前為所有輸出賦初值,這樣可以不用使用default,除非滿足case item進行覆蓋賦值,否則仍保持初值。

  • 學習了case語句的妙用,以及使用casez忽略無關位,更簡便的實現優先編碼器。

總結

以上是生活随笔為你收集整理的HDLBits答案(4)_如何避免生成锁存器?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。