Full record of HDLBits questions

Keywords: Verilog FPGA

Procedures

Before the beginning of this section, common statements are divided into comprehensible and non comprehensible. In short, comprehensible is the statement that can form a hardware circuit, while non comprehensible is the statement that can only be used for simulation.

categorysentenceIntegrability
Procedure statementinitial
always
Statement blockbegin-end
fork-join
Assignment statementassign
= , <=
Conditional statementif - else
case, casez, casex
Circular statementforever
repeat
while
for
Compile wizard statement'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;//It has the same effect as assign, but the signal is of register type
    
    endmodule
    
  • Key Point

    The always statement is always running. As long as the sensitive time after the statement is met, the always statement block will be executed. Its syntax format is:

    Always @ (< list of sensitive events >)

    Statement block;

    The list of sensitive events can be:

    @(*) / / any signal of the module changes

    @(a) / / when signal a changes

    @(a or b) / / when signal a or B changes

    @(posedge clk) / / triggered by the rising edge of clk signal

    @(posedge clk or negedge clk) / / triggered by the rising edge of clk signal or the falling edge of clk signal

    In procedure statements (initial and always), the assigned signal must be of type "register", such as' reg '.

    In a continuous assignment statement, the assigned signal must be a "wired" data type, such as' wire '.

    When using the always statement, all sensitive events that can trigger the always statement must be written in parentheses.

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.

Problem diagram (transferred from 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

    Procedure statement assignment includes:

    Blocking process assignment statement "variable = expression";

    Non blocking procedure assignment statement "variable < = expression".

    Blocking assignment statements and non blocking assignment statements can only be used in statement blocks.

    The blocking assignment statement is characterized by:

    ① In the serial statement block, it is executed according to the code sorting order, and in the parallel statement block, it is executed at the same time. There is no order.

    ② The order in which the statements are executed is to first calculate the right side of the equation, and then immediately assign the calculated value to the left.

    The non blocking assignment statement is characterized by:

    ① In any case, it is parallel and there is no sequential relationship

    ② The order of executing statements is to calculate the right side of the equation first, and then assign the calculated value to the left variable after the delay time is over.

    Only in the case of "behavior level modeling + serial statement block + blocking assignment", the code will be serialized

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.

Problem diagram (transferred from 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

    The serial statement block adopts the keywords "begin" and "end". If the code uses the blocking assignment statement "=", it is executed serially.

    if conditional statements have three expressions:

    if(Conditional expression) 	Statement block;
    
    if(Conditional expression)	Statement block;
    else		   Statement block;
    
    if(Conditional expression 1)			Statement block;
    else if	(Conditional expression 1)	Statement block;
    ......
    else				   Statement block;
    

    If statements are prioritized and executed from top to bottom. If the previous conditions have been met, the whole if statement block will be directly jumped out, and all else if statements will not be read in order.

    It is recommended to use the begin end statement block in the statement block of if, even if there is only one line of code.

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.

Problem diagram (transferred from 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

    When designing a circuit, we cannot expect the code to automatically generate the correct circuit, even if the code does not report an error when compiling, as shown in the following error:

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

    Because in verilog, if the if condition is not satisfied and the corresponding else is not written, verilog will keep the signal unchanged, and an error signal will be generated.

    Therefore, when we write if or case statement blocks, we need to consider all the situations of the circuit.

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 is a multi branch selection control statement more convenient than if else statement, and its syntax expression is:

    case(Control expression)
        value A: Statement block;
        value B: Statement block;
        ...
        value N: Statement block;
        default: Statement block;
    endcase
    

    The values A to N must be different, but the statement blocks can be the same.

    Once the case judges that the statement of a certain value is executed, the case statement block will jump out and will not be traversed completely.

    Only when all possible references of sensitive expressions are listed, otherwise the default option will be written.

    The bit width between values must be the same

Priority encoder

  • Problem Statement

    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

    If there are consecutive values between case s that execute the same sentence block, the values can be separated directly by commas.

    case is essentially a numerical matching, so there can be another idea to directly compare whether there is' 1 'at each position of the vector, as shown below:

    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
    

    Note: it cannot be written as follows.

    // 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 will be incorrectly used as the control value
                end
                in[2] : begin
                    pos = 2'd2;//4'b0101 will be incorrectly used as the control value
                end
                in[3] : begin
                    pos = 2'd3;//4'b1001 will be incorrectly used as the control value
                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

    When you don't care about the numerical relationship of other positions in the vector, you can use casez and replace the numerical positions you don't care with z.

    Because case is executed from top to bottom, it will exit once matched. Therefore, when writing the value of sensitive expression, pay attention to the order. The error example is as follows:

    // 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;//Because the position is changed with 8'bzzz1, when 8'bzzz11 appears, it will match here and exit directly, and will not match below.
                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.

    Problem diagram (transferred from 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

    When the situation is very complex, a simple default is not enough. We must assign values to four outputs in case item and default, which will lead to a lot of unnecessary coding.

    Therefore, we can assign an initial value to the output first, which can ensure that the output is assigned under all possible circumstances, unless the case statement overrides the assignment, so there is no need to write the default item.

    Integer is expressed as follows:

    numerical valuecardinality symbol
    BinaryB or B
    octal number systemO or o
    decimal systemD or D
    hexadecimalH or 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;//Parity check
    endmodule
    
  • Key Point

    The bit reduction operator can perform sum, or exclusive or and other operations on each bit of the vector to produce a bit output, as shown below:

    & a[3:0]     // Sum operation: a [3] & A [2] & A [1] & A [0]. Equivalent to (a[3:0] == 4'hf)
    | b[3:0]     // Or operation: b[3]|b[2]|b[1]|b[0]. Equivalent to (B [3:0]! = 4'h0)
    ^ c[2:0]     // XOR operation: c[2]^c[1]^c[0]. Parity check can be performed
    

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

    The syntax format of 'for' conditional loop is:

    integer Integer variable
    for (Initial value of cyclic variable; Cycle end condition; Cyclic variable increment)
    

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

    The main function of generate statement is to copy module, reg, assign, always, task and other statements or modules.

    Generate statements include generate for, generate if, and generate case statements.

    Precautions for generate statement block:

    generate Not on always Block, but always Blocks, etc. are included in generate Medium;
    generate for In, to be in begin It is mandatory to name the module later, and it is also necessary to assign the name when generating multiple instances with circular generation statements;
    task Can't put it in generatefor In order to realize the same function, sub modules are used;
    Loop generation in progress for Statement must use genvar Keyword definition, genvar Keywords can be written in generate Outside the statement, it can also be written in generate In the statement, as long as it precedes for Statement declaration is sufficient,And genvar Keyword defines only variables generate Statement blocks can be used;
    for The content of the statement must be begin-end,Even if there is only one statement, it cannot be omitted. This is also a mandatory requirement, and naming the loop is also inseparable begin keyword;
    

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
    

Posted by daveoliveruk on Tue, 12 Oct 2021 13:19:42 -0700