Introduction to Verilog

Sources:

Editted and padded GPT content; if you prefer human sources: Verilog Data Types

This article focus on Verilog as a programming language, i.e. the simulation part is not covered.

Verilog is C-like with a few quirks tweaked for the HDL side of things.

Data Types

Verilog is a hardware description language used in digital circuit design. Here are the data types in Verilog:

1. **Wire**: A wire is a net that can be used to connect different components in a Verilog design. It is used to represent a physical wire in a circuit. Here is an example:

wire a, b, c;

2. **Reg**: A reg is a variable that is used to store values in a Verilog design. It is used to represent a register in a circuit. Here is an example:

reg [7:0] data;

This creates an 8-bit register called `data`.

3. **Integer**: An integer is a data type used to represent signed or unsigned values. Here is an example:

integer count = 0;

This creates an integer variable called `count` and initializes it to 0.

4. **Real**: A real is a data type used to represent floating-point values. Here is an example:

real pi = 3.14159;

This creates a real variable called `pi` and initializes it to 3.14159.

5. **Time**: A time is a data type used to represent time values in a Verilog simulation. Here is an example:

time delay = 10;

This creates a time variable called `delay` and initializes it to 10 time units.

6. **Parameter**: A parameter is a constant value that is used in a Verilog design. It is used to make the code more readable and easier to modify. Here is an example:

parameter WIDTH = 8;

This creates a parameter called `WIDTH` with a value of 8.

7. **Vector**: A vector is a data type used to represent multiple binary values. Here is an example:

reg [7:0] data;

This creates an 8-bit vector called `data`.

8. **Array**: An array is a data type used to represent multiple values of the same type. Here is an example:

 reg [7:0] mem [0:255];

This creates an array called `mem` with 256 elements, each of which is an 8-bit vector.

Array

In Verilog, arrays can be used to store multiple values of the same data type. The syntax for declaring an array in Verilog is as follows:

data_type array_name [size-1 : 0];

Here, `data_type` specifies the type of data that will be stored in the array (such as `reg`, `wire`, or `integer`).

Arrays in Verilog can be one-dimensional or multi-dimensional.

reg [7:0] my_array [3:0];

This declares an array called `my_array` that can store 4 elements, each of which is an 8-bit value.

reg [7:0] my_array [3:0][2:0];

This declares an array called `my_array` that can store 12 elements, each of which is an 8-bit value. The first dimension has 4 elements, and the second dimension has 3 elements.

==> the array dimensions are ordered as row-major, but we should always explicitly access individual datum, i.e. providing full set of indices each time accessing the array;

Arrays in Verilog can be used for a variety of purposes, such as storing data from sensors, storing instructions for a processor, or storing lookup tables for digital signal processing.

Key Syntaxs

1. Module

Declaration: A module is the basic building block of a Verilog design. It is a block of code that describes a digital circuit or system. The module declaration syntax is as follows:

module module_name (input_list, output_list);
    // Verilog code here
endmodule

Here, `module_name` is the name of the module, `input_list` is a comma-separated list of input ports, and `output_list` is a comma-separated list of output ports. For example:

module adder (input a, b, output sum);
    assign sum = a + b;
endmodule

Instantiation, module instantiation is the process of creating an instance of a module within another module or in the top-level design. The syntax for module instantiation in Verilog is as follows:

module module_name (input_list, output_list);
    // module implementation
endmodule

module top_module;
    module_name instance_name (input_list, output_list);
endmodule

e.g. using the adder from above

module top_module;
    // Instantiate adder module
    adder adder_instance (.a(input_a), .b(input_b), .sum(output_sum));
    
    // Declare input and output ports
    input input_a, input_b;
    output output_sum;
    
    // Rest of the module implementation
endmodule

Optional Parameter and localparam

In Verilog, `parameter` and `localparam` are used to define constants that can be used throughout the design. The main difference between the two is their scope and how they are evaluated.

`parameter` is a global constant that can be accessed from any module or instance in the design. It is evaluated at compile-time and cannot be changed during simulation. `parameter` can be overridden by specifying a new value during instantiation of a module.

