HDLBits(四)学习笔记——Procedures

Alwaysblock1(组合逻辑)

由于数字电路由与导线连接的逻辑门组成,因此任何电路都可以表示为模块和赋值语句的某种组合。
有如下两种always块:
组合逻辑:always @ (*) ——等价于赋值语句 / 赋值语句(assign)的左侧必须是wire类型 / 带有敏感列表 ( * )用reg型
时序逻辑:always @ (clk) —— 变量类型reg型

练习:

使用赋值语句和组合always块构建与门。

module top_module(
    input a, 
    input b,
    output wire out_assign ,
    output reg out_alwaysblock  
);
    //赋值语句
    assign out_assign = a && b;
    //always块
    always @ (*) begin    
        out_alwaysblock = a && b;   
    end
endmodule

Alwaysblock2(时序逻辑)

时序always块会生成一系列的触发器(寄存器),另外输出数据不是立刻输出,而是再下一个时钟上升沿输出(posedge clk)。

Verilog三种赋值方式(阻塞和非阻塞)

  • 连续赋值(assign x=y;),只能在always块外使用。
  • 阻塞赋值(x=y;),只能在always块内使用。 ——组合逻辑中用
  • 非阻塞赋值(x<=y;)只能在always块内使用。——时序逻辑中用

练习

使用赋值语句、组合always块和时序always块三种方式构建一个异或门。
在这里插入图片描述

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 @ (*) begin
        
        out_always_comb = a ^ b;
        
    end
    
    always @ (posedge clk)begin
        
        out_always_ff <= a ^ b;
        
    end 
endmodule

Always if 1

if 语句通常创建一个 2 对 1 多路复用器,如果条件为真,则选择一个输入,如果条件为 false,则选择另一个输入。格式如下:

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

等效于使用条件运算符的连续赋值:

assign out = (condition) ? x : y;

练习

使用 if语句 构建在 a b 之间进行选择的 2 对 1 多路复用器。如果sel_b1和sel_b2都为真,请选择 b。否则,请选择 a。

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 
            out_always = a;
    end 
endmodule

Always if 2

避免生成锁存器

  • 锁存器是一种对脉冲电平(也就是0或者1)敏感的存储单元电路,而触发器是一种对脉冲边沿(即上升沿或者下降沿)敏感的存储电路。
  • 进行电路设计时,不能先写代码,后生成电路;语法正确的代码并不一定会产生合理的电路。
  • 典型错误如下:
If (cpu_overheated) then shut_off_computer = 1;

当 if 语句不满足条件的时候,Verilog给出的解决方法是保持输出不变,但是组合逻辑电路是不能记录当前状态的,因此会综合出锁存器。

因此我们使用if语句或者case语句的时候,应该对所有的情况都给予对应的输出,避免锁存器的生成。

练习

下面代码造成了锁存器的生成,请进行修改:
在这里插入图片描述
在这里插入图片描述

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 case1

Verilog 中的 case 语句几乎等效于将一个表达式与一个表达式列表进行比较的 if-elseif-else 序列。格式如下:
在这里插入图片描述
注意:
当有大量事例的时候用case语句更方便。
每个case事例项只能执行一个语句。因此如果需要多个语句,则必须使用 begin …end。

练习

创建一个 6 对 1 多路复用器。当 sel 介于 0 和 5 之间时,选择相应的数据输入。否则,输出 0。数据输入和输出均为4位宽。(避免锁存器的生成)

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'b0;   
       endcase           
    end
endmodule

Always case2

优先编码器是一种组合电路,当给定输入位向量时,输出该向量中第一个1的位置。 例如,给定输入8’b10010000的8位优先级编码器,将输出3’d4。 bit[4] 是第一个高位——也就是看第一个高位1,出现再输入的第几位上面。

练习

构建一个4位优先级编码器。 对于此问题,如果所有输入位都不为高(即输入为零),则输出零。 请注意,一个4位数字具有16种可能的组合。
解: 看输入位的第一个高电平1 出现再第几位。

module top_module (
    input [3:0] in,
    output reg [1:0] pos  
);
    always @ (*) begin
        //第一种写法:
        //case(in)
            //4'b0000: pos = 2'b0;
            //4'b0001: pos = 2'b0;
            //4'b0010: pos = 2'b1;
            //4'b0011: pos = 2'b0;
            //......
            //default:pos = 2'b0;  
        //第二种写法:
        case(1)  //检测第一个高位所在位置 如果在输入位的0位,就输出0,以此类推
            in[0]:pos = 0; 
            in[1]:pos = 1; 
            in[2]:pos = 2; 
            in[3]:pos = 3; 
            default:pos = 0;
        endcase        
    end    
endmodule

Always casez

casez 的用途:它将值 z 的位视为不关心的位(无关位)
例如,这将实现上一练习中的 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

case语句的行为就好像是按顺序检查每个项一样(实际上,它所做的事情更像是生成一个巨大的真值表,然后进行门操作)。当某些输入匹配多种case项的时候,选择第一个匹配项,不匹配后面的任何项目。

还有类似的casex,它将x和z均无视掉,但用它来代替casez的意义不大。

符号?是z的同义词,所以2‘bz0=2’b?0

练习

用casez的方式来编写8位优先级编码器

module top_module (
    input [7:0] in,
    output reg [2:0] pos  );
    always @(*) begin
        casez(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;
        endcase
    end   
endmodule

Always nolatches

去除锁存器。
假设在构建一个电路,来处理来自 PS/2 键盘的扫描码的游戏。给定收到的最后两个字节的扫描码,根据扫描码指示是否按下了键盘上的某个箭头键。相当于一个简单的映射,可以实现为具有四个事例的case语句(或 if-elseif)。case事项如下:
在这里插入图片描述
为避免生成锁存器,必须位所有的可能情况分配输出值,另外还需要对输出进行赋初值的操作,这种类型的代码确保在所有可能的情况下输出都被赋值,除非case语句覆盖了赋值。这也意味着不再需要缺省的default项。如下面示例代码:

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

如下进行Verilog代码编写:

module top_module (
    input [15:0] scancode,
    output reg left,
    output reg down,
    output reg right,
    output reg up  
);   
    always @ (*)begin
        left = 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;  
       endcase      
    end
endmodule
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fighting_FPGA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值