HDLBits刷题全记录(二)

文章目录

Procedures

在本节开始之前先将常用语句进行划分,分为可综合性和不可综合性两种,简单来说,可综合性即是可以构成硬件电路的语句,而不可综合性即是只能用来仿真的语句。

类别 语句 可综合性
过程语句 initial
always
语句块 begin-end
fork-join
赋值语句 assign
= , <=
条件语句 if - else
case, casez, casex
循环语句 forever
repeat
while
for
编译向导语句 'define
'include
'ifdef, 'else, 'endif

Always blocks(combinaitonal)

  • Problem Statement

    Build an AND gate using both an assign statement and a combinational always block.

  • Code Block

    // 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 @(*) out_alwaysblock = a && b;//与assign有同样效果,但是信号要是寄存器类型
    
    endmodule
    
  • Key Point

    always语句是一直在运行的,只要满足语句后面的敏感时间,always语句块就会被执行,其语法格式为:

    always@(<敏感事件列表>)

    ​ 语句块;

    其中敏感事件列表可以为:

    @ (*) //模块任意信号发生改变

    @ (a) //当信号a改变时

    @ (a or b) //当信号a或b改变时

    @(posedge clk) //clk信号上升沿触发

    @(posedge clk or negedge clk) //clk信号上升沿或clk信号下降沿触发

    在过程语句(initial和always)中,被赋值信号必须是“寄存器”类型,例如’reg’。

    在连续赋值语句(assign)中,被赋值信号必须是“连线型”数据类型,例如’wire’。

    在使用always语句时,必须将能触发always语句的敏感事件全部写入括号内。

Always blocks(clocked)

  • Problem Statement

    Build an XOR gate three ways, using an assign statement, a combinational always block, and a clocked always block. Note that the clocked always block produces a different circuit from the other two: There is a flip-flop so the output is delayed.

HDLBits刷题全记录(二)

问题示意图(转自HDLBits)
  • Code Block

    // 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 @(*) out_always_comb = a^b;
        always @(posedge clk) out_always_ff <= a^b;
    endmodule
    
  • Key Point

    过程语句赋值有:

    阻塞性过程赋值语句 “变量 = 表达式”;

    非阻塞性过程赋值语句 “变量 <= 表达式”。

    阻塞性赋值语句和非阻塞性赋值语句只有在语句块中能使用。

    阻塞性赋值语句特点是:

    ① 在串行语句块中,按照代码排序顺序执行,在并行语句块则同时执行,没有顺序之分。

    ② 执行语句的顺序的是,先计算等式右边,然后立刻将计算的值赋予左边。

    非阻塞性赋值语句特点是:

    ① 任何情况都是并行,没有先后顺序的关系

    ② 执行语句的顺序的是,先计算等式右边,等延迟时间结束后,才将计算值赋予左边变量。

    只有在“行为级建模 + 串行语句块 + 阻塞性赋值”的情况下,代码才会串行进行

If statement

  • Problem Statement

    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.

HDLBits刷题全记录(二)

问题示意图(转自HDLBits)
  • Code Block

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

    串行语句块采用关键字“begin”和“end”,其中代码如果用阻塞性赋值语句“=”,则为串行执行。

    if条件语句有三种表达式:

    if(条件表达式) 	语句块;
    
    if(条件表达式)	语句块;
    else		   语句块;
    
    if(条件表达式1)			语句块;
    else if	(条件表达式1)	语句块;
    ......
    else				   语句块;
    

    if语句有优先级的划分,从上至下执行,如果前面已经满足条件,那么就会直接跳出整个if语句块,并不会把所有else if顺序读完。

    if的语句块中推荐使用begin end语句块,那怕只有一行代码。

If statement latches

  • Problem Statement

    The following code contains incorrect behaviour that creates a latch. Fix the bugs so that you will shut off the computer only if it’s really overheated, and stop driving if you’ve arrived at your destination or you need to refuel.

    HDLBits刷题全记录(二)

问题示意图(转自HDLBits)
  • Code Block

    // 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
    
  • Key Point

    当我们在设计电路时,不能指望代码自动生成正确的电路,哪怕代码在编译的时候并没有报错,如下错误所示:

    If (cpu_overheated) then shut_off_computer = 1;
    If (~arrived) then keep_driving = ~gas_tank_empty;
    

    因为在verilog中,在不满足if条件的情况时,并没有写对应的else的情况,那么verilog会保持信号不变,那么就会产生错误的信号。

    所以在我们写if或者case语句块时,需要考虑到电路的所有情况。

