Verilog学习笔记 HDLBits——Procedures

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

由于数字电路由用线路连接的逻辑门组成,因此任何电路都可以表示为一些模块和赋值语句的组合。然而,有时这并不是描述电路最方便的方式。过程块提供了描述电路的另一种语法。

组合电路: always @(*)
时序电路: always @(posedge clk)

组合逻辑always等价于assign语句,因此总有一种方法可以用两种方式表达组合电路。使用哪一种语法的选择主要是哪种语法更方便的问题。过程块内部代码的语法与外部代码不同。过程块拥有更丰富的语句集(例如,if-then, case),不能包含连续的赋值*,但也引入了许多新的非直观的错误。(*过程式连续赋值确实存在,但与连续赋值有所不同,且不可综合。)
Verilog中的Case语句几乎等价于一个if-elseif-else序列,它将一个表达式与其他表达式的列表进行比较。它的语法和功能与C中的switch语句不同

一、Procedures

1.Always blocks(combinational)

Practice: Build an AND gate using both an assign statement and a combinational always block. (Since assign statements and combinational always blocks function identically, there is no way to enforce that you’re using both methods. But you’re here for practice, right?..)
翻译:根据下图,分别用assign语句和always块两种方式来写一个与门 。
在这里插入图片描述

Solution(不唯一,仅供参考):

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

endmodule


Timing Diagram
在这里插入图片描述

2.Always blocks(clocked)

阻塞赋值和非阻塞赋值:Verilog中有以下三种类型的赋值方式:

连续赋值:		(assign x=y;)	// 不能在过程块(always块)内使用;
过程阻塞赋值:	(x=y;)			// 只能在过程块内使用;
过程非阻塞赋值:	(x<=y;)			// 只能在过程块内使用。

Practice:Build an XOR gate three ways, using an assign statement, a combinational always block, and a clocked always block. Note that the clocked always block produces a different circuit from the other two: There is a flip-flop so the output is delayed.
翻译:构建异或门有三种方法:使用赋值语句、组合always块和计时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 @(*) begin 
       out_always_comb = a^b; 
    end
    always @(posedge clk)begin
       out_always_ff = a^b; 
    end
endmodule

Timing Diagram

在这里插入图片描述

3.If statement

Practice: Build a 2-to-1 mux that chooses between a and b. Choose b if both sel_b1 and sel_b2 are true. Otherwise, choose a. Do the same twice, once using assign statements and once using a procedural if statement.
翻译:建立一个二选一的数据选择器,用if和assign两种方法
在这里插入图片描述

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 @ (*)begin
        if(sel_b1&sel_b2)begin
           out_always = b;
        end
        else begin
            out_always = a; 
        end
    end

endmodule

Timing Diagram
在这里插入图片描述

4.If statement latches

Practice: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.
翻译:修复bug,只有在电脑过热的时候你才会关掉电脑,当你到达目的地或者需要加油的时候就不要开车了。

在这里插入图片描述

Solution(不唯一,仅供参考):

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)begin
           shut_off_computer = 1;
        end
        else begin
            shut_off_computer = 0;
        end
        
    end
    
    always @(*) begin
        if (~arrived)
           keep_driving = ~gas_tank_empty;
        else
           keep_driving = 0;
    end

endmodule

Timing Diagram

在这里插入图片描述

5.Case statement

Practice: create a 6-to-1 multiplexer. When sel is between 0 and 5, choose the corresponding data input. Otherwise, output 0. The data inputs and outputs are all 4 bits wide.
翻译:创建6选1的数据选择器。当sel在0到5之间时,选择相应的数据输入。否则,输出0。数据输入和输出都是4位宽的。

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  
        case(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 = 0;
        endcase
    end

endmodule

Timing Diagram
在这里插入图片描述

6.Priority encoder

Practice:Build a 4-bit priority encoder. For this problem, if none of the input bits are high (i.e., input is zero), output zero. Note that a 4-bit number has 16 possible combinations.performs 32-bit a + b).
翻译:构建一个4位优先编码器。对于这个问题,如果没有一个输入位是高的(即输入为零),输出为零。请注意,4位数字有16种可能的组合。

Solution(不唯一,仅供参考):

module top_module (
    input [3:0] in,
    output reg [1:0] pos  );
    always @(*)begin
        casex(in)
            4'bxxx1: pos = 0;
            4'bxx10: pos = 1;
            4'bx100: pos = 2;
            4'b1000: pos = 3;
            default: pos = 0;
        endcase
    end
	
