4、Verilog Language-Procedures

Alwaysblock1 - HDLBits链接

下列答案不唯一!


目录

1、Always blocks (combinational)

2、Always blocks (clocked)

3、If statement

4、If statement latches

5、Case statement

6、Priority encoder

7、Priority encoder with casez

8、Avoiding latches


1、Always blocks (combinational)

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


        对于硬件的设计,存在两种类型的always块:

组合:always@ (*)

时序:always@(posedge clk)


        组合逻辑的always块和assign赋值是等价的,因此总有一种方法可以用两种方式表达组合电路,使用哪一种语法择主要是看哪种语法更方便。

        例如,分配和组合总是块描述相同的电路。两者都创建了相同的组合逻辑。当任何输入(右边)改变值时,两者都将重新计算输出。

提供下列模板:

assign out1 = a & b | c ^ d;

always @(*) out2 = a & b | c ^ d;


        关于wire和reg的注意事项:assign赋值语句的左边一般为wire类型,always块中左边的变量一般为reg类型。这仅是Verilog语法的要求。

题目练习:

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

 代码:

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

2、Always blocks (clocked)

        与组合always块一样,时序always块创建一个组合逻辑blob,但也在组合逻辑blob的输出处创建一组触发器(或“寄存器”)。逻辑blob的输出不是立即可见的,而是在下一个(posedge clk)之后立即可见的。

阻塞与非阻塞:
在Verilog中有三种类型的赋值:

连续的赋值(assign x = y;)。只能在过程块之外使用("always block")。

过程阻塞赋值:(x = y;)。只能在过程中使用。

过程非阻塞赋值:(x <= y;)只能在过程中使用。

组合逻辑的always块中(always @(*))使用阻塞赋值语句,

时序逻辑的always块中(always @(posedge clk))使用非阻塞赋值语句。

题目练习:

        使用赋值语句、组合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

3、If statement

        如下所示,if语句创建一个2选1的多路器,当条件为真时选择一个输入,当条件为假时选择另一个输入。

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

上述也相当于在条件运算符中使用连续赋值:

assign out = (condition) ? x : y;

题目练习:

        构建一个2选1的多路选择器,在a和b之间进行选择。如果sel_b1和sel_b2都为真,则选择b。否则,选择a。重复执行两次,一次使用assign语句,一次使用过程中if语句。

 代码:

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_b2, sel_b1} == 2'b11) ? b : a;

always@(*)begin
	if({sel_b2, sel_b1} == 2'b11)
		out_always = b;
	else 
		out_always = a;
end

endmodule


//方法二
/*
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 == 1 ? b : a;
    always@(*) begin
        if(sel_b1 & sel_b2 == 1) begin
           out_always = b; 
        end
        else begin
           out_always = a; 
        end
    end
endmodule



*/

4、If statement latches

        一个常见的问题:如何避免锁存器的产生。当设计电路时,我们必须首先从电路的角度考虑,不应该是先写代码,然后希望它产生一个合适的电路。

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

        语法上正确的代码并不意味着设计成的电路也是合理的。例如上述示例,如果if条件不满足,输出如何变化呢?而Verilog给出的解决方法就是保持输出不变。因为组合逻辑电路不能记录当前的状态,所以就会产生锁存器。

        所以当我们使用if语句或者case语句时,我们必须考虑到所有情况并给对应情况的输出进行赋值,就意味着我们要为else或者default中的输出赋值。

题目练习:

        找BUG,解决下面的代码中包含的创建锁存器的不正确行为。

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;
    end

    always @(*) begin
        if (~arrived)
           keep_driving = ~gas_tank_empty;
    end

endmodule

 代码:

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) & (~gas_tank_empty)) 
           keep_driving = 1;
        else begin
            keep_driving = 0;
        end
    end

endmodule

5、Case statement

        Verilog中的Case语句几乎等价于一个if-elseif-else序列

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

题目练习:

         如果案例数量较多,案例陈述比if陈述更方便。因此,在这个练习中,创建一个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(...)
    end

endmodule

代码:

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'b000 : out = data0;//之所以用3'b000是因为input[2:0] sel
            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

6、Priority encoder

        优先编码器是一种组合电路,当给定一个输入位向量时,输出该向量中前1位的位置。例如,给定输入8'b10010000的8位优先级编码器将输出3'd4,因为位[4]是高的第一个位。

        构建一个4位优先编码器。对于这个问题,如果没有一个输入位是高的(即输入为零),输出为零。请注意,4位数字有16种可能的组合。

module top_module (
    input [3:0] in,
    output reg [1:0] pos  );
    always@(*) begin
        case(in)
            4'b0000: pos=2'b00;
            4'b0001: pos=2'b00;
            4'b0010: pos=2'b01;
            4'b0011: pos=2'b00;
            4'b0100: pos=2'b10;
            4'b0101: pos=2'b00;
            4'b0110: pos=2'b01;
            4'b0111: pos=2'b00;
            4'b1000: pos=2'b11;
            4'b1001: pos=2'b00;
            4'b1010: pos=2'b01;
            4'b1011: pos=2'b00;
            4'b1100: pos=2'b10;
            4'b1101: pos=2'b00;
            4'b1110: pos=2'b01;
            4'b1111: pos=2'b00;
        endcase
    end
endmodule

7、Priority encoder with casez

        根据前面的练习, case语句中有256个case。如果case语句中的case项不考虑bit位,我们可以减少(减少到9种情况)。这就是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

还有一种类似的情况,casex将x和z都视为无关紧要。我看不出用它来对付casez有什么用。

显式指定优先级行为可能比依赖于用例项的排序更不容易出错。例如,如果某些case项被重新排序,下面的代码仍然会以同样的方式运行,因为任何位模式最多只能匹配一个case项:

casez (in[3:0])
        4'bzzz1: ...
        4'bzz10: ...
        4'bz100: ...
        4'b1000: ...
        default: ...
    endcase

 题目练习:

        为8位输入构建优先级编码器。给定一个8位向量,输出应该报告向量中第一个(最低有效)位为1的位。如果输入向量没有高的位,则报告为零。例如,输入8'b10010000应该输出3'd4,因为位[4]是高的第一个位。

module top_module (
    input [7:0] in,
    output reg [2:0] pos );
	
    always@(*) begin
        casez(in)
            8'bzzzzzzz1 : pos = 3'b000;
            8'bzzzzzz10 : pos = 3'b001;
            8'bzzzzz100 : pos = 3'b010;
            8'bzzzz1000 : pos = 3'b011;
            8'bzzz10000 : pos = 3'b100;
            8'bzz100000 : pos = 3'b101;
            8'bz1000000 : pos = 3'b110;
            8'b10000000 : pos = 3'b111;
            default: pos = 3'b000;
        endcase
    end
endmodule

8、Avoiding latches

        假设你正在构建一个电路来处理来自PS/2键盘的游戏扫描代码。给定接收到的扫描码的最后两个字节,您需要指出是否按了键盘上的一个方向键。这涉及到一个相当简单的映射,它可以用一个case语句(或if-elseif)实现,有四个case。

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

        你的电路有一个16位输入和四个输出。建立这个电路,识别这四个扫描码,并断言正确的输出。


        为了避免创建锁存,所有输出都必须在所有可能的条件下赋值(参见4、If statement latches)。仅仅有一个默认情况是不够的。必须为所有四种情况下的所有四种输出以及默认情况下的输出分配一个值。这可能涉及很多不必要的输入。一个简单的方法是在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)。

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


 

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值