Case statement

  • Problem Statement

    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.

  • Code Block

    // 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 : begin 
                    out = data0;
                end
                3'b001 : begin 
                    out = data1;
                end
                3'b010 : begin 
                    out = data2;
                end
                3'b011 : begin 
                    out = data3;
                end
                3'b100 : begin 
                    out = data4;
                end
                3'b101 : begin 
                    out = data5;
                end
                default :begin 
                    out = 3'b000;
                end
            endcase
        end
    
    endmodule
    
  • Key Point

    case是比if-else语句更为方便的多路分支选择控制语句,其语法表达式为:

    case(控制表达式)
        值A:语句块;
        值B:语句块;
        ...
        值N:语句块;
        default:语句块;
    endcase
    

    值A~值N的之间必须各不相同,但是语句块可以是一样的。

    case一旦判断到执行某值的语句后,case语句块就会跳出,不会全部遍历。

    只有当列出了敏感表达式的所有可能指,不然就要写default选项。

    各数值之间位宽必须相同

Priority encoder

  • Problem Statement

    Build a 4-bit priority encoder(优先编码器,输出向量中第一个’1’的位置). 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.

  • Code Block

    // synthesis verilog_input_version verilog_2001
    module top_module (
       input [3:0] in,
       output reg [1:0] pos  );
       
       always@(*) begin
           case(in)
               4'b0010, 4'b0110, 4'b1010, 4'b1110 : begin
                   pos = 2'd1;
               end
               4'b0100, 4'b1100: begin
                   pos = 2'd2;
               end
               4'b1000 : begin
                   pos = 2'd3;
               end
               default: begin
                   pos = 2'd0;
               end
           endcase
       end
    
    endmodule
    
  • Key Point

    case之间若有连续排列的值执行的是同一语句块,可以用逗号将数值直接隔开。

    case本质上就是一种数值的匹配,那么可以有另外一种思路,直接对向量各位置直接比较有没有’1’,如下所示:

    module top_module (
        input [3:0] in,
        output reg [1:0] pos  );
        
        always@(*) begin
            case(1)
                in[0] : begin
                    pos = 2'd0;
                end
                in[1] : begin
                    pos = 2'd1;
                end
                in[2] : begin
                    pos = 2'd2;
                end
                in[3] : begin
                    pos = 2'd3;
                end
                default : begin
                	pos = 2'd0;
                end
            endcase
        end
    
    endmodule
    

    注:不能如下面这么写。

    // synthesis verilog_input_version verilog_2001
    module top_module (
        input [3:0] in,
        output reg [1:0] pos  );
        
        always@(*) begin
            case(1)
                in[1] : begin
                    pos = 2'd1;//会错误地将4'b0011作为控制数值
                end
                in[2] : begin
                    pos = 2'd2;//会错误地将4'b0101作为控制数值
                end
                in[3] : begin
                    pos = 2'd3;//会错误地将4'b1001作为控制数值
                end
                default : begin
                	pos = 2'd0;
                end
            endcase
        end
    
    endmodule
    

