Contents

1-7:HDLBits刷题记录(持续更新中)

前几天想要通过HDLBits刷题来熟练使用verilog语音,这几天想清楚一件事情,很多事情浅尝辄止都是只能停留在表面的很多东西也都不能深入的了解,这对个人发展来说是无益的,说到底还是应该熟练掌握一门技术,我选择了自己相对比较熟悉一点的verilog,并没有因为多熟悉,只是因为相对熟悉一些,再加上以后说不定能靠这个吃饭,所以在这里记录下自己刷题的记录。

此前已经刷了一小部分内容,突然想记录下来。

Getting Started

Step ONE

  • We’re going to start with a small bit of HDL to get familiar with the interface used by HDLBits. Here’s the description of the circuit you need to build for this exercise:

  • Build a circuit with no inputs and one output. That output should always drive 1 (or logic high).

  • 这个页面是一个简单的问题,让你构建一个没有输入、一个输出的电路,该输出输出一个常数0。

  • HDLBits使用Verilog-2001 ANSI风格的端口声明语法,因为它更易于阅读,减少了拼写错误。你也可以使用旧的Verilog-1995语法。例如,下面两个模块声明是可以接受的,且等效的:

module top_module( output one );

// Insert your code here
   assign one = 1;
   
endmodule

Output Zero

module top_module(
    output zero
);// Module body starts after semicolon

endmodule

Verilog Languages

Basics

simple wire

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

与物理连线不同,Verilog 中的连线(和其他信号)是有方向性的。这意味着信息仅沿一个方向流动,从(通常是一个)源到接收器(源也通常称为将值驱动到线路上的驱动程序)。在 Verilog“连续分配”( assign left_side = right_side; ) 中,右侧信号的值被驱动到左侧的线上。赋值是“连续的”,因为即使右侧的值发生变化,赋值也会一直持续。连续分配不是一次性事件。

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

有多个 assign 语句时,它们在代码中出现的顺序并不重要。与编程语言不同, assign 语句(“连续赋值”)描述事物之间的连接,而不是将值从一个事物复制到另一个事物的操作。 这里叫人感受到了verilog的设计哲学,wire表示连接而不是赋值,给人一种来自于物理世界的抽象,但又不像编程语言那样高度抽象的介于中间的感受。

inverter

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

Use an assign statement. The assign statement will continuously drive the inverse of in onto wire out. 这句话似乎暗示着,assign在verilog语言里有一种表示着驱动的力

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

assign 语句用一个值驱动一条线(或“网络”,更正式的称呼)。该值可以是您想要的复杂函数,只要它是组合(即无内存,没有隐藏状态)函数即可。 assign 语句是连续赋值,因为只要任何输入发生变化,输出就会永远“重新计算”,就像简单的逻辑门一样。

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 wire_a;
    wire wire_b;
    
    assign wire_a = a & b;
    assign wire_b = c & d;
    assign out = wire_a | wire_b;
    assign out_n = !(wire_a | wire_b);

endmodule

