HDLBits 刷題(Procedures部分)

  • 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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容