Priority encoder with casez

  • Problem Statement

    Build a priority encoder for 8-bit inputs. Given an 8-bit vector, the output should report the first bit in the vector that is 1. Report zero if the input vector has no bits that are high.

  • Code Block

    // synthesis verilog_input_version verilog_2001
    module top_module (
        input [7:0] in,
        output reg [2:0] pos  );
        always@(*) begin
            casez (in) 
                8'bzzzzzzz1 : begin
                    pos = 3'd0;
                end
                8'bzzzzzz1z : begin
                    pos = 3'd1;
                end
                8'bzzzzz1zz : begin
                    pos = 3'd2;
                end
                8'bzzzz1zzz : begin
                    pos = 3'd3;
                end
                8'bzzz1zzzz : begin
                    pos = 3'd4;
                end
                8'bzz1zzzzz : begin
                    pos = 3'd5;
                end
                8'bz1zzzzzz : begin
                    pos = 3'd6;
                end
                8'b1zzzzzzz : begin
                    pos = 3'd7;
                end
                default : begin
                    pos = 3'd0;
                end
            endcase
        end
    endmodule
    
  • Key Point

    当你不关心向量中其他位置的数值关系的时候,就可以使用casez,并且不关心的数值位置替换为z即可。

    因为case是从上至下执行,一旦匹配就会退出,所以在写敏感表达式的数值时,要注意顺序,错误示范如下:

    // synthesis verilog_input_version verilog_2001
    module top_module (
        input [7:0] in,
        output reg [2:0] pos  );
        always@(*) begin
            casez (in) 
                8'bzzzzzz1z : begin
                    pos = 3'd1;//因为与8'bzzzzzzz1调换了位置,所以当出现8'bzzzzzz11时,将会匹配到这并直接退出,并不会匹配到下面。
                end
                8'bzzzzzzz1 : begin
                    pos = 3'd0;
                end
                8'bzzzzz1zz : begin
                    pos = 3'd2;
                end
                8'bzzzz1zzz : begin
                    pos = 3'd3;
                end
                8'bzzz1zzzz : begin
                    pos = 3'd4;
                end
                8'bzz1zzzzz : begin
                    pos = 3'd5;
                end
                8'bz1zzzzzz : begin
                    pos = 3'd6;
                end
                8'b1zzzzzzz : begin
                    pos = 3'd7;
                end
                default : begin
                    pos = 3'd0;
                end
            endcase
        end
    endmodule
    

Avoiding latches

  • Problems Statement

    Suppose you’re building a circuit to process scancodes from a PS/2 keyboard for a game. Given the last two bytes of scancodes received, you need to indicate whether one of the arrow keys on the keyboard have been pressed. This involves a fairly simple mapping, which can be implemented as a case statement (or if-elseif) with four cases.

    Your circuit has one 16-bit input, and four outputs. Build this circuit that recognizes these four scancodes and asserts the correct output.

    HDLBits刷题全记录(二)

    问题示意图(转自HDLBits)
  • Code Block

    // 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 : begin
                    left = 1;
                end
                16'he072 : begin
                    down = 1;
                end
                16'he074 : begin
                    right = 1;
                end
                16'he075 : begin
                    up = 1;
                end
                /*
                default : begin
                	left  = 0;
                    down  = 0;
                    right = 0;
                    up    = 0;
                end*/
            endcase
        end
    endmodule
    
  • Key Point

    当情况非常复杂时,仅有一个简单的default是不够的,我们必须在case item和default中为4个输出进行赋值,这会导致很多不必要的代码编写。

    所以我们可以对输出先进行赋初值的操作,这种能确保在所有可能的情况下输出都被赋值,除非case语句覆盖了赋值,所以不再需要写default项。

    整数的表达方式如下:

    数值 基数符号
    二进制 b或B
    八进制 o或O
    十进制 d或D
    十六进制 h或H

More Verilog Features

Conditional ternary operator

  • Problem Statement

    Given four unsigned numbers, find the minimum. Unsigned numbers can be compared with standard comparison operators (a < b). Use the conditional operator to make two-way min circuits, then compose a few of them to create a 4-way min circuit. You’ll probably want some wire vectors for the intermediate results.

  • Code Block

    module top_module (
        input [7:0] a, b, c, d,
        output [7:0] min);//
        // assign intermediate_result1 = compare? true: false;
        wire [7:0] temp_num1 , temp_num2;
        assign temp_num1 = (a>b ? b:a);
        assign temp_num2 = (c>d ? d:c);
        assign min       = (temp_num1 > temp_num2 ? temp_num2 : temp_num1);
    	
    endmodule
    

Reduction operators

  • Problem Statement

    Parity checking is often used as a simple method of detecting errors when transmitting data through an imperfect channel. Create a circuit that will compute a parity bit for a 8-bit byte (which will add a 9th bit to the byte). We will use “even” parity, where the parity bit is just the XOR of all 8 data bits.

  • Code Block

    module top_module (
        input [7:0] in,
        output parity); 
        assign parity = ^in;//奇偶校验
    endmodule
    
  • Key Point

    缩位运算符可以将向量各个位进行和,或和异或等运算,产生一位输出,如下所示:

    & a[3:0]     // 和运算: a[3]&a[2]&a[1]&a[0]。 相当于(a[3:0] == 4'hf)
    | b[3:0]     // 或运算:  b[3]|b[2]|b[1]|b[0]。 相当于(b[3:0] != 4'h0)
    ^ c[2:0]     // 异或运算: c[2]^c[1]^c[0]。 可以进行奇偶检验
    

