【HDLBit】— 刷题


一、Getting Started

1.1 Step one

module top_module( output one );

    assign one = 1;

endmodule

1.2 Zero

module top_module(zero);
    output zero;
    assign zero = 1'b0;

endmodule

二、Verilog language

2.1 Basics

2.1.1 wire

module top_module( input in, output out );
    assign out = in;

endmodule

2.1.2 Wire4

module top_module( 
    input a,b,c,
    output w,x,y,z );
    assign w = a;
    assign x = b;
    assign y = b;
    assign z = c;
endmodule

2.1.3 Notgate

module top_module( input in, output out );
    assign out = ~in;

endmodule

2.1.4 Andgate

module top_module( 
    input a, 
    input b, 
    output out );
    assign out = a&b;

endmodule

2.1.5 Norgate

module top_module( 
    input a, 
    input b, 
    output out );
    assign out = ~(a|b);

endmodule

2.1.6 Xnorgate

module top_module( 
    input a, 
    input b, 
    output out );
    assign out = a?(b?1:0):(b?0:1);
endmodule

2.1.7 Wire decl

module top_module(
    input a,
    input b,
    input c,
    input d,
    output out,
    output out_n   ); 
    
    wire w1,w2,w3;
    
    assign out_n = ~w1;
    assign out = w1;
    assign w1 = w2|w3;
    assign w2 = a&b;
    assign w3 = c&d;
 
endmodule

2.1.8 7458

module top_module ( 
    input p1a, p1b, p1c, p1d, p1e, p1f,
    output p1y,
    input p2a, p2b, p2c, p2d,
    output p2y );
    
    assign p1y = (p1a & p1b & p1c)|(p1d & p1e & p1f);
    assign p2y = (p2a & p2b)|(p2c & p2d);

endmodule

2.2 vectors

2.2.1 vector0

module top_module ( 
    input wire [2:0] vec,
    output wire [2:0] outv,
    output wire o2,
    output wire o1,
    output wire o0  ); // Module body starts after module declaration
    
    assign outv = vec;
    assign o0 = vec[0];
    assign o1 = vec[1];
    assign o2 = vec[2];

endmodule

2.2.2 vector1

module top_module( 
    input wire [15:0] in,
    output wire [7:0] out_hi,
    output wire [7:0] out_lo );
    
    assign out_lo[7:0] = in[7:0];
    assign out_hi[7:0] = in[15:8];
    
endmodule

2.2.3 Vector2

module top_module( 
    input [31:0] in,
    output [31:0] out );//

    // assign out[31:24] = ...;
    assign out[7:0] = in[31:24];
    assign out[15:8] = in[23:16];
    assign out[23:16] = in[15:8];
    assign out[31:24] = in[7:0];

endmodule

2.2.4 Bitwise vs. Logical Operators

module top_module(
    input [2:0] a, 
    input [2:0] b, 
    output [2:0] out_or_bitwise,
    output out_or_logical,
    output [5:0] out_not
);
    // 按位或
    assign out_or_bitwise = a | b;
    // 逻辑或
    assign out_or_logical = a || b;
    // 取反
    assign out_not[2:0] = ~a;    
    assign out_not[5:3] = ~b;    
    
endmodule

2.2.4 four-input gates:AND, OR, and XOR operators

module top_module( 
    input [3:0] in,
    output out_and,
    output out_or,
    output out_xor
);
// 写法一: assign out_and
= in[0]&&in[1]&&in[2]&&in[3]; assign out_or = in[0]||in[1]||in[2]||in[3]; assign out_xor = in[0]^in[1]^in[2]^in[3];
// 写法二:
   assign out_and = ∈
   assign out_or = |in;
   assign out_xor = ^in;
endmodule

2.2.6 反转8bit输入

因为Verilog中向量顺序,先高位再低位的顺序[7:0]不能改变,因此不能用out[7:0]=in[0:7]来实现反转操作

module top_module( 
    input [7:0] in,
    output [7:0] out
);
    //assign {out[0],out[1],out[2],out[3],out[4],out[5],out[6],out[7]} = in;
    assign out = {in[0],in[1],in[2],in[3],in[4],in[5],in[6],in[7]};
endmodule

