HDLBits(2)——Procedures(上)

简介: HDLBits——Procedures问题28 Always blocks(combinational) (Alwaysblock1)A bit of practice问题29: Always blocks(clocked) (Alwaysblock2)Blocking vs. Non-Blocking AssignmentA bit of practice问题30: If statement(Always if)A bit of practice问题31If statement latches(Always if2)常见的错误来源:如何避免锁存。问题32: Case st

问题28 Always blocks(combinational) (Alwaysblock1)

由于数字电路是由用导线连接的逻辑门组成的,因此任何电路都可以表示为模块和赋值语句的某种组合。然而,有时这并不是描述电路的最方便的方式。Procedures (of which always blocks are one example) provide an alternative syntax for describing circuits.


对于综合硬件,两种类型的 always 块是相关的:


Combinational: always @(*)

Clocked: always @(posedge clk)

组合always blocks 等同于赋值语句,因此总有一种方法可以双向表达组合电路。选择使用哪个主要是哪个语法更方便的问题。程序块内部代码的语法与外部代码不同。 程序块有更丰富的语句集(例如,if-then、case),不能包含连续赋值,但也引入了许多新的非直观的出错方式。(Procedural continuous assignments 确实存在,但与连续赋值有些不同,并且不可综合。)


例如,assign和combinational always块描述相同的电路。两者都创建了相同的组合逻辑块。无论何时任何输入(右侧)改变值,两者都将重新计算输出。

assign out1 = a & b | c ^ d;
always @(*) out2 = a & b | c ^ d;
• 1
• 2

对于combinational always块,始终使用 (*)的敏感度列表。显式列出信号容易出错(如果您错过了一个信号),在硬件合成中会被忽略。如果显式指定灵敏度列表并错过信号,则合成硬件的行为仍将如同指定了(*),但模拟将与硬件的行为不匹配。(在 SystemVerilog 中,使用 always_comb。)关于 wire 与 reg 的注意事项:assign 语句的左侧必须是 net 类型(例如,wire),而过程赋值(在 always block中)的左侧必须是变量类型(例如,reg)

A bit of practice

使用 assign 语句和组合 always block构建 AND 门。

// 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 @(*) begin
        out_alwaysblock = a & b;
    end
endmodule


问题29: Always blocks(clocked) (Alwaysblock2)

对于硬件综合,有两种相关的 always block

组合型: always @(*)

时序型: always @(posedge clk)

Clocked always blocks总是创建了一个组合逻辑块,而且还会在组合逻辑块的输出处创建一组触发器(或“寄存器”)。不是立即可见逻辑块的输出,而是仅在下一个(posedge clk)之后立即可见输出。


Blocking vs. Non-Blocking Assignment

Verilog 中有三种类型的赋值:

Continuous assignments (assign x = y;). Can only be used when not inside a procedure (“always block”).

Procedural blocking assignment: (x = y;). Can only be used inside a procedure.

Procedural non-blocking assignment: (x <= y;). Can only be used inside a procedure.

在combinational always block,使用blocking assignments. 在clocked always block中,使用non-blocking assignments.

A bit of practice

以三种方式构建XOR gate,使用assign statement、combinational always block和clocked always block。请注意,the clocked always block产生的电路与其他两个不同:有一个触发器,因此输出被延迟

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


问题30: If statement(Always if)

if 语句通常创建一个 21 多路复用器,如果条件为真则选择一个输入,如果条件为假则选择另一个输入。

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

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

assign out = (condition) ? x : y;
• 1

但是,程序 if 语句提供了一种新的出错方式。仅当总是为 out 分配一个值时,该电路才是组合电路。

A bit of practice

构建一个在 ab 之间进行选择的 21 多路复用器。如果 sel_b1sel_b2 都为真,则选择 b。否则,选择a。做同样的事情两次,一次使用分配语句,一次使用过程 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_b2 & sel_b1) ? b : a;
    always@(*)begin
        if(sel_b2 & sel_b1)begin
            out_always = b;
        end
        else begin
            out_always = a;
        end
    end
endmodule


问题31If statement latches(Always if2)

常见的错误来源:如何避免锁存。

设计电路时,首先要从电路方面考虑:

I want this logic gate

I want a combinational blob of logic that has these inputs and produces these outputs

I want a combinational blob of logic followed by a set of flip-flops

你不能做的是先写代码,然后希望它生成一个合适的电路。

If (cpu_overheated) then shut_off_computer = 1;

If (~arrived) then keep_driving = ~gas_tank_empty;

语法正确的代码不一定会产生合理的电路(组合逻辑 + 触发器)。通常的原因是:“在您指定的情况以外的情况下会发生什么?”。 Verilog 的回答是:保持输出不变。


这种“保持输出不变”的行为意味着需要记住当前状态,从而产生一个锁存器。组合逻辑(例如逻辑门)不能记住任何状态。在所有条件下,组合电路必须为所有输出分配一个值。这通常意味着您总是需要 else 子句或分配给输出的默认值。

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

问题32: Case statement(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 item以冒号结尾。没有switch。

每个案例项只能执行一个语句。这使得 C 中使用的break变得不必要。但这意味着,如果您需要多个语句,则必须使用 begin ... end。

允许重复(和部分重叠)案例项目。使用第一个匹配的。 C语言不允许重复的案例项目。

A bit of practice

如果有大量 casecase 语句比 if 语句更方便。因此,在本练习中,创建一个 61 多路复用器。当 sel05 之间时,选择对应的数据输入。否则,输出 0。数据输入和输出均为 4 位宽。

// 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'b000: begin
                  out = data0;
                   end
            3'b001: begin
                  out = data1;
                   end
            3'b010: begin
                  out = data2;
                   end
            3'b011: begin
                  out = data3;
                   end
            3'b100: begin
                  out = data4;
                   end
            3'b101: begin
                  out = data5;
                   end
            default: begin
                  out = 4'b 0000;
                end
        endcase
    end
endmodule
// 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
        out = 4'b 0000;
        case(sel)
            3'b000: begin
                  out = data0;
                   end
            3'b001: begin
                  out = data1;
                   end
            3'b010: begin
                  out = data2;
                   end
            3'b011: begin
                  out = data3;
                   end
            3'b100: begin
                  out = data4;
                   end
            3'b101: begin
                  out = data5;
                   end
        endcase
    end
endmodule

相关文章