【hdlbits】个人学习交流分享(带答案)——verilog language部分

hdlbits是用verilog HDL进行小型电路硬件设计的一个练习网站,该网站提供综合和仿真,作为一个verilog HDL的初学者,我将在此记录我的学习状况和刷hdlbits的答案和感想和大家交流。个人感想通过代码中的注释或者对应题目代码段上下的文字呈现。hdlbits网站:HDLBits (01xz.net)

在此之前,先贴一个verilog HDL的教程:1.1 Verilog 教程 | 菜鸟教程 (runoob.com)

鉴于hdlbits题目太多,我计划分成多篇博客来编写,分别是verilog language部分、combinational logic部分、sequential logic部分、FSM部分以及其他部分,这一篇内容是verilog language部分。

其他hdlbits博客:

【hdlbits】个人学习交流分享(带答案)——combinational logic部分-CSDN博客

【hdlbits】个人学习交流分享(带答案)——sequential logic部分-CSDN博客

【hdlbits】个人学习交流分享(带答案)——finite state machines(FSM)-CSDN博客

【hdlbits】个人学习交流分享(带答案)——Reading Simulations和Writing Testbenches部分-CSDN博客

正文:

basics

simple wire

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

four wires

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

invertor

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

AND gate

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

NOR gate

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

XNOR gate

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

declaring wires

`default_nettype none
module top_module(
    input a,
    input b,
    input c,
    input d,
    output out,
    output out_n   ); 
    wire e,f;
    assign e=a&b;
    assign f=c&d;
    assign out=e|f;
    assign out_n=~(e|f);
endmodule

7458 chips

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

vectors

vectors

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 o2=vec[2];
    assign o1=vec[1];
    assign o0=vec[0];
endmodule

vectors in more detail

`default_nettype none     // Disable implicit nets. Reduces some types of bugs.
module top_module( 
    input wire [15:0] in,
    output wire [7:0] out_hi,
    output wire [7:0] out_lo );
    assign out_lo=in[7:0];
    assign out_hi=in[15:8];
endmodule

vectors part select

module top_module( 
    input [31:0] in,
    output [31:0] out );//
    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

bitwise 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=~{b,a};
endmodule

通过这个题目区分按位运算和逻辑运算的区别

具体的信息可以参考:https://wenku.csdn.net/answer/7f52c26fe1d9470db8f2090a0f44d3b1

four-input gates

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

vectors concatenation operator