这是第一次声明wire,端口声明的不需要重复声明,内部必须在使用之前就声明(这里值得回顾 https://hdlbits.01xz.net/wiki/Wire_decl

7458 chip

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

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

wire [7:0] w; 声明一个名为 w 的 8 位向量,其功能相当于具有 8 条独立的连线。

vertors in more detail

隐含网线可能存在的问题:

wire [2:0] a, c; // Two vectors assign a = 3'b101; // a = 101 assign b = a; // b = 1 implicitly-created wire assign c = b; // c = 001 <-- bug my_module i1 (d,e); // d and e are implicitly one-bit wide if not declared.  
// This could be a bug if the port was intended to be a vector.

这段代码主要展示了 Verilog 中隐含网线可能导致的问题。首先,代码定义了两个三位向量 ac,然后给 a 赋值 3'b101。接下来,b 被赋值为 a,但是请注意 b 在之前并没有被声明,所以 b 将会被隐式地创建为一个一位的 wire,其值为 a 的最低位,即 1。然后 c 被赋值为 b,由于 b 是一位的 wire,所以 c 的值将会是 001,这就是一个 bug,因为我们原本可能希望 c 的值和 a 相同。

接下来,模块 my_module 的实例 i1 的端口 de 没有被声明,所以它们将会被隐式地创建为一位宽的 wire。如果我们原本希望 de 是向量的话,这就是一个 bug。

在代码的最后,作者提到,如果添加了 default_nettype none,那么 b = a 这一行就会导致一个错误,这样 bug 就更容易被发现了。因为 default_nettype none 会禁止 Verilog 隐式地创建网线,所以如果 b 没有被声明,代码就会报错。

即隐式创建的的网络其实只有一位,而不是向量的相同位数
`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_hi = in [15:8];
    assign out_lo = in [7:0];
endmodule

vector part select

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

    // assign out[31:24] = ...;
    assign out[31:24] = in[7:0];
    assign out[23:16] = in[15:8];
    assign out[15:8] = in[23:16];
    assign out[7:0] = in[31:24];
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[0] || a[1] || a[2]) || (b[0] || b[1] || b[2]);
	assign out_not = {~b, ~a};

endmodule

按位或和逻辑或的区别

four-input gates

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];
endmodule

vector concatenation operator

module top_module (
    input [4:0] a, b, c, d, e, f,
    output [7:0] w, x, y, z );//

    // assign { ... } = { ... };
    wire [31:0] concatenated = {a, b, c, d, e, f, 2'b11};
    assign w = concatenated[31:24];
    assign x = concatenated[23:16];
    assign y = concatenated[15:8];
    assign z = concatenated[7:0];
endmodule
//或者直接一行
// assign {w, x, y, z} = {a, b, c, d, e, f, 2'b11} ;

连接运算符 {a,b,c} 用于通过将向量的较小部分连接在一起来创建更大的向量。

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

这里卡了我很久,结果是自己粗心大意看的时候没注意,用到复制操作符的时候应该是 {24{in}} 要注意括号数

一开始我就只是24{in}, 这样怎么可行呢 执行 assign a = {b,b,b,b,b,b}; 之类的操作仍然很乏味。复制运算符允许重复向量并将它们连接在一起:

{num{vector}}

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

    // assign out = { replicate-sign-bit , the-input };
    //wrong way, my first try:assign out = {24{in[7]}, in};,其中24{in[7]}应该为一个整体
    assign out = {{24{in[7]}}, in};
endmodule

more replication

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

Modules:Hierarchy

modules

module top_module ( input a, input b, output out );
    mod_a module_a (.out(out), .in1(a), .in2(b));
endmodule

mod_a instance2 ( .out(wc), .in1(wa), .in2(wb) ); 调用的时候前面为模块,后面为实例

connecting ports by position

module top_module ( 
    input a, 
    input b, 
    input c,
    input d,
    output out1,
    output out2
);
    mod_a u_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 u_mod_a (.out1(out1), .out2(out2), .in1(a), .in2(b), .in3(c), .in4(d));
endmodule

这种有一定鲁棒性,便于维护

three modules

写一个模块,当三个的用 这里相当于d延迟3个周期之后出现在q信号

module top_module ( input clk, input d, output q );
    
    wire q1;
    wire q2;
    my_dff u1_my_dff(
        .clk(clk),
        .d(d),
        .q(q1)
    );
    my_dff u2_my_dff(
        .clk(clk),
        .d(q1),
        .q(q2)
    );
    my_dff u3_my_dff(
        .clk(clk),
        .d(q2),
        .q(q)
    );
endmodule

modules and vectors

这里相当于可以选择输出d第几个周期的移位信号

module top_module ( 
    input clk, 
    input [7:0] d, 
    input [1:0] sel, 
    output [7:0] q 
);
    wire [7:0] q1;
    wire [7:0] q2;
    wire [7:0] q3;
    my_dff8 u1_my_dff8(
        .clk(clk),
        .d(d),
        .q(q1)
    );
    my_dff8 u2_my_dff8(
        .clk(clk),
        .d(q1),
        .q(q2)
    );
    my_dff8 u3_my_dff8(
        .clk(clk),
        .d(q2),
        .q(q3)
    );
    always@(*)begin
        case(sel)
            2'b00:begin
                q = d;
            end
            2'b01:begin
                q = q1;
            end
            2'b10:begin
                q = q2;
            end
            2'b11:begin
                q = q3;
            end
		endcase
		end
endmodule

adder 1

这里通过位连接符连接两个加法器

module top_module(
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
);
    wire cout;
    add16 u1_add16(
        .a(a[15:0]),
        .b(b[15:0]),
        .cin(1'b0),
        .cout(cout),
        .sum(sum[15:0])
    );
    add16 u2_add16(
        .a(a[31:16]),
        .b(b[31:16]),
        .cin(cout),
        .cout(1'b0),
        .sum(sum[31:16])
    );
endmodule

adder 2

module top_module (
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
);
    wire cout;
    
    add16 u1_add16(
        .a	(a[15:0]),
        .b	(b[15:0]),
        .cin(1'b0),
        .sum(sum[15:0]),
        .cout(cout)
    );
    
    add16 u2_add16(
        .a	(a[31:16]),
        .b	(b[31:16]),
        .cin(cout),
        .sum(sum[31:16]),
        .cout(1'b0)
    );
endmodule

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

    // Full adder module here
    assign {cout, sum} = a + b + cin;
endmodule

carry-select adder

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

    wire[15:0]	sum_1;
    wire[15:0]	sum_2;
    add16 u1_add16(
        .a	(a[15:0]),
        .b	(b[15:0]),
        .cin(1'b0),
        .sum(sum[15:0]),
        .cout(cout)
    );
    
    add16 u2_add16(
        .a	(a[31:16]),
        .b	(b[31:16]),
        .cin(1'b0),
        .sum(sum_1),
        .cout(1'b0)
    );
    add16 u3_add16(
        .a	(a[31:16]),
        .b	(b[31:16]),
        .cin(1'b1),
        .sum(sum_2),
        .cout(1'b0)
    );
    assign sum = cout ? {sum_2,sum[15:0]} : {sum_1, sum[15:0]};
endmodule

提升加法器速度,(提前运算,结果选择)

adder-subtractor

module top_module(
    input [31:0] a,
    input [31:0] b,
    input sub,
    output [31:0] sum
);
    wire [31:0] b_inv;
    wire cout1, cout2;

    // Invert 'b' if 'sub' is high.
    assign b_inv = b ^ {32{sub}};
/*
1 0 = 1
1 1 = 0
0 1 = 1
0 1 = 1
*/
    // Instantiate the first 16-bit adder
    add16 adder1 (
        .a(a[15:0]),
        .b(b_inv[15:0]),
        .cin(sub),
        .sum(sum[15:0]),
        .cout(cout1)
    );

    // Instantiate the second 16-bit adder
    add16 adder2 (
        .a(a[31:16]),
        .b(b_inv[31:16]),
        .cin(cout1),
        .sum(sum[31:16]),
        .cout(cout2)
    );
endmodule

可完成加减法运算,最终的结果是一个可以执行两个操作的电路:(a + b + 0) 和 (a + ~b + 1)

sub输入决定我们是执行加法还是减法。当sub为0时,我们执行加法;当sub为1时,我们执行减法。

我们使用一个XOR门来根据sub的值选择性地反转b的所有位。XOR门的功能是,当且仅当两个输入不同时,输出为1。所以,当sub为0时,b的所有位都保持不变;当sub为1时,b的所有位都被反转。

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) begin
            out_always = b;
        end
        else begin
            out_always = a;
        end
    end
        
endmodule

if statement latches

// 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) begin
           shut_off_computer = 1;
    end
    else begin
        shut_off_computer = 0;
    end
    end
    
    always @(*) begin
        if (~arrived) begin
           keep_driving = ~gas_tank_empty;
    end
    else begin
	keep_driving = 0;
    end
    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'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

priority encoder

// 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'b1111: 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'bzzzzzzz1: pos = 3'b000;
            8'bzzzzzz10: pos = 3'b001;
            8'bzzzzz100: pos = 3'b010;
            8'bzzzz1000: pos = 3'b011;
            8'bzzz10000: pos = 3'b100;
            8'bzz100000: pos = 3'b101;
            8'bz1000000: pos = 3'b110;
            8'b10000000: pos = 3'b111;
            default: pos = 0;
        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
        up = 1'b0; down = 1'b0; left = 1'b0; right = 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

More Verilog Feature

conditional ternary operator

reduction operator

reduction:even wider gates

combinational for-loop:vector reversal 2

combinational for-loop:255-bit population count

generate for-loop:100-bit binary adder2

generate for-loop:100-digit BCD adder

Circuits

Flag Counter