Example:

//declare an optional parameter, default to 8
module my_module #(parameter WIDTH = 8) (
  input [WIDTH-1:0] data_in,
  output [WIDTH-1:0] data_out
);
  // ...
endmodule

//instantiate the module and override the parameter
my_module #(13) myModule1 (data_in, data_out);

`localparam`, on the other hand, is a local constant that is only visible within the module or block where it is defined. It is evaluated at compile-time and cannot be changed during simulation.

Example:

module my_module (
  input [7:0] data_in,
  output [15:0] data_out
);
  localparam WIDTH = 8;
  // ...
endmodule

2. Assignments:

Verilog uses the `assign` keyword to assign a value to a signal. For example:

assign sum = a + b;

This Verilog code assigns the sum of `a` and `b` to the `sum` signal.

In Verilog, there are two types of assignments: blocking and non-blocking.

Blocking assignments are denoted by the "=" operator. They execute in the order they appear in the code, and the next statement will not execute until the current statement has been completed. This means that the value of the left-hand side variable is updated immediately with the value of the right-hand side expression. The example above is a blocking assignment.

Non-blocking assignments are denoted by the "<=" operator. They execute concurrently and do not block the execution of the next statement. The right-hand side expression is evaluated immediately, but the assignment is not completed until all other non-blocking assignments in the current procedural block have been evaluated.

a <= b + c;

The main difference between blocking and non-blocking assignments is the order in which they execute and update the values of variables. Blocking assignments are useful for combinational logic, while non-blocking assignments are useful for sequential logic.

4. Always Blocks

An `always` block is used to describe the behavior of a Verilog module. It is executed whenever any of its input signals change. The syntax for an `always` block is as follows:

always @(posedge clk) begin
    // Verilog code here
end

Here, `posedge clk` is a sensitivity list that specifies that the `always` block should be executed whenever the `clk` signal goes from low to high (on the positive edge of the clock). For example:

always @(posedge clk) begin
    carry <= a & b;
    sum <= a ^ b ^ carry;
end

This Verilog code describes the behavior of a full-adder module. It calculates the carry and sum outputs based on the inputs `a` and `b`.

5. Conditional Statements

Verilog supports `if-else` and `case` statements for conditional execution. The syntax for an `if-else` statement is as follows:

if (condition) begin
    // Verilog code here
end
else begin
    // Verilog code here
end

The syntax for a `case` statement is as follows:

case (expression)
    value1: begin
        // Verilog code here
    end
    value2: begin
        // Verilog code
    end
    default: begin
        // Verilog code
    end
endcase
module example(input [1:0] sel, output reg [3:0] out);

always @(*)
begin
    case(sel)
        2'b00: out = 4'b0000;
        2'b01: out = 4'b0001;
        2'b10: out = 4'b0010;
        2'b11: out = 4'b0011;
    endcase
end

endmodule

6. identifier rules

https://people.cs.georgetown.edu/~squier/Teaching/HardwareFundamentals/LC3-trunk/docs/verilog/VerilogLangRef.pdf

An identifier is any sequence of letters, digits, dollar signs ($), and the underscore (_) symbol. ==> [a-z_A-Z$0-9]

 The first character must not be a digit or $; it can be a letter or an underscore. Upper- and lower-case letters are considered to be different. Identifiers can be up to 1024 characters long.

7. initial blocks

Initial Block and Testbenches in Verilog_EverNoob的博客-CSDN博客

8. generate

In Verilog, the `generate` construct is used to generate hardware structures based on a set of rules or parameters. It is commonly used to create repetitive structures such as arrays, counters, and multiplexers. The `generate` construct allows for more concise and efficient code, as well as greater flexibility in designing complex hardware.

The basic syntax of a `generate` block is as follows:

generate
    // hardware structures to be generated
endgenerate

Within the `generate` block, Verilog code can be used to create hardware structures based on parameters or rules. These structures can be instantiated multiple times, with different parameters or conditions, using `for` or `if` statements.

