HDLBits(十 四)学习笔记——查找代码中的bug / 根据仿真波形构建组合和时序电路


这部分主要是找代码中的bug。

一、 Finding bugs in code

MUX 二选一多路复用器

在这里插入图片描述
本题为MUX多路复用器,根据选择信号sel,来判断输出是a还是b,因此输出的位宽要和输入位宽是一致的。
同时sel是位宽是1位,而ab的位宽是8位,所以不能sel和输入一起做位运算。

module top_module (
    input sel,
    input [7:0] a,
    input [7:0] b,
    output[7:0]out 
);

    always @ (*)
        if(sel == 1'b1)
            out = a ;
        else
            out = b;

endmodule

nand3 与非门

此三输入 NAND 门不起作用。修复错误。

您必须使用提供的 5 输入 AND 门:

module andgate ( output out, input a, input b, input c, input d, input e );

题目分析:首先该题目进行了例化,那么既然例化,所给模块的所有信号都要写入,不能因为没有用到就擅自删除,没有用到的信号可以选择不连接,或者在本题目中,由于是与AND操作,那么没有用到的信号可以赋值1,一个数和1做与运算,还是它本身。
另外因为提供的是AND门,而我们最终要的是与非门,所以还要进行非运算,那么即可引出一根导线,对非运算后的输出进行连接即可。

wire型和reg型

在Verilog中,wire就是相当于一条导线,用来连接电路,不能存储数据,无驱动能力,是组合逻辑,只能在assign左侧赋值,不能用于always
块中赋值;
reg为寄存器型,可以综合成寄存器,latch,甚至wire(当其只是中间变量的时候),可以用于组合逻辑或者时序逻辑(只要是在always块中的信号都定义成reg型),能存储数据,有驱动能力,在always块表达式左侧被赋值。

关于例化列表信号的顺序问题

如果采用下面的例化方式,那么例化列表的顺序是可以改变的,只要信号与括号内的信号相互对应就可以成功连接。——名称相关

andgate inst1 ( 
    .a(a),
    .b(b),
    .c(c),
    .e(1'b1),
    .d(1'b1),
    .out(out1)
);

如果采用下面的例化方式,那么顺序就不能改变,必须一 一对应——位置相关

andgate inst1 (out1,a,b,c,1'b1,1'b1); 

最终代码:

module top_module (input a, input b, input c, output out);
    wire out1;
    andgate inst1 (out1,a,b,c,1'b1,1'b1); 
    assign out = ~ out1;
endmodule

mux4 四选一多路复用器

题目给了我们一个二选一多路器,并且需要在此基础上来实现四选一多路器

module mux2 (
    input sel,
    input [7:0] a,
    input [7:0] b,
    output [7:0] out
);

例化名称

若某一给定模块需要进行多次例化,那么在同一模块中,实例化名称(本例中为mux2_1、mux2_2)可任意指定,但不能相同,同时也不能使用verilog中的关键字,也不能和我们已经定义过的信号相同。

题目中给出sel为两位,因此是00,01,10,11。因此我们可以采用低位的两次01、01变化来分别在信号ab和信号cd中选择,从而得到最终的两个信号,再用高位的01选择出最终的out。

module top_module (
    input [1:0] sel,
    input [7:0] a,
    input [7:0] b,
    input [7:0] c,
    input [7:0] d,
    output [7:0] out
); 

    wire [7:0] mux0, mux1;
    mux2 mux2_1 ( sel[0],    a,    b, mux0 );
    mux2 mux2_2 ( sel[0],    c,    d, mux1 );
    mux2 mux2_3 ( sel[1], mux0, mux1,  out );

endmodule

add/sub 加法减法器

逻辑取反和按位取反

“!”表示逻辑求反,“~”表示按位求反。
本题是将两个八位的数,进行加减法运算,可以看到result_is_zero信号是一位的,因此肯定是要进行逻辑操作,得到一个一位数0或者1.
由于判断结果是不是0,因此对out进行判断即可。

module top_module ( 
    input do_sub,
    input [7:0] a,
    input [7:0] b,
    output reg [7:0] out,
    output reg result_is_zero
);

    always @(*) begin
        case (do_sub)
          0: out = a+b;
          1: out = a-b;
        endcase
        
        if (!out) //指out等于0的情况
            result_is_zero = 1; //因此将该信号置高
        else
            result_is_zero = 0;
    end

endmodule

Case statement(case语句)

这个组合电路应该可以识别8位键盘扫描码的0到9按键。它应该指示10个分支中是否有一个被识别(有效),如果是,那么被识别到的那个按键被检测到。
若题目中没有明显的错误,因此我们先进行仿真,从波形中找bug。
可看出:
参考波形中有vaild的高低变化,而我们的代码波形却一直没有变化,说明我们没有将vaild写到case语句的分支中去。

在这里插入图片描述

根据上面标出的我处错误,其中code26以及46是我们代码中有的,因此进行检查,发现8’h26写成了8’d26,另外46处是位数出错,不是6位,是8位。——绿色部分已经修改
至此再次仿真还存在如下的错误:
在这里插入图片描述
对于红色错误的部分它们都有一个特性:
out该输出下一个结果的时候,我们写的代码的输出却还是上一个的输出,因此说明生成了锁存器,说明case语句没有写全。的确,在default处没有给出输出out的值。
至此不在继续仿真,而是把valid值的情况在case语句中补上,然后进行最终的仿真。
在这里插入图片描述

module top_module (
    input [7:0] code,
    output reg [3:0] out,
    output reg valid=1 
);//

     always @(*)
        case (code)
            8'h45: begin
                out   = 0;
                valid = 1;
            end
            8'h16: begin
                out   = 1;
                valid = 1;
            end
            8'h1e: begin
                out   = 2;
                valid = 1;
            end
            8'h26: begin
                out   = 3;
                valid = 1;
            end
            8'h25: begin
                out   = 4;
                valid = 1;
            end
            8'h2e: begin
                out   = 5;
                valid = 1;
            end
            8'h36: begin
                out   = 6;
                valid = 1;
            end
            8'h3d: begin
                out   = 7;
                valid = 1;
            end
            8'h3e:begin
                out   = 8;
                valid = 1;
            end
            8'h46: begin
                out   = 9;
                valid = 1;
            end
            default: begin
                out   = 0;
                valid = 0;
            end
        endcase

endmodule

二、Build a circuit from a simulation waveform

circuit1组合电路1

根据如下波形确定其功能,并进行代码的编写。
在这里插入图片描述
可看出该当ab都是高电平的时候,输出q为高电平,因此是一个与门电路。

module top_module (
    input a,
    input b,
    output q 
);

    assign q = a & b; 

endmodule

circuit2组合电路2

在这里插入图片描述
这个题目时序比较多,不如上一题我们直接就可以看出描述的电路,这个题目我们找出来输出q为高电平的地方,同时写出卡诺图。可以参考卡诺图的画法
在这里插入图片描述
通过卡通诺我们可以分成两种情况,q = 红色圈q1 + 黑色圈q2。

同或和异或

同或和异或互为非运算。同或也称为异或非。

红色圈如下,我们可以看出 q1 = (a同或b) 与 (c同或d )

a b c d
0 0 0 0
0 0 1 1
1 1 0 0
1 1 1 1

黑色圈:q2 = ( a异或b ) 与上 ( c异或d )

a b c d
0 1 0 1
0 1 1 0
1 0 0 1
1 0 1 0

因此 q = q1 + q2 = ( a异或b ) 同或( c异或d ) =~ a^ b^ c^d

module top_module (
    input a,
    input b,
    input c,
    input d,
    output q
);
    assign q = ~a^b^c^d;
endmodule

circuit3组合电路3

在这里插入图片描述
仍然找q高电平,然后画出卡诺图。
在这里插入图片描述

module top_module (
    input a,
    input b,
    input c,
    input d,
    output q );//

    assign q = b&d | b&c | a&d | a&c;

endmodule

circuit4 组合逻辑4

在这里插入图片描述
同上
在这里插入图片描述
看到一种更简便的方式
在这里插入图片描述

可以化简成q = b | c;

module top_module (
    input a,
    input b,
    input c,
    input d,
    output q );//

    assign q = (~a&b) |(a&b)|(c&d)|(c&~d);

endmodule

circuit5组合逻辑5

在这里插入图片描述
首先我们可看出,这是一个四选一多路器,同时根据c的值进行选择。
因此我们就可以看当c的取值为0,1,2,3……的时候,对应的q等于多少,同时q对应的输出值对应输入中abcd的哪个值。
最终可看出:
c = 0 ,q 输出 b的值。
c = 1 ,q 输出 e的值。
c = 2 ,q 输出 a的值。
c = 3 ,q 输出 d的值。
其他情况,q输出f。

module top_module (
    input [3:0] a,
    input [3:0] b,
    input [3:0] c,
    input [3:0] d,
    input [3:0] e,
    output [3:0] q 
);
    
    always @(*)
        case(c)
            4'd0: q = b;
            4'd1: q = e;
            4'd2: q = a;
            4'd3: q = d;
            default: q = 4'hf;
        endcase

endmodule

circuit6组合逻辑电路6

在这里插入图片描述
通过波形可看出该题是根据a的取值来确定q的取值,采用case语句实现即可。

关于case中的default

在使用case语句的时候,不管有没有将所有的情况写全,都要写default,不然容易生成锁存器。
但是该波形中,无法没有给出default的情况,因此我们可以采用如下的写法:

default: q = 16’h1232;或者
default: ;

module top_module (
    input [2:0] a,
    output [15:0] q
); 
    always @(*)
        case(a)
            3'd0: q = 16'h1232;
            3'd1: q = 16'haee0;
            3'd2: q = 16'h27d4;
            3'd3: q = 16'h5a0e;
            3'd4: q = 16'h2066;
            3'd5: q = 16'h64ce;
            3'd6: q = 16'hc526;
            3'd7: q = 16'h2f19;
            default:q = 16'h1232 ;   
        endcase

endmodule

circuit7时序逻辑电路7

在这里插入图片描述

可看出a的上升沿与clk对齐的。同时在q 在 a变成高电平的下一时钟上升沿变成低电平。
题目两个点:

时序逻辑
取反操作

逻辑非和按位非

取反也就是进行非操作,这里同样涉及到按位非和逻辑非,但是由于输入输出是一位的,所以采用按位非和逻辑非都可。
二者的符号:

按位非:~,对操作数的每一位取反。
逻辑非:!,输出结果要么为真要么为假。(1或0)

module top_module (
    input clk,
    input a,
    output q 
);
    always @ (posedge clk)
        q <= ~a;

endmodule

circuit8时序逻辑电路8

在这里插入图片描述
根据波形,我们先看pq输出为高电平的地方。

电平触发和边沿触发

简言之: 电平触发是在高或低电平保持的时间内触发,而边沿触发是信号由高到低或由低到高这一瞬间触发。
电平触发和边沿触发的区别:边沿触发是触发器,触发器会读取并锁存输入信号。输出信号也仅在时钟信号上升(或下降)的一瞬间发生变化。锁存器是电平触发,也相当于使能信号,只要该信号处于高电平(或低电平),输出就会随着输入信号变化,直到使能信号变为低电平(或高电平)时,输出才会锁存,不再随输入变化。(比如使能信号高电平有效,因此高电平输出随着输入变化,低电平的时候保持。)

关于p:
可看出p输出信号是电平触发。当clk处于高电平的时候,p随着a变化,而低电平的时候输出保持。
关于q:
和p不同,q输出信号是边沿触发,只有时钟下降沿的一瞬间输出随a变化,否则保持。

module top_module (
    input clock,
    input a,
    output p,
    output q
);
    always @ (*)begin
        if(clock == 1'b1)//高电平
            p = a;//p随a变化
        else
            p = p;//保持       
    end
    
    always@(negedge clock)begin
        q <= a; //边沿触发,触发器存放a的值。
    end
endmodule

或者在边沿触发的时候,q也可以用来存放p的值。

module top_module (
    input clock,
    input a,
    output p,
    output q
);
    always @ (*)begin
        if(clock == 1'b1)//高电平
            p = a;//p随a变化
        else
            p = p;//保持       
    end
    
    always@(negedge clock)begin
        q <= a;
    end
endmodule

circuit9 时序逻辑电路(关于使能信号)

在这里插入图片描述
根据波形可看出q是边沿触发,当clk上升沿来到,q的值变化,同时当a为使能信号。高电平无效置为4。低电平使能有效进行计数。
同时q是从0-6进行计数,计数满清零。

module top_module (
    input clk,
    input a,
    output [3:0] q
);
    always@(posedge clk)
        if(a)
            q <= 4'd4;
        else if(q == 4'd6)
            q <= 1'd0;
        else
         q <=  q + 1'd1;

endmodule

circuit10 时序逻辑电路10

在这里插入图片描述
题目中说 q输出可根据输出state观察。
首先对所有q高电平的情况进行分析,可看到当a,b,state三个信号,有奇数个1的时候,q才是高电平,因此相当于奇偶校验。只要是奇数个1,那么结果为1,所以可以采用异或 q = a^b ^ state。
观察state所有高电平的位置。可看出当a == b的时候,state发生变化,且状态与ab的相同,但是 ab不相等的时候,state保持之前的值不变。

module top_module (
    input clk,
    input a,
    input b,
    output q,
    output state  );
 
    assign q = a ^ b ^ state;
    
    always @(posedge clk)begin
        if(a == b)
        	state <= b;
        else
            state <= state;
    end
endmodule

猜你喜欢

转载自blog.csdn.net/H19981118/article/details/124710294