HDLBits 刷题(Procedures部分)

  • alwaysblock 1:
    这里需要了解一下always过程块中的语法点:
    其赋值情况与assign语句的对比情况
assign out1 = a & b | c ^ d;
always @(*) out2 = a & b | c ^ d;

并且对于赋值情况而言,assign连续赋值语句中左侧的类型必须是线网类型(wire),而always语句块中的左侧类型必须是变量类型(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
    对于硬件综合来说,有两种相关的always块:
  • 组合 : always@(*);
  • 计时: always@(posedge clk);
    时钟always块创建一个组合逻辑块,就像组合always块一样,但也在组合逻辑块的输出处创建一组触发器(或“寄存器”)。不是立即可见逻辑块的输出,而是仅仅在下一个(posedge clk)之后立即可见输出。
  • Blocking vs. Non-Blocking Assignment (阻塞与非阻塞赋值)
  • 连续赋值(assign x = y;)。只能在不在过程中使用(“always block”总是阻塞)
  • 程序阻塞赋值:(x=y)。只能在程序内部使用。
  • 程序非阻塞赋值:(x<=y)。只能在程序内部使用
    在组合的always块中,使用阻塞赋值;而在时钟控制的always块中,使用非阻塞赋值。这个点还是很重要的,尤其是涉及时序电路分析时。
  • 练习:使用三种赋值方式构建一个异或门:


    练习图.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语句经常创建一个2 对 1 多路复用器,如果条件为真则选择一个输入,如果条件为假则选择另一个输入。


    image.png
always @(*) begin
  if (condition) begin
    out = x;
  end
  else begin
    out = y;
  end
end

上述代码等价于使用带有条件运算符的连续赋值语句:
assign out = (condition) ? x : y ;
但是,程序if语句提供了一种新的出错方式,仅当总是为out分配一个值,该电路才是组合电路。

  • 构建一个在a和b之间进行选择的 2 对 1 多路复用器。如果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常见的错误来源:如何避免锁存的产生
    设计电路时,首先要从电路方面考虑:

我想要这个逻辑门
我想要一个具有这些输入并产生这些输出的组合逻辑块
我想要一个组合的逻辑块,然后是一组触发器
你不能做的是先写代码,然后希望它生成一个合适的电路。

  • 如果 (cpu_overheated) 然后shut_off_computer = 1;
  • 如果 (~arrived) 那么 keep_driving = ~gas_tank_empty;
    语法正确的代码不一定会产生合理的电路(组合逻辑 + 触发器)。通常的原因是:“在您指定的情况以外的情况下会发生什么?”。Verilog 的回答是:保持输出不变。

这种“保持输出不变”的行为意味着需要记住当前状态,从而产生一个锁存器。组合逻辑(例如逻辑门)不能记住任何状态。注意警告(10240):...推断锁存器“消息。除非锁存器是故意的,否则它几乎总是表示错误。组合电路必须在所有条件下为所有输出分配一个值。这通常意味着您总是需要else子句或分配给输出的默认值。

  • 示例:
    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

    题目描述翻译过来就是这么个意思:
    以下代码包含创建闩锁的不正确行为。修复错误,以便您仅在计算机确实过热时关闭计算机,并在您到达目的地或需要加油时停止驾驶。所以当你arrived的时候,那就需要停止驾驶了.本题的初衷是为了避免锁存的产生,也就是将两个if语句的其他情况补齐。

// 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 语句几乎等同于将一个表达式与其他表达式进行比较的 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项”以冒号结尾,没有“switch”
  • 每个case项只能执行一个语句,这使得C中使用的“中断”变得不必要。但这意味着如果需要多个语句,则必须使用begin...end。
  • 允许重复(和部分重叠)case项,使用第一个匹配的。C不允许重复的case案例。
    练习:使用case语句创建一个6选1的多路复用器,当sel是0到5,选择相应的输出,其余情况下,输出为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项的时候,`'`要在位宽和数制之间*/

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.优先级编码器是一种组合电路,当给定输入位向量时,输出向量中第一个1位的位置。例如,给定输入8'b100 1 0000的 8 位优先级编码器将输出3'd4,因为 bit[4] 是第一个高位。
构建一个 4 位优先级编码器。对于这个问题,如果没有一个输入位为高(即输入为零),则输出零。请注意,一个 4 位数字有 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
------------------------------官网答案:-------------------------------------------
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

注意:

这里使用了十六进制数进行了case项的输入,是一个很简便的方式,值得借鉴,介绍了打字量
但同时我们应该也会想到这样的解答方式并不有效,因为代码量有点繁多了。

  • Always casez语句
    为 8 位输入构建优先级编码器。给定一个 8 位向量,输出应报告向量中的第一位1。如果输入向量没有高位,则报告零。例如,输入8'b100 1 0000应该输出3'd4,因为 bit[4] 是第一个高位。案例陈述中将有 256 个案例。如果 case 语句中的 case 项支持 don't-care 位,我们可以减少这个(减少到 9 个 case)。这就是z的情况:它在比较中将具有值z的位视为不关心.
    例如,我们通过casez语句实现上题中的4输入优先级编码器:
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语句实现形式如下:

// 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
  • 假设您正在构建一个电路来处理来自游戏的 PS/2 键盘的扫描码。鉴于收到的扫描码的最后两个字节,您需要指出是否已按下键盘上的箭头键之一。这涉及到一个相当简单的映射,它可以实现为具有四个案例的案例语句(或 if-elseif)。
    image.png

    您的电路有一个 16 位输入和四个输出。构建识别这四个扫描码并断言正确输出的电路。
    为避免创建锁存器,必须在所有可能的条件下为所有输出分配一个值(另请参见always_if2)。仅仅有一个<tt style="box-sizing: border-box; font-family: "Roboto Mono", monospace;">默认</tt>情况是不够的。您必须为所有四种情况和默认情况下的所有四个输出分配一个值。这可能涉及大量不必要的输入。解决此问题的一种简单方法是在 case 语句 之前为输出分配一个“默认值” :
always@(*) begin
  up = 1'b0; down = 1'b0; left = 1'b0; right = 1'b0;
    case (scancode)
        ... // Set to 1 as necessary.
    endcase
end
注意:

提前为所有可能的输出分配一个值将有效避免锁存现象。
这种代码风格确保在所有可能的情况下为输出分配一个值(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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容