Here is an example of a `generate` block that creates an 8-bit adder with a carry lookahead:
(using a full adder module such as:

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

  wire w1, w2, w3;

  assign w1 = a ^ b;
  assign w2 = w1 ^ cin;
  assign sum = w2;
  assign w3 = a & b;
  assign cout = w3 | (w1 & cin);

endmodule

)

wire a[7:0];
wire b[7:0];
wire cin;
wire sum[7:0];
wire carry[7:0];

generate
    // create 8 full adders
    genvar i;
    for (i = 0; i < 8; i = i + 1) begin : adder
        full_adder fa(
            .a(a[i]),
            .b(b[i]),
            .cin(cin),
            .sum(sum[i]),
            .cout(carry[i])
        );
    end

    // create carry lookahead
    assign carry[0] = cin;
    genvar j;
    for (j = 0; j < 7; j = j + 1) begin : lookahead
        assign carry[j+1] = carry[j] & carry[j+1];
    end
endgenerate

In this example, the `generate` block creates 8 instances of a full adder module, with inputs and outputs connected to arrays of signals. It then creates a carry lookahead circuit using a `for` loop and `assign` statements.

The `genvar` keyword is used to declare a generate variable, which can be used to iterate over a range of values in a `for` loop. The `begin` and `end` keywords are used to group the hardware structures created by each iteration of the loop.

Overall, the `generate` construct is a powerful tool for creating complex hardware structures in Verilog, allowing for greater efficiency and flexibility in designing digital circuits.

Special Operators

1. Concatenation Operator

The concatenation operator is used to combine two or more vectors into a single vector. In Verilog, the concatenation operator is represented by the symbol "{ }". Here's an example:

module concat_example(input [3:0] A, input [3:0] B, output [7:0] C);
  assign C = {A, B};
endmodule

In this example, the concatenation operator is used to combine two 4-bit vectors A and B into an 8-bit vector C.

2. Replication Operator

The replication operator is used to replicate a vector a specified number of times. In Verilog, the replication operator is represented by the symbol "{n{vec}}" where "n" is the number of times to replicate and "vec" is the vector to be replicated. Here's an example:

module replication_example(input [3:0] A, output [15:0] B);
  assign B = {4{A}};
endmodule

In this example, the replication operator is used to replicate the 4-bit vector A four times to create a 16-bit vector B.

3. Conditional Operator

The conditional operator is used to select one of two values based on a condition. In Verilog, the conditional operator is represented by the symbol "?:". Here's an example:

module conditional_example(input [3:0] A, input [3:0] B, input sel, output [3:0] C);
  assign C = sel ? A : B;
endmodule

In this example, the conditional operator is used to select either vector A or vector B based on the value of the "sel" input.

4. Bit-select Operator

The bit-select operator is used to select a single bit from a vector. In Verilog, the bit-select operator is represented by the symbol "[ ]". Here's an example:

module bit_select_example(input [7:0] A, output [3:0] B);
  assign B = A[3:0];
endmodule

In this example, the bit-select operator is used to select the lower 4 bits of the input vector A and assign it to the output vector B.

==> ! the MSB2LSB order is crucial, [0:3] would result in an error.

5. Reduction Operator

reduction operators are used to perform bitwise operations on a set of bits.

module reduction_operators_example;
  
  // Define a 4-bit input vector
  wire [3:0] a;
  
  // Define the output signals for each reduction operator
  wire and_out, or_out, xor_out, nand_out, nor_out, nxor_out;
  
  // Assign the input vector a value
  assign a = 4'b1101;
  
  // Use the reduction operators to compute the output signals
  assign and_out = &a;     // returns 0, not all bits are 1s
  assign or_out = |a;      // returns 1, at least 1 bit is 1
  assign xor_out = ^a;     // returns 1, 11 -> 0, 00 -> 0, 01 -> 1
  assign nand_out = ~&a;   // returns 1, not all bits are 1s
  assign nor_out = ~|a;    // returns 0, at least 1 bit is 1
  assign nxor_out = ~^a;   // returns 0, not even number of 1s
  
endmodule

==> xor returns 1 if an odd number of bits are 1, otherwise it returns 0;

==> the not version flip the reduced results; no need to perform the reversed bitwise operation.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值