Reduction : Even wider gates

  • Problem Statement

    Build a combinational circuit with 100 inputs, in[99:0].

    There are 3 outputs:

    • out_and: output of a 100-input AND gate.
    • out_or: output of a 100-input OR gate.
    • out_xor: output of a 100-input XOR gate.
  • Code Block

    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

  • Problem Statement

    Given a 100-bit input vector [99:0], reverse its bit ordering.

  • Code Block

    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
    
  • Key Point

    'for’条件循环的语法格式为:

    integer 整数变量
    for (循环变量初值; 循环结束条件; 循环变量增值)
    

Combinational for-loop: 255-bit population count

  • Problem Statement

    A “population count” circuit counts the number of '1’s in an input vector. Build a population count circuit for a 255-bit input vector.

  • Code Block

    module top_module( 
        input [254:0] in,
        output [7:0] out );
        
        integer i;
        always @(*) begin
            out = 8'd0;
            for(i=0; i<255; i++)begin 
                out += in[i];
            end
        end
    endmodule
    

Generate for-loop: 100-bit binary adder2

  • Problem Statement

    Create a 100-bit binary ripple-carry adder by instantiating 100 full adder. The adder adds two 100-bit numbers and a carry-in to produce a 100-bit sum and carry out. To encourage you to actually instantiate full adders, also output the carry-out from each full adder in the ripple-carry adder. cout[99] is the final carry-out from the last full adder, and is the carry-out you usually see.

  • Code Block

    module top_module( 
        input [99:0] a, b,
        input cin,
        output [99:0] cout,
        output [99:0] sum );
        genvar i;
        generate
            for(i=0; i<100; i++) begin : adder			//命名
                    if(i == 0)begin
                        assign {cout[0], sum[0]} = a[0] + b[0] + cin;
                	end
                	else begin
                		assign {cout[i], sum[i]} = a[i] + b[i] + cout[i-1];
                	end 
                end
        endgenerate
    endmodule
    
  • Key Point

    generate语句的最主要功能就是对module、reg、assign、always、task等语句或者模块进行复制。

    generate语句有generate for、generate if、generate case三种语句。

    generate语句块注意事项:

    generate不放在always块中,而是always块等包含在generate中;
    generate for中,要在begin后面给模块命名,这是一个强制规定,并且也是利用循环生成语句生成多个实例的时候分配名字所必须的;
    task不能放在generatefor中,要想实现同样的功能,用子模块;
    循环生成中for语句使用的变量必须用genvar关键字定义,genvar关键字可以写在generate语句外面,也可以写在generate语句里面,只要先于for语句声明即可,且genvar关键字定义的变量只有generate语句块能使用;
    for语句的内容必须加begin-end,即使只有一条语句也不能省略。这也是一个强制规定,而且给循环起名字也离不开begin关键字;
    

Generate for-loop: 100-digit BCD adder

  • Problem statement

    You are provided with a BCD one-digit adder named bcd_fadd that adds two BCD digits and carry-in, and produces a sum and carry-out.

    module bcd_fadd {
        input [3:0] a,
        input [3:0] b,
        input     cin,
        output   cout,
        output [3:0] sum );
    

    Instantiate 100 copies of bcd_fadd to create a 100-digit BCD ripple-carry adder. Your adder should add two 100-digit BCD numbers (packed into 400-bit vectors) and a carry-in to produce a 100-digit sum and carry out.

  • Code Block

    module top_module( 
        input [399:0] a, b,
        input cin,
        output cout,
        output [399:0] sum );
        wire [99:0] cout_temp;
    	genvar i;
        generate
            for(i=0;i<100;i++) begin:adder
                if(i == 0)	begin
                    bcd_fadd bcd_inst(a[3:0],b[3:0],cin,cout_temp[0],sum[3:0]);
                end
                else	begin
                    bcd_fadd bcd_inst(a[4*i+3:4*i],b[4*i+3:4*i],cout_temp[i-1],cout_temp[i],sum[4*i+3:4*i]);
                end
            end
            assign cout=cout_temp[99];
        endgenerate
    endmodule
    
上一篇:HDLbits刷题笔记—Lfsr32


下一篇:12、容器内部删除一个元素