HDLbits exercises 3 (procedures节选题)

目录

1\  ALWAYS IF

2\  ALWAYS IF2

3\  ALWAYS CASE

4\  ALWAYS CASE2

5\  ALWAYS NOLATCHES


预备知识点:
always块有两种形式,一种是括号里是*,另一种是有posedge和nagedge的,其中前者用于组合逻辑电路,后者用于时序逻辑电路。

【阻塞赋值 vs 非阻塞赋值】
verilog中有三种赋值方式:
连续赋值(assign x=y;),只能在always块外使用。
阻塞赋值(x=y;),只能在always块内使用。
非阻塞赋值(x<=y;)只能在always块内使用。
在组合逻辑的always块中(always @(*))使用阻塞赋值语句;
在时序逻辑的always块中(always @(posedge clk))使用非阻塞赋值语句。

1\  ALWAYS IF

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.

CORRECT:
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

HINT:

2\  ALWAYS IF2

如何避免在使用if语句时生成锁存器?

tip:锁存器与触发器的区别?

锁存器是一种对脉冲电平(也就是0或者1)敏感的存储单元电路,而触发器是一种对脉冲边沿(即上升沿或者下降沿)敏感的存储电路。
当我们在设计电路时,不能直接先写成代码然后期望它直接生成为合适的电路,如下典型错误所示:

If (cpu_overheated) then shut_off_computer = 1;
If (~arrived) then keep_driving = ~gas_tank_empty;
语法上正确的代码并不意味着设计成的电路也是合理的。我们来思考这么一个问题,如上图的错误示例,如果if条件不满足,输出如何变化呢?Verilog给出的解决方法是:保持输出不变。因为组合逻辑电路不能记录当前的状态,所以就会综合出锁存器。

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

CORRECT:

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

HINT:

这个题有点意思,是让你纠正bug的题。题目的初衷就是让你用if或者case语句的时候一定要把所有情况都列出来,别写得跟“无头厉鬼”似的,让人摸不着头脑。

3\  ALWAYS CASE

Case statements are more convenient than if statements if there are a large number of cases. So, in this exercise, 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.

Be careful of inferring latches

ERRO:

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)
                1'd0:out=data0;
                1'd1:out=data1;
                1'd2:out=data2;
                1'd3:out=data3;
                1'd4:out=data4;
                1'd5:out=data5;
                1'd6:out=0;
                1'd7:out=0;
                default:out=0;

        endcase
    end

endmodule
 

CORRECT:

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'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

HINT:

既然前面都定义是4位的data信号了,就别非得用十进制秀了。。。。

4\  ALWAYS CASE2

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.

CORRECT1:

module top_module (
    input [3:0] in,
    output reg [1:0] pos  );

    always@(*)
        begin
            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
        end
endmodule 

CORRECT2:

module top_module (
    input [3:0] in,
    output reg [1:0] pos  );
    always @(*) begin
        case(1)
            in[0]:pos = 0;
            in[1]:pos = 1;
            in[2]:pos = 2;
            in[3]:pos = 3;
            default:pos = 0;

        endcase
    end
endmodule

CORRECT3:

module top_module (
    input [3:0] in,
    output reg [1:0] pos  );
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
endmodule

HINT:

Using hexadecimal (4'hb) or decimal (4'd11) number literals would save typing vs. binary (4'b1011) literals.

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

构建一个4位优先级编码器。 对于此问题,如果所有输入位都不为高(即输入为零),则输出零。 请注意,一个4位数字具有16种可能的组合。
CORRECT1不好,因为它不适用情况多的项目;其他两种比较简洁方便。

5\  ALWAYS NOLATCHES

假设构建一个电路来处理游戏的PS/2键盘上的扫描代码。对于收到的最后两个字节的扫描码,我们需要指示是否按下了键盘上的一个方向键。这涉及到一个相当简单的映射,它可以实现为一个case语句(或if-elseif),包含四个case。

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

ERRO:

module top_module (
    input [15:0] scancode,
    output reg left,
    output reg down,
    output reg right,
    output reg up  ); 
    always @(*)
        begin
            case(scancode)
                16'he06b:left=1;
                16'he072:down=1;
                16'he074:right=1;
                16'he075:up=1;
                default:left=0;
                default:down=0;
                default:right=0;
                default:up=0;

            endcase
        end
endmodule
 

CORRECT:

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

HINT:

case语句中的default不能有多个只能有一个,但是为避免生成锁存器,所有的输入情况必须要被考虑到。但仅有一个简单的default是不够的,我们必须在case item和default中为4个输出进行赋值,这会导致很多不必要的代码编写。

一种简单的方式就是对输出先进行赋初值的操作,这种类型的代码确保在所有可能的情况下输出都被赋值,除非case语句覆盖了赋值。这也意味着不再需要缺省的default项。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

wo~he!

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

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

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

打赏作者

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

抵扣说明:

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

余额充值