endmodule

Timing Diagram
在这里插入图片描述

7.Priority encoder casez

Practice:Build a priority encoder for 8-bit inputs. Given an 8-bit vector, the output should report the first bit in the vector that is 1. Report zero if the input vector has no bits that are high. For example, the input 8’b10010000 should output 3’d4, because bit[4] is first bit that is high.
翻译:写一个8位优先编码器
提示:注意,casez是有优先级的!比如在上面的例子中,4’b1111能匹配4’bzzz1、4’bzz1z、4’bz1zz、4’b1zzz四项中的任一项,但是为什么最终out输出0,因为4’bzzz1写在最前面(第一个case项),所以它的优先级最高,4’b1111按out=0输出。如果把四个case项改写成4’bzzz1、4’bzz10、4’bz100、4’b1000,那4’b1111只能匹配4’bzzz1,所以不管把4’bzzz1放第几个,4’b1111都会按4’bzzz1这一项的out=0来输出。

Solution(不唯一,仅供参考):

module top_module (
    input [7:0] in,
    output reg [2:0] pos  );
    always @ (*)begin
        casez(in)
            8'bzzzzzzz1: pos = 0;
            8'bzzzzzz10: pos = 1;
            8'bzzzzz100: pos = 2;
            8'bzzzz1000: pos = 3;
            8'bzzz10000: pos = 4;
            8'bzz100000: pos = 5;
            8'bz1000000: pos = 6;
            8'b10000000: pos = 7;
            default: pos = 0;
        endcase
    end
endmodule

Timing Diagram
在这里插入图片描述

8.Avoiding latches

Practice:Suppose you’re building a circuit to process scancodes from a PS/2 keyboard for a game. Given the last two bytes of scancodes received, you need to indicate whether one of the arrow keys on the keyboard have been pressed. This involves a fairly simple mapping, which can be implemented as a case statement (or if-elseif) with four cases.
翻译:假设你正在构建一个电路来处理来自PS/2键盘的游戏扫描代码。给定接收到的扫描码的最后两个字节,您需要指出是否按了键盘上的一个方向键。这涉及到一个相当简单的映射,它可以用一个case语句(或if-elseif)实现,有四个case。
**提示:**为了避免创建锁存,所有输出都必须在所有可能的条件下赋值(参见always_if2)。仅仅有一个默认情况是不够的。必须为所有四种情况下的所有四种输出以及默认情况下的输出分配一个值。这可能涉及很多不必要的输入。一个简单的方法是在case语句之前给输出赋一个“默认值”:

always @(*) begin
    up = 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 @(*) begin
    up = 1'b0; down = 1'b0; left = 1'b0; right = 1'b0;
    case (scancode)
        16'he06b:  left = 1'b1;
        16'he072:  down = 1'b1;
        16'he074:  right = 1'b1;
        16'he075:  up = 1'b1;
        
    endcase
end
endmodule

Timing Diagram
在这里插入图片描述

总结

1、 对于组合always块,敏感列表总是使用“(星号)”。明确地列出信号容易出错(比如漏掉一个),并且这类错误在硬件综合时会被忽略:如果你明确地列出了敏感列表但漏了一个信号,综合出来的硬件电路仍与使用(*)时一样。但在仿真时,仿真器会按漏了一个信号的情况跑仿真,这会导致仿真结果与原硬件不匹配。
2、关于wire和reg的声明:assign赋值语句的左边必须是net类型(例如wire),而过程赋值语句(在always块中)的左边必须是变量类型(例如reg)。这些类型(wire和reg)与最终综合的硬件无关,只是Verilog作为硬件仿真语言使用时遗留下来的语法习惯。
3、为了避免Verilog代码综合后的电路中生成锁存器。如果用到if语句,最好写上else项,如果用到case语句,最好写上default项。
4、优先编码器是一种组合电路,当给定一个输入位向量时,输出该向量从右往左数(从低位到高位)第一个1的位置。例如,输入8’b10010000时,8位优先级编码器将输出3’d4,因为位[4]是从低到高第一个为1的位。(注:从右到左,最低的那位是第0位。)
5、还有一个类似的casex,将x和z都视为无关项。不认为casex相比casez有什么特别的意义。(z和x状态的问题涉及电路的基本知识)
· 符号"?"是z的同义词,所以2’bz0与2’b?0是相同的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值