31 if 语句与锁存器

题目描述

使用 verilog 设计电路时,应按照如下流程:

  • 确定你需要的电路或逻辑门
  • 确定输入输出信号,以及产生输出信号的组合逻辑块
  • 确定组合逻辑块后面是否加上一组触发器。

因此,应极力避免这样的心态:试着写下一段代码,然后期待其生成正确的电路,如:

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

语法正确的代码并不一定能产生功能正常的电路,一般来说都是因为不小心引入了锁存器造成的。如上述例子所示,除了指定的情况外(cpu_overheated),还有一些其它情况,这时会发生什么?在 verilog 中,其结果就是保持不变,这意味着要记住当前状态,从而产生了锁存。

另一个例子代码与图示如下:

always @(*) begin
    if (cpu_overheated)
       shut_off_computer = 1;
end

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

【USTC】verilog 习题练习 31-35-LMLPHP
为消除锁存器,我们应当使组合逻辑过程块中的条件完备,即 if 语句后应加上 else 语句。
试修改上述两段代码,以消除锁存器。

输入格式

输入信号 cpu_overheated, 位宽 1bit,控制 shut_off_computer 信号。 输入信号 arrived, 位宽 1bit,控制 keep_driving 信号。 输入信号 gas_tank_empty,位宽 1bit, 作为 keep_driving 的输入信号之一。

输出格式

输出信号 shut_off_computer,位宽 1bit,要求 cpu_overheated 为真时输出 1'b1,反之输出 1'b0。 输出信号 keep_driving,位宽 1bit,要求 arrived 为假时输出 ~gas_tank_empty,反之输出 1'b0。

【USTC】verilog 习题练习 31-35-LMLPHP

module top_module (
    input		cpu_overheated		,
    output	reg	shut_off_computer	,
    input      	arrived				,
    input      	gas_tank_empty		,
    output 	reg keep_driving
);
  	// Edit the code below
	always @(*) begin
    	if (cpu_overheated)
        	shut_off_computer = 1'b1;
        else
            shut_off_computer = 1'b0;
    end

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

32 case语句

题目描述

Verilog中的case语句几乎等同于if…else if…else…序列,其语法与C语言中的switch语句类似,如下例所示:
always @(*) begin     // This is a combinational circuit
    case (in)
      1'b1: out = 1'b1;  // 对应if(in>=1) 语句
      1'b0: out = 1'b0;  // 对应else语句
      default: out = 1'bx; //使用case实现组合逻辑时,必须有default,以防出现锁存器
    endcase            //endcase语句表示case语句结束,两者成对使用
end
-case语句以关键字case开始,以endcase结束,两者成对出现
-case语句中每个条目只执行一条语句,如要在一个条目下进行多个赋值,需要将多条预计放在begin/end关键字之间
-case条目允许重复或部分重叠,第一个匹配到的条目有效
当有多个条目进行选择时,使用case语句比if…else…语句方便很多,本例中,使用Verilog设计六选一选择器,当sel信号在0~5时,选择对应的数据输出,否则输出0,输入输出数据位宽均为4bit。

输入格式

1个 3bit 位宽选择信号 sel 6个 4bit 位宽数据信号 data0、data1、...

输出格式

1个 4bit 位宽信号 out

【USTC】verilog 习题练习 31-35-LMLPHP

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 : out = data0;
            3'b001 : out = data1;
            3'b010 : out = data2;
            3'b011 : out = data3;
            3'b100 : out = data4;
            3'b101 : out = data5;
       		default : out = 4'b0000;
        endcase
    end

endmodule

33 优先编码器

题目描述

优先编码器是一种组合电路,当给定一个输入位矢量时,输出矢量中第一个 1 位的位置。例如,给定输入 8'b10010000 的 8 位优先级编码器将输出 3'd4,因为位 [4] 是高的第一位。
设计一个 4 位优先编码器电路。如果没有输入位为高电平(即输入为零),则输出为零。请注意,4 位数字有 16 种可能的组合。

输入格式

4'b1000

输出格式

2'd3

【USTC】verilog 习题练习 31-35-LMLPHP

module top_module(
  input [3:0] in,
  output reg [1:0] pos
);
  // Write your code here
    always @(*)
        case (in)
            4'b0000 : pos = 0;
            4'b0001 : pos = 0;
            4'b0010 : pos = 1;
            4'b0011 : pos = 0;
            4'b0100 : pos = 2;
            4'b0101 : pos = 0;
            4'b0110 : pos = 1;
            4'b0111 : pos = 0;
            4'b1000 : pos = 3;
            4'b1001 : pos = 0;
            4'b1010 : pos = 1;
            4'b1011 : pos = 0;
            4'b1100 : pos = 2;
            4'b1101 : pos = 0;
            4'b1110 : pos = 1;
            4'b1111 : pos = 0;
            default : pos = 0;
        endcase
endmodule

34 casez语句

题目描述

对于一个8bit输入信号的优先级编码器。例如,输入8'b10010000应该输出3'd4,因为位[4]是第一个高位。如果使用上例中的case语句,则需要包含256个条目。但如果case语句中的case项支持don-care位,我们可以将其减少到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
case语句的行为就好像每个项都是按顺序检查的。有某些输入(例如4'b1111)匹配多个条目,则选择第一个匹配项(因此4'b1111匹配第一个项,out=0,而不匹配后面的任何项)。
还有一个类似的casex,它将x和z都视为不在乎,此处不再介绍。
创建一Verilog模块,实现8bit信号输入的优先级编码器

输入格式

8'b10010000

输出格式

3'd4

module top_module (
    input [7:0] in,
    output reg [2:0] pos  );

    //code here:
    always @(*) begin
        casez (in[7:0])
            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

35 避免锁存器

题目描述

假设您正在构建一个电路来处理游戏中PS/2键盘上的扫描码。给定接收到的扫描码的最后两个字节,您需要指示是否按下了键盘上的一个箭头键。这涉及到一个相当简单的映射,它可以实现为一个case语句(或者if…else if…),包含四个case。
【USTC】verilog 习题练习 31-35-LMLPHP
您的电路有一个16位输入和四个输出,该电路识别这四个扫描码并确认正确的输出。为避免产生锁存,必须在所有四种情况和默认(default)情况下,为所有四个输出指定一个值。这可能涉及许多不必要的输入。解决这个问题的一个简单方法是在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项变得不必要。
请试着将上述代码补充完整。

输入格式

16位宽扫描码scancode

输出格式

1位宽输出left,right,up,down

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)
            // Set to 1 as necessary.
			16'he075 : up = 1'b1;
            16'he072 : down = 1'b1;
            16'he06b : left = 1'b1;
            16'he074 : right = 1'b1;
            default : begin
                up = 1'b0;
                down = 1'b0;
                left = 1'b0;
                right = 1'b0;
            end
        endcase
    end
endmodule
01-22 20:24