module top_module (
    input [4:0] a, b, c, d, e, f,
    output [7:0] w, x, y, z );//
    assign {w,x,y,z}={a, b, c, d, e, f,2'b11};
    // assign { ... } = { ... };
endmodule

vector reversal 1

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

replication operator

module top_module (
    input [7:0] in,
    output [31:0] out );//
    assign out={{24{in[7]}},in};
    // assign out = { replicate-sign-bit , the-input };
endmodule

这里将8位数字扩展到了32位,8位中最高位是符号位,这种扩展是算术扩展。

区别于逻辑扩展直接在前面填0,算术扩展就是将符号位扩展,不改变真值。

逻辑扩展适合无符号数(unsigned)的扩展,算术扩展适合有符号数(signed)的扩展。

more replication

module top_module (
    input a, b, c, d, e,
    output [24:0] out );//
    wire [24:0]vec1,vec2;
    assign vec1={{5{a}},{5{b}},{5{c}},{5{d}},{5{e}}};
    assign vec2={5{a,b,c,d,e}};
    assign out=~(vec1^vec2);
    // assign out = ~{ ... } ^ { ... };
endmodule

Modules:hierarchy

modules

module top_module ( input a, input b, output out );
    mod_a(a,b,out);
endmodule

connecting ports by position

module top_module ( 
    input a, 
    input b, 
    input c,
    input d,
    output out1,
    output out2
);
    mod_a(out1,out2,a,b,c,d);
endmodule

connecting ports by name

module top_module ( 
    input a, 
    input b, 
    input c,
    input d,
    output out1,
    output out2
);
    mod_a(.out1(out1),.out2(out2),.in1(a),.in2(b),.in3(c),.in4(d));
endmodule

three modules

module top_module ( input clk, input d, output q );
    wire q1,q2;
    my_dff my_dff_1(clk,d,q1);
    my_dff my_dff_2(clk,q1,q2);
    my_dff my_dff_3(clk,q2,q);
endmodule

modules and vectors

module top_module ( 
    input clk, 
    input [7:0] d, 
    input [1:0] sel, 
    output [7:0] q 
);
    wire [7:0] mido,midt,midf;//注意D触发器输出是8位q,所以这里的wire都是8位,不能漏掉[7:0]
    my_dff8 my_dff8_1(clk,d,mido);
    my_dff8 my_dff8_2(clk,mido,midt);
    my_dff8 my_dff8_3(clk,midt,midf);
    
    always @(*) begin//多路选择器
        case(sel)
            2'b00:q=d;
            2'b01:q=mido;
            2'b10:q=midt;
            2'b11:q=midf;
       endcase
    end
endmodule

adder 1

module top_module(
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
);
    wire c;
    add16 add16_1(a[15:0],b[15:0],1'b0,sum[15:0],c);
    add16 add16_2(a[31:16],b[31:16],c,sum[31:16],1'b0);
endmodule

adder 2

module top_module (
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
);//
    wire c;
    add16 add16_1(a[15:0],b[15:0],1'b0,sum[15:0],c);
    add16 add16_2(a[31:16],b[31:16],c,sum[31:16],1'b0);
endmodule

module add1 ( input a, input b, input cin,   output sum, output cout );
    assign {cout,sum}=a+b+cin;
// Full adder module here
endmodule

carry -select adder 

module top_module(
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
);
    wire sel;
    wire [15:0]c,d;//c和d传输两个16bit的sum输出值,所以声明为16位
    add16 add16_1(a[15:0],b[15:0],1'b0,sum[15:0],sel);
    add16 add16_2(a[31:16],b[31:16],1'b0,c,1'b0);
    add16 add16_3(a[31:16],b[31:16],1'b1,d,1'b0);
    assign sum[31:16]=sel?d:c;
endmodule

adder-subtractor

module top_module(
    input [31:0] a,
    input [31:0] b,
    input sub,
    output [31:0] sum
);
    wire [31:0]d;//XOR门的输出信号
    wire c;
    assign d=b^{32{sub}};//sub是一位信号,XOR门输入b和输出d都是32位,进行按位异或需要sub扩展到32位
    add16 add16_1(a[15:0],d[15:0],sub,sum[15:0],c);
    add16 add16_2(a[31:16],d[31:16],c,sum[31:16],1'b0);
endmodule

procedures

always blocks(combinational)

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

always blocks(clocked)

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

if statement

// 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_b1&sel_b2)?b:a;
    always @(*)begin
        if(sel_b1&sel_b2) 
            out_always=b;
        else 
            out_always=a;
    end
endmodule

if statement latches

latch错误:一个变量声明为寄存器时,它既可以被综合成触发器,也可能被综合成 Latch,甚至是 wire 型变量。但是大多数情况下我们希望它被综合成触发器,但是有时候由于代码书写问题,它会被综合成不期望的 Latch 结构。

Latch 的主要危害有:

1.输入状态可能多次变化,容易产生毛刺,增加了下一级电路的不确定性;

2.在大部分 FPGA 的资源中,可能需要比触发器更多的资源去实现 Latch 结构;

3.锁存器的出现使得静态时序分析变得更加复杂。

Latch 多用于门控时钟(clock gating)的控制,一般设计时,我们想要避免 Latch 的产生。

常见产生latch的原因:

1.if-else不完整:组合逻辑中,不完整的 if - else 结构,出现了没有设定过的状态,会产生 latch。

2.case语句不完整:组合逻辑中,case 语句产生 Latch 的原理和 if 语句一致。当 case 选项列表不全且没有加 default ,或多个赋值语句不完整时,出现了没有设定过的状态,会产生 Latch

3.组合逻辑中,always@() 块内敏感列表没有列全,该触发的时候没有触发,会产生latch

对应的解决办法:

1.补全 if-else 结构,或者对信号赋默认初值。

2.将 case 选项列表补充完整,或对信号赋初值。

3.把敏感信号补全或者直接用 always@(*) 。

回到本题,题给的带有latch错误的代码如下

// 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)
           shut_off_computer = 1;
    end

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

endmodule

对应生成的电路是这样的(This is the circuit described by the code, not the circuit you want to build.)

修改后(补全了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)
           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

case statement

// 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'd0:out=data0;
            3'd1:out=data1;
            3'd2:out=data2;
            3'd3:out=data3;
            3'd4:out=data4;
            3'd5:out=data5;
            default:out=4'd0;
        endcase
    end
endmodule

priority encoder

这个题目学习的时候看到了下面的casez,所以学习了一下casez发现用casez更加简单干净,所以这里用casez实现

// synthesis verilog_input_version verilog_2001
module top_module (
    input [3:0] in,
    output reg [1:0] pos  );
always @(*) begin
    casez (in)
        4'bzzz1: pos = 2'b00;   // in[3:1] can be anything
        4'bzz1z: pos = 2'b01;
        4'bz1zz: pos = 2'b10;
        4'b1zzz: pos = 2'b11;
        default: pos = 2'b00;
    endcase
end
endmodule

priority encoder with casez

// synthesis verilog_input_version verilog_2001
module top_module (
    input [7:0] in,
    output reg [2:0] pos );
    always @(*)begin
        casez(in)
             8'b zzzzzzz1:pos=3'b000;
             8'b zzzzzz1z:pos=3'b001;
             8'b zzzzz1zz:pos=3'b010;
             8'b zzzz1zzz:pos=3'b011;
             8'b zzz1zzzz:pos=3'b100;
             8'b zz1zzzzz:pos=3'b101;
             8'b z1zzzzzz:pos=3'b110;
             8'b 1zzzzzzz:pos=3'b111;
             default:pos=3'b000;
       endcase
    end
endmodule

avoiding latches

// 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//always块中内容是顺序执行,所以赋值语句放在前面
        up = 1'b0; down = 1'b0; left = 1'b0; right = 1'b0;//在if-else、case等语句前赋初值避免latch
        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

more verilog features

conditional ternary operator

module top_module (
    input [7:0] a, b, c, d,
    output [7:0] min);//
    wire [7:0]w1,w2;
//四个数比大小选最小,也就是四选一,本题介绍的三目运算符只能实现两个数比大小2选1,所以四个数分成两组分别选出小的,然后两个小的比较选出最小的,实现四选一选最小数。
    assign w1=(a<b)?a:b;
    assign w2=(c<d)?c:d;
    assign min=(w1<w2)?w1:w2;
endmodule

reduction operators

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

reduction:even wider gates

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

combinational for-loop:vector reversal 2

module top_module( 
    input [99:0] in,
    output [99:0] out
);
    always @(*)begin
        integer i;//for语句变量定义为整型变量integer
        for(i=0;i<100;i++)//for是循环语句,循环语句只能在 always 或 initial 块中使用,类比C语言的for循环语句理解就好
            out[i]=in[99-i];
    end
endmodule

combinational for-loop:255-bit population count

//对255位输入向量中“1”的数量进行计数
module top_module( 
    input [254:0] in,
    output [7:0] out );
    always @(*)begin//always块中内容是顺序执行,所以赋值语句放在前面
       integer i;
        out=8'b0;//要对out赋初值,因为后面out=out+in[i]是对out的迭代,第一次运行这个式子时候需要一个out的初值
        for(i=0;i<255;i++)
            out=out+in[i];//数据流编写法
    end
endmodule

generate for-loop:100-bit binary adder 2

题目要求通过例化100个一位全加器实现100位行波二进制加法器,例化多个相同的模块时,一个一个的手动例化会比较繁琐。用 generate 语句进行多个模块的重复例化,generate语句中定义的变量类型是genvar(为什么是这个我也不太懂)

//要求通过例化100个一位全加器实现100位行波加法器
module top_module( 
    input [99:0] a, b,
    input cin,
    output [99:0] cout,
    output [99:0] sum );
    generate//用generate语句进行100次一位全加器的例化
        genvar i;
        for(i=0;i<100;i++) begin:adder
            if(i==0)
                fulladder fulladder_0(a[i],b[i],cin,sum[i],cout[i]);
            else
                fulladder fulladder_i(a[i],b[i],cout[i-1],sum[i],cout[i]);
        end           
    endgenerate
endmodule

module fulladder(input a,b,cin,output sum,cout);//编写一位全加器
    assign {cout,sum}=a+b+cin;
endmodule

generate for-loop:100-digit BCD-adder

对比上一题不同的是,这里是100位十进制BCD加法器,可以先学习一下BCD编码,这里每4位二进制a/b/sum表示一个十进制位,[3:0]表示第0位,[7:4]表示第1位,[4*i+3:4*i]表示第i位,总共需要400位输入和400位输出。

要进行100次一位全加器的例化,同样通过generate实现。

module top_module( 
    input [399:0] a, b,
    input cin,
    output cout,
    output [399:0] sum );
    wire [100:0]c;//声明101位wire c连接所有一位全加器的cin和cout
    assign c[0]=cin;    
    generate
        genvar i;
        for(i=0;i<100;i++)begin:bcd_fadd_loop
            bcd_fadd bcd_fadd_f1(a[4*i+3:4*i],b[4*i+3:4*i],c[i],c[i+1],sum[4*i+3:4*i]);
        end
    endgenerate
    assign cout=c[100];
endmodule

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值