2.3 模块

2.3.5 移位寄存器+4选一

注意:case语句必须在always块中使用

module top_module ( 
    input clk, 
    input [7:0] d, 
    input [1:0] sel, 
    output [7:0] q 
);
    wire [7:0]w1,w2,w3;
    my_dff8 a1(.clk(clk),.d(d),.q(w1));
    my_dff8 a2(.clk(clk),.d(w1),.q(w2));
    my_dff8 a3(.clk(clk),.d(w2),.q(w3));
    always@(*)begin
    case(sel)
        2'b00:q = d;
        2'b01:q = w1;
        2'b10:q = w2;
        2'b11:q = w3;
    endcase
        end
endmodule

2.3.6 顶层模块和子模块之间连接

子模块的输入输出都是wire,只需要将其和顶层模块的输入输出使用assign相连即可

module top_module(
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
);
    wire [15:0]a1,a2,b1,b2,s1,s2;
    wire cin,c;
    assign cin = 0;
    assign a1 = a[15:0];
    assign a2 = a[31:16];
    assign b1 = b[15:0];
    assign b2 = b[31:16];   
    add16 add16_inst01
    (
        .a(a1),
        .b(b1),
        .cin(cin),
        .cout(c),
        .sum(s1)
    );
    add16 add16_inst02
    (
        .a(a2),
        .b(b2),
        .cin(c),
        .sum(s2)
    );   
    assign sum = {s2,s1};
endmodule

2.3.7全加器

add16可以直接用,add1全加器需要自己写

module top_module (
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
);

    wire cout1;
add16 add1_inst01
    (
        .a(a[15:0]),//将a的指定位宽和端口相连,就不用在外面单独声明变量
        .b(b[15:0]),
        .cin(1'd0),
        .cout(cout1),
        .sum(sum[15:0]) 
    );
add16 add1_inst02
    (
        .a(a[31:16]),
        .b(b[31:16]),
        .cin(cout1),
        .cout(),
        .sum(sum[31:16]) 
    );    
endmodule

module add1 ( input a, input b, input cin,   output sum, output cout );

    // Full adder module here
    //输入信号:a+b+cin 相加产生的进位信号会自动给cout 输出信号:cout、sum,
    assign {cout,sum} = a+b+cin; 

endmodule

2.3.7 带有选择的加法器(用资源换速度)

3个模块同时计算,等第一个模块得到cout后,直接去选下面两个模块的输出

module top_module(
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
);
    wire wei;
    wire [15:0]sum1,sum2,sum3,sum4;
    
add16 add1_inst01
    (
        .a(a[15:0]),
        .b(b[15:0]),
        .cin(1'd0),
        .cout(wei),
        .sum(sum1) 
    );
add16 add1_inst02
    (
        .a(a[31:16]),
        .b(b[31:16]),
        .cin(1'd0),
        .cout(),
        .sum(sum2) 
    );
add16 add1_inst03
    (
        .a(a[31:16]),
        .b(b[31:16]),
        .cin(1'd1),
        .cout(),
        .sum(sum3) 
    );
    always@(*)begin
        case(wei)
            1'd0: sum4 = sum2;
            1'd1: sum4 = sum3;
        endcase    
    end
    assign sum = {sum4,sum1};
endmodule

2.3.8 加法器和减法器

减一个数,相当于加上这个数的补码,以此来实现减法功能

a-b=a+((~b)+1)

(~b)+1:原码按位取反得到反码,反码加1后得到补码

----------------------------------------------------------------------------------

加法器:(a + b + 0)

减法器 :(a + ~b + 1)

当输入端口sub为1时,add16就是减法器;当输入端口sub为0时,add16就是加法器

module top_module(
    input [31:0] a,
    input [31:0] b,
    input sub,
    output [31:0] sum
);
    wire cout1;
    wire [31:0]bb;
        
    assign bb = b^{32{sub}}; //按位异或,于是需要复制32个sub   
        
    //end
add16 add1_inst01
    (
        .a(a[15:0]),
        .b(bb[15:0]),
        .cin(sub),
        .cout(cout1),
        .sum(sum[15:0]) 
    );
    
add16 add1_inst02
    (
        .a(a[31:16]),
        .b(bb[31:16]),
        .cin(cout1),
        .cout(),
        .sum(sum[31:16]) 
    ); 
endmodule

2.3 过程块

 过程块,是一个过程,中间有些东西需要被记忆,所以会用到寄存器

2.4.1 组合always块

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

2.4.2 时序always块

使用非阻塞赋值即可

2.4.3 使用if实现2-1复用器

// 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   ); 
// always
    always @(*)begin
        if(sel_b1 & sel_b2)// 为啥不能用sel_b1 == sel_b2
          out_always = b;
        else
           out_always = a;
    end
 //assign
    assign out_assign = (sel_b1 & sel_b2)?b:a;
endmodule

2.4.4、5 if语句、case语句产生的锁存器

在if语句和case语句中,需要将语句写完整,else和endcase要写出来,这样能避免出现锁存器

2.4.6 四输入的优先编码器

// synthesis verilog_input_version verilog_2001
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'b1110:pos = 2'b01;
            default:pos = 2'b00;
        endcase   
    end

endmodule

2.4.7 八输入的优先编码器(casez)

不关心z位置上的值是多少

// synthesis verilog_input_version verilog_2001
module top_module (
    input [7:0] in,
    output reg [2:0] pos  );
    always@(*)begin
        casez(in)
            8'bzzzzzzz1: pos = 3'b000;
            8'bzzzzzz1z: pos = 3'b001;
            8'bzzzzz1zz: pos = 3'b010;
            8'bzzzz1zzz: pos = 3'b011;
            8'bzzz1zzzz: pos = 3'b100;
            8'bzz1zzzzz: pos = 3'b101;
            8'bz1zzzzzz: pos = 3'b110;
            8'b1zzzzzzz: pos = 3'b111;
            default:pos = 3'b000;
        endcase
    end

endmodule

2.4.8 键盘输入

在case语句使用前,先对输出赋值,当满足case语句条件,再改变输出的值

对于出现多输出,需要在每种情况下,对每个输出进行赋值,不然会出现锁存器

// synthesis verilog_input_version verilog_2001
module top_module (
    input [15:0] scancode,
    output reg left,
    output reg down,
    output reg right,
    output reg up  ); 
    always@(*)begin
        left = 1'b0;down = 1'b0; right = 1'b0; up = 1'b0;
        case(scancode)
            16'he06b:left = 1'b1;
            16'he072:down = 1'b1;
            16'he074:right = 1'b1;
            16'he075:up = 1'b1;
        endcase
    end
endmodule

2.5 关于其他

 2.5.1 条件判断 (a
module top_module (
    input [7:0] a, b, c, d,
    output [7:0] min);//

    // assign intermediate_result1 = compare? true: false;
    wire [7:0]w1,w2;
    assign w1 = (aa:b;
    assign w2 = (cc:d;
    assign min = (w1w1:w2;
    
endmodule

 2.5.2 缩位运算(奇偶校验)

8bit数据位+1bit奇偶校验位

module top_module (
    input [7:0] in,
    output parity); 
    assign parity = ^in;
endmodule

为什么奇偶校验不常用?
数据通信中的数据只能是1或者0,如果一个数据中的1位发生错误,那1的个数肯定就从奇数变为偶数(或者偶数变为奇数)。
但是如果数据中有多个数据位都发生错误了呢。举例来说,我们要发送11110000.如果有两位发生了错误,比如变成了11000000,那么1的个数还是偶数,那奇偶校验就检测不出来错误了。
所以说:如果数据中发生多位数据错误就可能检测不出来,更检测不到错误发生在哪一位

module top_module( 
    input [99:0] in,
    output out_and,
    output out_or,
    output out_xor 
);
    assign out_and = ∈
    assign out_or = |in;
    assign out_xor = ^in;
endmodule

 2.5.4 for循环实现数据翻转

这里的out[i]代表数据的第几位
module top_module( 
    input [99:0] in,
    output [99:0] out
);
integer i;
    always@(*)begin
        for(i=0;i<100;++i)begin
            out[i] = in[99-i];            
        end        
    end
endmodule

 2.5.5 求出255个输入数据中1的总数

module top_module( 
    input [254:0] in,
    output [7:0] out );
    integer i;
    always@(*)begin
    out = 0;
    for(i=0;i<$bits(in);++i)
        //写法一
        if(in[i])
            out = out+8'd1;
        //写法二
        out = out+in[i];
    end
endmodule

2.5.6 使用generate重复例化实现100位全加器

module top_module( 
    input [99:0] a, b,
    input cin,
    output [99:0] cout,
    output [99:0] sum );
    generate 
        genvar i;
        for(i=0;i<$bits(a);++i)begin:adder
            if(i == 0)begin
                fullder fullder_inst01(
                    .a(a[i]),
                    .b(b[i]),
                    .cin(cin),
                    .cout(cout[i]),
                    .sum(sum[i])                
                );
            end
            else begin
                fullder fullder_inst02(
                    .a(a[i]),
                    .b(b[i]),
                    .cin(cout[i-1]),
                    .cout(cout[i]),
                    .sum(sum[i])                
                );                
            end
        end 
    endgenerate
endmodule
module  fullder
    (
    input a,b,cin,
    output cout,sum
    );
    assign {cout,sum} = a+b+cin;            
endmodule

2.5.7 使用generate重复例化实现100个BCD加法器

BCD:用4位2进制表示1个十进制数

module top_module( 
    input [399:0] a, b,
    input cin,
    output cout,
    output [399:0] sum );
    wire [100:0]mid;
    assign mid[0] = cin;
    
    generate 
        genvar i;
        for(i=0;i<100;++i)begin:adder
            bcd_fadd bcd_fadd_inst(
                .a(a[4*i+3:4*i]),
                .b(b[4*i+3:4*i]),
                .cin(mid[i]),
                .cout(mid[i+1]),
                .sum(sum[4*i+3:4*i])
            );
        end           
    endgenerate 
    assign cout = mid[100];

endmodule

 三、电路

3.1 基本门电路

3.1.3 或非门

这里取非,为什么用~或者!都可以啊?

~:按位取反——即在数值的二进制表示方式上,将0变为1,将1变为0

!:逻辑取反——即把所有非0的数值(相当于1)变为0,0变为1;

一般在设计门级电路时,常用逻辑取反

module top_module (
    input in1,
    input in2,
    output out);
    assign out = ~(in1|in2);
    //assign out = !(in1|in2);

endmodule

3.1.5 两个门级电路

分析:从输出端往前推,巧用括号

module top_module (
    input in1,
    input in2,
    input in3,
    output out);
    assign out = (!((in1)^(in2)))^(in3);

endmodule

3.1.8 真值表

module top_module( 
    input x3,
    input x2,
    input x1,  // three inputs
    output f   // one output
);
    assign f = ((!x1)&x2&(!x3))|(x1&x2&(!x3))|(x1&(!x2)&x3)|(x1&x2&x3);
endmodule

3.1.11 同或门

module top_module ( input x, input y, output z );
    assign z = (x&y)|((!x)&(!y));
    //assign z = !(x^y); 同或是异或取反 
endmodule

3.1.12 多个小模块的例化

优点:只需要关注小模块的输出,在top顶层模块中进行例化

module top_module (input x, input y, output z);
    wire w1,w2,w3,w4;
    A A_inst01(
        .x(x),
        .y(y),
        .z(w1)
    );
    A A_inst02(
        .x(x),
        .y(y),
        .z(w2)
    );    
    B B_inst01(
        .x(x),
        .y(y),
        .z(w3)
    );
    B B_inst02(
        .x(x),
        .y(y),
        .z(w4)
    );
    assign z = ((w3)&(w4))^((w1)|(w2));
    
endmodule

module A (input x, input y, output z);
    assign z = (x^y)&x;
endmodule
module B (input x, input y, output z);
    assign z = !(x^y);
endmodule

3.1.13 手机来电

门级描述

module top_module(
    input ring, 
    input vibrate_mode,
    output ringer,
    output motor
);
    
    // When should ringer be on? When (phone is ringing) and (phone is not in vibrate mode)
    assign ringer = ring & ~vibrate_mode;
    
    // When should motor be on? When (phone is ringing) and (phone is in vibrate mode)
    assign motor = ring & vibrate_mode;
    
endmodule