Verilog https://www.verilogpro.com/category/verilog/ Verilog and Systemverilog Resources for Design and Verification Thu, 29 Sep 2022 08:01:22 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.2 98068679 Verilog Module for Design and Testbench https://www.verilogpro.com/verilog-module-for-design-and-testbench/ https://www.verilogpro.com/verilog-module-for-design-and-testbench/#comments Sun, 19 Jun 2022 07:48:07 +0000 https://www.verilogpro.com/?p=784 A Verilog module is a building block that defines a design or testbench component, by defining the building block’s ports and internal behaviour. Higher-level modules can embed lower-level modules to create hierarchical designs. Different Verilog modules communicate with each other through Verilog port. Together, the many Verilog modules communicate and model dataflow of a larger, ... Read more

The post Verilog Module for Design and Testbench appeared first on Verilog Pro.

]]>
A Verilog module is a building block that defines a design or testbench component, by defining the building block’s ports and internal behaviour. Higher-level modules can embed lower-level modules to create hierarchical designs. Different Verilog modules communicate with each other through Verilog port. Together, the many Verilog modules communicate and model dataflow of a larger, hierarchical design.

Verilog has a simple organization. All data, functions, and tasks are in modules, except for system tasks and functions, which are global. Any uninstantiated module is at the top level. A model must contain at least one top-level module.

Defining a Verilog Module

A Verilog module is enclosed between the keywords module and endmodule. It has the following components:

  • Keyword module to begin the definition
  • Identifier that is the name of the module
  • Optional list of parameters
  • Optional list of ports (to be addressed more deeply in a future article)
  • Module item
  • Keyword endmodule to end the definition

Let’s address each of these components one by one.

Verilog Parameter

Verilog parameters were introduced in Verilog-2001 (not present in the original Verilog-1995). They allow a single piece of Verilog module code to be more extensible and reusable. Each instantiation of a Verilog module can supply different values to the parameters, creating different variations of the same base Verilog module. For example, a FIFO Verilog module may have a Verilog parameter to adjust its data width (or even data type, in SystemVerilog). They are not strictly necessary for a design, so I will defer discussing the topic further to a future article.

Verilog Port

A Verilog module only optionally needs to have a list of Verilog port (a port list). For example, a top level testbench may not have any Verilog ports at all. Verilog ports allow different modules of a design to communicate with each other. There are other (more backdoor) ways that Verilog modules can communicate. But for a design that intends to be synthesized, Verilog ports is the standard method. There are many ways to code port connections. This will be discussed in more detail in another future article.

Module Item

Module item is essentially the “code that is inside the module” (after the port declaration). It defines what constitutes the module, and can include many different types of declarations and definitions (net and variable declarations, always blocks and initial blocks, etc.)

Putting it Together

Here is a very simple example of Verilog module definition, that puts together all the pieces above.

module my_module
#(
    parameter WIDTH = 1
) (
    input wire              clk,
    input wire              rst_n,
    input wire [WIDTH-1:0]  in_a, in_b,
    output reg [WIDTH-1:0]  out_c
);

always @(posedge clk or negedge rst_n)
    out_c <= in_a & in_b;

endmodule

Instantiating a Verilog Module

A Verilog module can instantiate other Verilog modules, creating a hierarchy of modules to form the full design and testbench. Any uninstantiated module is at the top level.

Instantiation Statement

The Verilog module instantiation statement creates one or more named instances of a defined module. Multiple instances (identical copies of the Verilog module) can be created on the same line of code. This type of coding style is obviously easier with simple modules that have few (or none) ports. Multiple instantiations can even contain a range specification. This allows an array of instances to be created.

Connecting the Ports

For a Verilog module that does have ports, Verilog defines two styles to connect the ports:

  • By position – specify the connection to each port in the same order as the ports were listed in the module declaration
  • By name – specify each port by the name used in the (sub) module declaration, followed by the name used in the instantiating module

When connecting by name, an unconnected port can be indicated by either omitting it from the port list, or by providing no expression in the parentheses ( .name () ). The two types of port connections shall not be mixed (in Verilog) in a single declaration.

For a Verilog module that does not have any port, you still need to write the parentheses when instantiating it.

As to what to connect to the port, from Verilog, it can be a register or net identifier, an expression, or a blank (to indicate no connection that that port). An unconnected port may also be simply omitted from the port list, but only when connecting by name.

Here are some examples to illustrate the concepts above.

wire clk, rst_n;
wire a, b, c1, c2, c3, c4, d;

// Instantiating a module and connecting ports by position
my_module mod_b (clk, rst_n, a, b, c1);

// Instantiating a module and connecting ports by name
my_module mod_a
(
    .clk   (clk),
    .rst_n (rst_n),
    .in_a  (a),
    .in_b  (b),
    .out_c (c2)
);

// Instantiating a module, but leaving a port unconnected (a bug in this case!)
my_module mod_c
(
    .clk   (), // A bug! But this will compile
    .rst_n (rst_n),
    .in_a  (a),
    .in_b  (b),
    .out_c (c3)
);

// Instantiating a module with no ports
my_module_with_no_ports mod_d();

// Connecting an expression to a port
my_module mod_e
(
    .clk   (clk),
    .rst_n (rst_n),
    .in_a  (a & d), // an expression as a port connection
    .in_b  (b),
    .out_c (c4)
);

Verilog Module Hierarchy

When instantiating and connecting Verilog modules and ports, a hierarchical design is created. Every identifier (for example every module) has a unique hierarchical path name. This is useful generally in testbench coding, where you sometimes need to reference a particular signal, in a somewhat backdoor way, in a different module within your testbench. This is generally not used in design coding, where you always want to more formally use Verilog ports to make explicit connections and model dataflow (with exception possibly with Verilog defparam—topic for a future article).

The complete hierarchy of names can be viewed as a tree structure, with the root being the top level module. Each module, generate block instance, task, function, named begin-end or fork-join block defines a new hierarchical level (also called a scope), in a particular branch of the tree. A design description contains one or more top-level modules. Each such module forms the root of a name hierarchy. The following figure shows an example Verilog module hierarchy.

Verilog module hierarchical name example
Verilog module hierarchical name example

Verilog generate block, that do not have a name label, creates a hierarchy that is only visible within the block, and within the sub-tree formed by this block—and nowhere else. Therefore it’s good practice to always name Verilog generate blocks so all identifiers can be referenced throughout your environment. See my article Verilog Generate Configurable RTL Designs.

Now that we have defined a hierarchy, we can reference any named Verilog object or hierarchical name reference, by concatenating the names of the modules, module instance names, generate blocks, tasks, functions, or named blocks that contain it. Each of the names in the hierarchy is separated by a period.

You can reference the complete path name, starting from the top-level (root) module, or you can reference “downwards”, starting from the level where the path is being used. You can also reference a particular instance of an array (or a generate loop) by a constant expression within square brackets. The expression shall evaluate to one of the legal index values of the array.

The following code matches the example design hierarchy above. We’ll use it to illustrate some examples of referencing.

// A trivial AND module
module my_and
(
  input wire in1,
  input wire in2,
  output wire out
);
  assign out = in1 & in2;

endmodule

// A design that instantiates the AND module
module dut
(
  input wire clk,
  input wire rst_n,
  input wire [3:0] a, b,
  output reg [3:0] c
);
  my_and i_my_and_3 (a[3], b[3], c[3]);

  generate
    genvar gi;
    for (gi=0; gi<3; gi=gi+1) begin : gen_and
      my_and i_my_and (a[gi], b[gi], c[gi]);
    end // gen_and
  endgenerate
endmodule

// Testbench that instantiates the design under test (DUT)
module testbench;
  wire clk, rst_n;
  wire [3:0] a, b, c;

  dut i_dut
  (
    .clk   (clk),
    .rst_n (rst_n),
    .a     (a),
    .b     (b),
    .c     (c)
  );

  // Some examples of hierarchical references
  initial begin
    $monitor(a); // reference to testbench hierarchy
    $monitor(i_dut.a); // reference to dut hierarchy
    $monitor(i_dut.i_my_and_3.out); // reference to dut sub-module hierarchy
    $monitor(i_dut.gen_and[0].i_my_and.out); // reference to generated sub-module
  end
endmodule

Some final notes on Verilog module hierarchy:

  • Each node in the hierarchical name tree creates a new scope for identifiers. Within each scope, the same identifier (e.g. net name, variable name) can be declared only once. Conversely, it is permitted to use the same identifier in different scopes, or different Verilog modules.
  • Objects declared in automatic tasks and functions are one exception that cannot be referenced by hierarchical names.
  • It is also possible, from Verilog LRM perspective, to reference “upwards” in the hierarchy. For example, it is possible for a lower level module to reference a variable in the module that instantiates the lower level module. However, the usage is tricky, and application is dubious (certainly not recommended for design code), so I will not go into it here.

Verilog Module Summary

Verilog module is one of the fundamental hierarchical constructs in Verilog. It encapsulates code and functionality, allowing a larger design to be built from lower level components, enhancing modularity and reuse. This article described the basic syntax of a Verilog module, how to define a module, how to connect multiple modules together, and how the interconnection creates a design hierarchy.

The next article in this fundamentals series will dive deeper into Verilog ports, exploring how the syntax evolved from the original Verilog to the latest SystemVerilog language manuals. Stay tuned!

References

The post Verilog Module for Design and Testbench appeared first on Verilog Pro.

]]>
https://www.verilogpro.com/verilog-module-for-design-and-testbench/feed/ 2 784
Verilog Always Block for RTL Modeling https://www.verilogpro.com/verilog-always-block/ https://www.verilogpro.com/verilog-always-block/#respond Wed, 13 Apr 2022 15:00:00 +0000 https://www.verilogpro.com/?p=723 Verilog always block is a procedural statement that starts an activity flow. It is essentially an infinite loop. However, when combined with a Verilog event expression, it can be used to model combinational and sequential logic.

The post Verilog Always Block for RTL Modeling appeared first on Verilog Pro.

]]>
This article is going to introduce the Verilog always block—one of the most basic constructs that has existed since the very beginning of Verilog (IEEE standard 1364-1995)—relate it to some other introductory constructs, and use them to write some simple hardware logic.

After a long hiatus, I’m picking up the proverbial pen again and writing some Verilog articles! I have a new goal to create a series of articles to help new engineers transition from “textbook knowledge” to real world knowledge needed to become a digital design engineer. You’ll see some new articles on basic concepts, as well as intermediate level concepts similar to my previous articles. Hope you find these new articles useful for your career! Let’s get started!

Verilog Always Block In a Nutshell

Verilog behaviour models (RTL design/model is a class of behavioural models) contain procedural statements that control the simulation, and manipulate variables to model hardware circuitry and data flow. The Verilog always block is a procedural statement that starts an activity flow. Each Verilog always block starts a separate activity flow. All of the activity flows are concurrent to model the inherent concurrence of hardware. Each Verilog always block repeats continuously throughout the duration of the simulation, executing the statements defined in its procedure. Its activity ceases only when the simulation is terminated.

An Infinite Loop?

The Verilog always block essentially describes an infinite loop. Without some form of timing control to avoid a zero-delay infinite loop, and allow simulation time to advance, a simulation deadlock condition can be created (read: a simulation hang). The following code, for example, creates such a zero-delay infinite loop.

always areg = ~areg;

If there is a Verilog always block with no timing control, your simulation will look as though it has hung, and will not advance in time. So let’s add a timing control to make the code more useful:

always #half_period clk = ~clk;

Now this becomes a potentially useful statement. It causes the clk signal to toggle its polarity at a delay of half_period. If you haven’t noticed already, it can be used to create a continuously toggling clock stimulus in a simulation.

The “#” is formally called a delay control. It causes the simulation to insert the specified delay (to that procedural block) where the “#” is written.

Timing Control with Event Expression (Sensitivity List)

Another way to provide a timing control is in the form of a Verilog event expression. The syntax of Verilog event expression is “@(event_expression)“. For the procedural block that contains the Verilog event expression, it causes the simulator to wait until the event_expression has occurred before continuing execution of that procedural block. When used together with a Verilog always block, it adds the necessary timing control to make the Verilog always block useful to model hardware. The SystemVerilog standards (IEEE 1800-2005 onwards) also describe the event expression, when used to trigger a Verilog always block, as a “sensitivity list”.

The event expression (or sensitivity list) can contain multiple signals. Each signal is separated by the keyword “or”. The following Verilog always block describes a simple OR gate with inputs A and B, and output C. This code tells the simulator to re-evaluate the value of output C, whenever the signal A or B changes. It is not entirely straightforward, but you can see how this essentially describes the behaviour of an OR gate in simulation.

always @(A or B)
    C = A | B;

Modelling Hardware Logic with Verilog Always Block

Combining these ideas brings us to the more common usage of Verilog always block—together with an event expression.

always @(event_expression)
    single_statement;

always @(event_expression)
begin
    multiple_statements;
end

For hardware modeling, the Verilog event expression is typically specified in one of two forms:

  • Sequential logic: execution triggered based on a clock event (and frequently a reset event)
  • Combinational logic: execution triggered based on the inputs to the logic (i.e. nets and variables on the right hand side of an assignment statement)

Modeling Sequential Logic

A typical event expression for a flip-flop based design is “@(posedge clock_name)”. This tells the simulator to only evaluate the Verilog always block when the clock_name signal makes a positive edge transition (0->1), which is how a flip-flop based design is constructed. Without the “posedge” keyword, the Verilog always block will trigger on any transition of the clock_name signal, both positive and negative.

The following code describes a flip-flop with input d and output q, clocked by positive edge of clk (with no reset):

always @(posedge clk)
    q <= d;

Let’s make it a more complete flip-flop design by also adding a reset, namely an asynchronous reset. An asynchronous reset means the reset will occur even without the presence of a clock. An asynchronous reset is modeled by adding the reset signal also to the sensitivity list. This will cause the simulator to re-evaluate this Verilog always block when the reset signal transitions, irrespective of whether the clk signal transitions (which is what an asynchronous reset means). The Verilog event expression “@(negedge rst_n)” makes the reset active low (trigger an evaluation when rst_n transitions 1->0), and the “if” statement specifies a reset value for the flip-flop. The following code describes the same flip-flop, now with an active-low asynchronous reset (rst_n) that will reset the flip-flop output q to 0 when the reset is asserted (when rst_n transitions 1->0).

always @(posedge clk or negedge rst_n)
    if (!rst_n)
        q <= 1'b0;
    else
        q <= d;

To describe two flip flops, you can write them as two separate Verilog always block. Let’s make it more interesting and put them in series into a two-stage pipeline. First the diagram, then the code.

Two stage pipelined D flip flops
Two stage pipelined D flip flops
always @(posedge clk or negedge rst_n)
    if (!rst_n)
        q1 <= 1'b0;
    else
        q1 <= d1;

always @(posedge clk or negedge rst_n)
    if (!rst_n)
        q2 <= 1'b0;
    else
        q2 <= q1;

Or you can also write it as one Verilog always block.

always @(posedge clk or negedge rst_n)
    if (!rst_n) begin
        q1 <= 1'b0;
        q2 <= 1'b0;
    end
    else begin
        q1 <= d1;
        q2 <= q1;
    end

When there are multiple Verilog always blocks, there is no implied order of execution between them. There is also no limit to the number of always constructs that can be defined in a module.

Modeling Combinational Logic

The Verilog always block can also model combinational logic, but it is a bit less straight forward to understand. A physical implementation of a combinational circuit obviously operates continuously, sampling the inputs and calculating the resulting outputs. A simulator, however, cannot execute a logical statement “continuously”, without causing the zero-delay infinite loop described at the beginning of the article. Therefore to simulate combinational circuit, we have to define specific events at which the simulator should execute procedures, while maintaining correct behaviour.

The simple answer is, to model combinational circuit, the sensitivity list needs to contain all the inputs to the circuit (all the variables on the right hand side of the assignment). The following code describes a combinational AND gate using a Verilog always block.

always @(A or B)
    C = A & B;

More on Event Expression with Verilog Always Block

Here are a few more tips with using Verilog event expression with Verilog always block.

Using Comma in Event Expression

Verilog-2001 standard introduced the use of comma “,” to separate items in the event expression. Prior to that with the original Verilog-1995 standard, the separate items in the event expression must be separated by the keyword “or”. I have used the Verilog-1995 syntax in all the code examples so far. But here is the same flip-flop code example written with the Verilog-2001 syntax.

always @(posedge clk, negedge rst_n)
    if (!rst_n)
        q <= 1'b0;
    else
        q <= d;

Personally I prefer the Verilog-2001 syntax. That is how I write my code.

Implicit Event Expression @* or @(*)

Sensitivity list is a frequent source of bugs in a Verilog design/model. A deeper discussion of common pitfalls will be the subject of a future article. The standard writers wanted to simplify the usage of the the sensitivity list somewhat in Verilog-2001, so they added the “implicit event_expression list” syntax to simplify using Verilog always block, at least to describe combinational logic. Using an implicit event expression list, the AND gate combinational logic can be rewritten as follows.

always @*
    C = A & B;

always @(*)
    C = A & B;

More precisely, the “@*” and “@(*)” syntax will add all nets and variables that appear in the (right hand side of a) statement to the event expression, with some exceptions.

Verilog Always Block, Evolution to SystemVerilog always_comb and always_ff

SystemVerilog adds several new syntax in addition to the Verilog always block, primarily to address the exceptions noted above. You can read more about these constructs in my article SystemVerilog always_comb, always_ff.

Conclusion

Verilog always block is one of the four procedural statements in the original Verilog language. It can be used to model testbench stimulus as well as hardware design. The Verilog always block is essentially an infinite loop. However, when combined with a Verilog event expression, it can be used to model combinational and sequential logic. SystemVerilog adds several new versions of “always”, in addition to Verilog always block, to address some limitations and pitfalls of the original Verilog syntax.

References

The post Verilog Always Block for RTL Modeling appeared first on Verilog Pro.

]]>
https://www.verilogpro.com/verilog-always-block/feed/ 0 723
Verilog Generate Configurable RTL Designs https://www.verilogpro.com/verilog-generate-configurable-rtl/ https://www.verilogpro.com/verilog-generate-configurable-rtl/#comments Thu, 04 Jan 2018 18:00:09 +0000 http://www.verilogpro.com/?p=641 Verilog generate statement is a powerful construct for writing configurable, synthesizable RTL. It can be used to create multiple instantiations of modules and code, or conditionally instantiate blocks of code. However, many Verilog programmers often have questions about how to use Verilog generate effectively. In this article, I will review the usage of three forms ... Read more

The post Verilog Generate Configurable RTL Designs appeared first on Verilog Pro.

]]>
Verilog generate statement is a powerful construct for writing configurable, synthesizable RTL. It can be used to create multiple instantiations of modules and code, or conditionally instantiate blocks of code. However, many Verilog programmers often have questions about how to use Verilog generate effectively. In this article, I will review the usage of three forms of Verilog generate—generate loop, if-generate, and case-generate.

Types of Verilog Generate Constructs

There are two kinds of Verilog generate constructs. Generate loop constructs allow a block of code to be instantiated multiple times, controlled by a variable index. Conditional generate constructs select at most one block of code between multiple blocks. Conditional generate constructs include if-generate and case-generate forms.

Verilog generate constructs are evaluated at elaboration, which occurs after parsing the HDL (and preprocessor), but before simulation begins. Therefore all expressions within generate constructs must be constant expressions, deterministic at elaboration time. For example, generate constructs can be affected by values from parameters, but not by dynamic variables.

A Verilog generate block creates a new scope and a new level of hierarchy, almost like instantiating a module. This sometimes causes confusion when trying to write a hierarchical reference to signals or modules within a generate block, so it is something to keep in mind.

Use of the keywords generate and endgenerate (and begin/end) is actually optional. If they are used, then they define a generate region. Generate regions can only occur directly within a module, and they cannot nest. For readability, I like to use the generate and endgenerate keywords.

Verilog Generate Loop

The syntax for a generate loop is similar to that of a for loop statement. The loop index variable must first be declared in a genvar declaration before it can be used. The genvar is used as an integer to evaluate the generate loop during elaboration. The genvar declaration can be inside or outside the generate region, and the same loop index variable can be used in multiple generate loops, as long as the loops don’t nest.

Within each instance of the “unrolled” generate loop, an implicit localparam is created with the same name and type as the loop index variable. Its value is the “index” of the particular instance of the “unrolled” loop. This localparam can be referenced from RTL to control the generated code, and even referenced by a hierarchical reference.

Generate block in a Verilog generate loop can be named or unnamed. If it is named, then an array of generate block instances is created. Some tools warn you about unnamed generate loops, so it is good practice to always name them.

The following example shows a gray to binary code converter written using a Verilog generate loop.

Example of parameterized gray to binary code converter

module gray2bin
#(parameter SIZE = 8)
(
  input [SIZE-1:0] gray,
  output [SIZE-1:0] bin
)

Genvar gi;
// generate and endgenerate is optional
// generate (optional)
  for (gi=0; gi&lt;SIZE; gi=gi+1) begin : genbit
    assign bin[gi] = ^gray[SIZE-1:gi]; // Thanks Dhruvkumar!
  end
// endgenerate (optional)
endmodule

Another example from the Verilog-2005 LRM illustrates how each iteration of the Verilog generate loop creates a new scope. Notice wire t1, t2, t3 are declared within the generate loop. Each loop iteration creates a new t1, t2, t3 that do not conflict, and they are used to wire one generated instance of the adder to the next. Also note the naming of the hierarchical reference to reference an instance within the generate loop.

module addergen1
#(parameter SIZE = 4)
(
  input  logic [SIZE-1:0] a, b,
  input  logic            ci,
  output logic            co,
  output logic [SIZE-1:0] sum
);

wire [SIZE :0] c;
genvar i;

assign c[0] = ci;

// Hierarchical gate instance names are:
// xor gates: bitnum[0].g1 bitnum[1].g1 bitnum[2].g1 bitnum[3].g1
// bitnum[0].g2 bitnum[1].g2 bitnum[2].g2 bitnum[3].g2
// and gates: bitnum[0].g3 bitnum[1].g3 bitnum[2].g3 bitnum[3].g3
// bitnum[0].g4 bitnum[1].g4 bitnum[2].g4 bitnum[3].g4
// or gates: bitnum[0].g5 bitnum[1].g5 bitnum[2].g5 bitnum[3].g5
// Gate instances are connected with nets named:
// bitnum[0].t1 bitnum[1].t1 bitnum[2].t1 bitnum[3].t1
// bitnum[0].t2 bitnum[1].t2 bitnum[2].t2 bitnum[3].t2
// bitnum[0].t3 bitnum[1].t3 bitnum[2].t3 bitnum[3].t3

for(i=0; i&lt;SIZE; i=i+1) begin:bitnum
  wire t1, t2, t3;
  xor g1 ( t1, a[i], b[i]);
  xor g2 ( sum[i], t1, c[i]);
  and g3 ( t2, a[i], b[i]);
  and g4 ( t3, t1, c[i]);
  or g5 ( c[i+1], t2, t3);
end

assign co = c[SIZE];

endmodule

Generate loops can also nest. Only a single generate/endgenerate is needed (or none, since it’s optional) to encompass the nested generate loops. Remember each generate loop creates a new scope. Therefore the hierarchical reference to the inner loop needs to include the label of the outer loop.

Conditional If-Generate

Conditional if-generate selects at most one generate block from a set of alternative generate blocks. Note I say at most, because it may also select none of the blocks. The condition must again be a constant expression during elaboration.

Conditional if-generate may be named or unnamed, and may or may not have begin/end. Either way, it can contain only one item. It also creates a separate scope and level of hierarchy, like a generate loop. Since conditional generate selects at most one block of code, it is legal to name the alternative blocks of code within the single if-generate with the same name. That helps to keep hierarchical reference to the code common regardless of which block of code is selected. Different generate constructs, however, must have different names.

Conditional Case-Generate

Similar to if-generate, case-generate can also be used to conditionally select one block of code from several blocks. Its usage is similar to the basic case statement, and all rules from if-generate also apply to case-generate.

Direct Nesting of Conditional Generate

There is a special case where nested conditional generate blocks that are not surrounded by begin/end can consolidate into a single scope/hierarchy. This avoids creating unnecessary scope/hierarchy within the module to complicate the hierarchical reference. This special case does not apply at all to loop generate.

The example below shows how this special rule can be used to construct complex if-else if conditional generate statements that belong to the same hierarchy.

module test;
  parameter p = 0, q = 0;
  wire a, b, c;

  //---------------------------------------------------------
  // Code to either generate a u1.g1 instance or no instance.
  // The u1.g1 instance of one of the following gates:
  // (and, or, xor, xnor) is generated if
  // {p,q} == {1,0}, {1,2}, {2,0}, {2,1}, {2,2}, {2, default}
  //---------------------------------------------------------

  if (p == 1)
    if (q == 0) begin : u1 // If p==1 and q==0, then instantiate
      and g1(a, b, c); // AND with hierarchical name test.u1.g1
    end
    else if (q == 2) begin : u1 // If p==1 and q==2, then instantiate
      or g1(a, b, c); // OR with hierarchical name test.u1.g1
    end
    // "else" added to end "if (q == 2)" statement
    else ; // If p==1 and q!=0 or 2, then no instantiation
  else if (p == 2)
    case (q)
      0, 1, 2:
        begin : u1 // If p==2 and q==0,1, or 2, then instantiate
          xor g1(a, b, c); // XOR with hierarchical name test.u1.g1
        end
      default:
        begin : u1 // If p==2 and q!=0,1, or 2, then instantiate
          xnor g1(a, b, c); // XNOR with hierarchical name test.u1.g1
        end
    endcase

endmodule

This generate construct will select at most one of the generate blocks named u1. The hierarchical name of the gate instantiation in that block would be test.u1.g1. When nesting if-generate constructs, the else always belongs to the nearest if construct. Note the careful placement of begin/end within the code Any additional begin/end will violate the direct nesting requirements, and cause an additional hierarchy to be created.

Named vs Unnamed Generate Blocks

It is recommended to always name generate blocks to simplify hierarchical reference. Moreover, various tools often complain about anonymous generate blocks. However, if a generate block is unnamed, the LRM does describe a fixed rule for how tools shall name an anonymous generate block based on the text of the RTL code.

First, each generate construct in a scope is assigned a number, starting from 1 for the generate construct that appears first in the RTL code within that scope, and increases by 1 for each subsequent generate construct in that scope. The number is assigned to both named and unnamed generate constructs. All unnamed generate blocks will then be given the name genblk[n] where [n] is the number assigned to its enclosing generate construct.

It is apparent from the rule that RTL code changes will cause the unnamed generate construct name to change. That in turn makes it difficult to maintain hierarchical references in RTL and scripts. Therefore, it is recommended to always name generate blocks.

Conclusion

Verilog generate constructs are powerful ways to create configurable RTL that can have different behaviours depending on parameterization. Generate loop allows code to be instantiated multiple times, controlled by an index. Conditional generate, if-generate and case-generate, can conditionally instantiate code. The most important recommendation regarding generate constructs is to always name them, which helps simplify hierarchical references and code maintenance.

References

The post Verilog Generate Configurable RTL Designs appeared first on Verilog Pro.

]]>
https://www.verilogpro.com/verilog-generate-configurable-rtl/feed/ 17 641
Verilog Arrays Plain and Simple https://www.verilogpro.com/verilog-arrays-plain-simple/ https://www.verilogpro.com/verilog-arrays-plain-simple/#comments Tue, 25 Jul 2017 17:00:47 +0000 http://www.verilogpro.com/?p=598 Arrays are an integral part of many modern programming languages. Verilog arrays are quite simple; the Verilog-2005 standard has only 2 pages describing arrays, a stark contrast from SystemVerilog-2012 which has 20+ pages on arrays. Having a good understanding of what array features are available in plain Verilog will help understand the motivation and improvements ... Read more

The post Verilog Arrays Plain and Simple appeared first on Verilog Pro.

]]>
Arrays are an integral part of many modern programming languages. Verilog arrays are quite simple; the Verilog-2005 standard has only 2 pages describing arrays, a stark contrast from SystemVerilog-2012 which has 20+ pages on arrays. Having a good understanding of what array features are available in plain Verilog will help understand the motivation and improvements introduced in SystemVerilog. In this article I will restrict the discussion to plain Verilog arrays, and discuss SystemVerilog arrays in an upcoming post.

Verilog Arrays

Verilog arrays can be used to group elements into multidimensional objects to be manipulated more easily. Since Verilog does not have user-defined types, we are restricted to arrays of built-in Verilog types like nets, regs, and other Verilog variable types.

Each array dimension is declared by having the min and max indices in square brackets. Array indices can be written in either direction:

array_name[least_significant_index:most_significant_index], e.g. array1[0:7]
array_name[most_significant_index:least_significant_index], e.g. array2[7:0]

Personally I prefer the array2 form for consistency, since I also write vector indices (square brackets before the array name) in [most_significant:least_significant] form. However, this is only a preference not a requirement.

A multi-dimensional array can be declared by having multiple dimensions after the array declaration. Any square brackets before the array identifier is part of the data type that is being replicated in the array.

The Verilog-2005 specification also calls a one-dimensional array with elements of type reg a memory. It is useful for modeling memory elements like read-only memory (ROM), and random access memory (RAM).

Verilog arrays are synthesizable, so you can use them in synthesizable RTL code.

reg [31:0] x[127:0];          // 128-element array of 32-bit wide reg
wire[15:0] y[  7:0], z[7:0];  // 2 arrays of 16-bit wide wires indexed from 7 to 0
reg [ 7:0] mema   [255:0];    // 256-entry memory mema of 8-bit registers
reg        arrayb [  7:0][255:0]; // two-dimensional array of one bit registers

Assigning and Copying Verilog Arrays

Verilog arrays can only be referenced one element at a time. Therefore, an array has to be copied a single element at a time. Array initialization has to happen a single element at a time. It is possible, however, to loop through array elements with a generate or similar loop construct. Elements of a memory must also be referenced one element at a time.

initial begin
  mema             = 0; // Illegal syntax - Attempt to write to entire array
  arrayb[1]        = 0; // Illegal syntax - Attempt to write to elements [1][255]...[1][0]
  arrayb[1][31:12] = 0; // Illegal syntax - Attempt to write to multiple elements
  mema[1]          = 0; // Assigns 0 to the second element of mema
  arrayb[1][0]     = 0; // Assigns 0 to the bit referenced by indices [1][0]
end

// Generate loop with arrays of wires
generate
genvar gi;
  for (gi=0; gi&lt;8; gi=gi+1) begin : gen_array_transform
    my_example_16_bit_transform_module u_mod (
      .in  (y[gi]),
      .out (z[gi])
    );
  end
endgenerate

// For loop with arrays
integer index;
always @(posedge clk, negedge rst_n) begin
  if (!rst_n) begin
    // reset arrayb
    for (index=0; index&lt;256; index=index+1) begin
      mema[index] &lt;= 8'h00;
    end
  end
  else begin
    // out of reset functional code
  end
end

Conclusion

Verilog arrays are plain, simple, but quite limited. They really do not have many features beyond the basics of grouping signals together into a multidimensional structure. SystemVerilog arrays, on the other hand, are much more flexible and have a wide range of new features and uses. In the next article—SystemVerilog arrays, Synthesizable and Flexible—I will discuss the new features that have been added to SystemVerilog arrays and how to use them.

References

Sample Source Code

The accompany source code for this article is a toy example module and testbench that illustrates SystemVerilog array capabilities, including using an array as a port, assigning multi-dimensional arrays, and assigning slices of arrays. Download and run it to see how it works!

[lab_subscriber_download_form download_id=11].

The post Verilog Arrays Plain and Simple appeared first on Verilog Pro.

]]>
https://www.verilogpro.com/verilog-arrays-plain-simple/feed/ 14 598
Verilog reg, Verilog wire, SystemVerilog logic. What’s the difference? https://www.verilogpro.com/verilog-reg-verilog-wire-systemverilog-logic/ https://www.verilogpro.com/verilog-reg-verilog-wire-systemverilog-logic/#comments Tue, 02 May 2017 17:00:40 +0000 http://www.verilogpro.com/?p=499 The difference between Verilog reg and Verilog wire frequently confuses many programmers just starting with the language (certainly confused me!). As a beginner, I was told to follow these guidelines, which seemed to generally work: Use Verilog reg for left hand side (LHS) of signals assigned inside in always blocks Use Verilog wire for LHS ... Read more

The post Verilog reg, Verilog wire, SystemVerilog logic. What’s the difference? appeared first on Verilog Pro.

]]>
The difference between Verilog reg and Verilog wire frequently confuses many programmers just starting with the language (certainly confused me!). As a beginner, I was told to follow these guidelines, which seemed to generally work:

  • Use Verilog reg for left hand side (LHS) of signals assigned inside in always blocks
  • Use Verilog wire for LHS of signals assigned outside always blocks

Then when I adopted SystemVerilog for writing RTL designs, I was told everything can now be “type logic”. That again generally worked, but every now and then I would run into a cryptic error message about variables, nets, and assignment.

So I decided to find out exactly how these data types worked to write this article. I dug into the language reference manual, searched for the now-defunct Verilog-2005 standard document, and got into a bit of history lesson. Read on for my discovery of the differences between Verilog reg, Verilog wire, and SystemVerilog logic.

Verilog data types, Verilog reg, Verilog wire

Verilog data types are divided into two main groups: nets and variables. The distinction comes from how they are intended to represent different hardware structures.

A net data type represents a physical connection between structural entities (think a plain wire), such as between gates or between modules. It does not store any value. Its value is derived from what is being driven from its driver(s). Verilog wire is probably the most common net data type, although there are many other net data types such as tri, wand, supply0.

A variable data type generally represents a piece of storage. It holds a value assigned to it until the next assignment. Verilog reg is probably the most common variable data type. Verilog reg is generally used to model hardware registers (although it can also represent combinatorial logic, like inside an always@(*) block). Other variable data types include integer, time, real, realtime.

Almost all Verilog data types are 4-state, which means they can take on 4 values:

  • 0 represents a logic zero, or a false condition
  • 1 represents a logic one, or a true condition
  • X represents an unknown logic value
  • Z represents a high-impedance state

Verilog rule of thumb 1: use Verilog reg when you want to represent a piece of storage, and use Verilog wire when you want to represent a physical connection.

Assigning values to Verilog reg, Verilog wire

Verilog net data types can only be assigned values by continuous assignments. This means using constructs like continuous assignment statement (assign statement), or drive it from an output port. A continuous assignment drives a net similar to how a gate drives a net. The expression on the right hand side can be thought of as a combinatorial circuit that drives the net continuously.

Verilog variable data types can only be assigned values using procedural assignments. This means inside an always block, an initial block, a task, a function. The assignment occurs on some kind of trigger (like the posedge of a clock), after which the variable retains its value until the next assignment (at the next trigger). This makes variables ideal for modeling storage elements like flip-flops.

Verilog rule of thmb 2: drive a Verilog wire with assign statement or port output, and drive a Verilog reg from an always block. If you want to drive a physical connection with combinatorial logic inside an always@(*) block, then you have to declare the physical connection as Verilog reg.

SystemVerilog logic, data types, and data objects

SystemVerilog introduces a new 2-state data type—where only logic 0 and logic 1 are allowed, not X or Z—for testbench modeling. To distinguish the old Verilog 4-state behaviour, a new SystemVerilog logic data type is added to describe a generic 4-state data type.

What used to be data types in Verilog, like wire, reg, wand, are now called data objects in SystemVerilog. Wire, reg, wand (and almost all previous Verilog data types) are 4-state data objects. Bit, byte, shortint, int, longint are the new SystemVerilog 2-state data objects.

There are still the two main groups of data objects: nets and variables. All the Verilog data types (now data objects) that we are familiar with, since they are 4-state, should now properly also contain the SystemVerilog logic keyword.

wire my_wire;                       // implicitly means "wire logic my_wire" 
wire logic my_wire;                 // you can also declare it this way
wire [7:0] my_wire_bus;             // implicitly means "wire logic[15:0] my_wire_bus" 
wire logic [7:0] my_wire_logic_bus; // you can also declare it this way
reg [15:0] my_reg_bus;              // implicitly means "reg logic[15:0] my_reg_bus" 
//reg logic [15:0] my_reg_bus;        // but if you declare it fully, VCS 2014.10 doesn't like it

There is a new way to declare variables, beginning with the keyword var. If the data type (2-state or 4-state) is not specified, then it is implicitly declared as logic. Below are some variable declaration examples. Although some don’t seem to be fully supported by tools.

  // From the SV-2012 LRM Section 6.8
  var byte my_byte;    // byte is 2-state, so this is a variable
  //  var v;           // implicitly means "var logic v;", but VCS 2014.10 doesn't like this
  var logic v;         // this is okay
  //  var [15:0] vw;   // implicitly means "var logic [15:0] vw;", but VCS 2014.10 doesn't like this
  var logic [15:0] vw; // this is okay
  var enum bit {clear, error} status; // variable of enumerated type
  var reg r;                          // variable reg

Don’t worry too much about the var keyword. It was added for language preciseness (it’s what happens as a language evolves and language gurus strive to maintain backward-compatibility), and you’ll likely not see it in an RTL design.

I’m confused… Just tell me how I should use SystemVerilog logic!

After all that technical specification gobbledygook, I have good news if you’re using SystemVerilog for RTL design. For everyday usage in RTL design, you can pretty much forget all of that!

The SystemVerilog logic keyword standalone will declare a variable, but the rules have been rewritten such that you can pretty much use a variable everywhere in RTL design. Hence, you see in my example code from other articles, I use SystemVerilog logic to declare variables and ports.

module my_systemverilog_module
(
  input  logic       clk,
  input  logic       rst_n,
  input  logic       data_in_valid,
  input  logic [7:0] data_in_bus,
  output logic       data_out_valid, // driven by always_ff, it is a variable
  output logic [7:0] data_out_bus,   // driven by always_comb, it is a variable
  output logic       data_out_err    // also a variable, driven by continuous assignment (allowed in SV)
);

  assign data_out_err = 1'b1; // continuous assignment to a variable (allowed in SV)
//  always_comb data_out_err = 1'b0; // multiple drivers to variable not allowed, get compile time error

  always_comb data_out_bus = <data_out_bus logic expression>;
  always_ff @(posedge clk, negedge rst_n)
    if (!rst_n)
      data_out_valid <= 1'b0;
    else
      data_out_valid <= <data_out_valid logic expression>;
  
endmodule

When you use SystemVerilog logic standalone this way, there is another advantage of improved checking for unintended multiple drivers. Multiple assignments, or mixing continuous and procedural (always block) assignments, to a SystemVerilog variable is an error, which means you will most likely see a compile time error. Mixing and multiple assignments is allowed for a net. So if you really want a multiply-driven net you will need to declare it a wire.

In Verilog it was legal to have an assignment to a module output port (declared as Verilog wire or Verilog reg) from outside the module, or to have an assignment inside the module to a net declared as an input port. Both of these are frequently unintended wiring mistakes, causing contention. With SystemVerilog, an output port declared as SystemVerilog logic variable prohibits multiple drivers, and an assignment to an input port declared as SystemVerilog logic variable is also illegal. So if you make this kind of wiring mistake, you will likely again get a compile time error.

SystemVerilog rule of thumb 1: if using SystemVerilog for RTL design, use SystemVerilog logic to declare:

  • All point-to-point nets. If you specifically need a multi-driver net, then use one of the traditional net types like wire
  • All variables (logic driven by always blocks)
  • All input ports
  • All output ports

If you follow this rule, you can pretty much forget about the differences between Verilog reg and Verilog wire! (well, most of the time)

Conclusion

When I first wondered why it was possible to always write RTL using SystemVerilog logic keyword, I never expected it to become a major undertaking that involved reading and interpreting two different specifications, understanding complex language rules, and figuring out their nuances. At least I can say that the recommendations are easy to remember.

I hope this article gives you a good summary of Verilog reg, Verilog wire, SystemVerilog logic, their history, and a useful set of recommendations for RTL coding. I do not claim to be a Verilog or SystemVerilog language expert, so please do correct me if you felt I misinterpreted anything in the specifications.

References

Sample Source Code

The accompanying source code for this article is a SystemVerilog design and testbench toy example that demonstrates the difference between using Verilog reg, Verilog wire, and SystemVerilog logic to code design modules. Download the code to see how it works!

[lab_subscriber_download_form download_id=8].

The post Verilog reg, Verilog wire, SystemVerilog logic. What’s the difference? appeared first on Verilog Pro.

]]>
https://www.verilogpro.com/verilog-reg-verilog-wire-systemverilog-logic/feed/ 16 499
Verilog twins: case, casez, casex. Which Should I Use? https://www.verilogpro.com/verilog-case-casez-casex/ https://www.verilogpro.com/verilog-case-casez-casex/#comments Sun, 13 Sep 2015 18:01:11 +0000 http://www.verilogpro.com/?p=96 The Verilog case statement is a convenient structure to code various logic like decoders, encoders, onehot state machines. Verilog defines three versions of the case statement: case, casez, casex. Not only is it easy to confuse them, but there are subtleties between them that can trip up even experienced coders. In this article I will ... Read more

The post Verilog twins: case, casez, casex. Which Should I Use? appeared first on Verilog Pro.

]]>
The Verilog case statement is a convenient structure to code various logic like decoders, encoders, onehot state machines. Verilog defines three versions of the case statement: case, casez, casex. Not only is it easy to confuse them, but there are subtleties between them that can trip up even experienced coders. In this article I will highlight the identifying features of each of the twins, and discuss when each should be used.

Basic Verilog Case Statement

Let’s start by reviewing the basic case statement:

case (case_expression) // case statement header
  case_item_1 : begin
    case_statement_1a;
    case_statement_1b;
  end
  case_item_2 : case_statement_2;
  default     : case_statement_default;
endcase

A case statement has the following parts:

  • Case statement header—consists of the case, casez, or casex keyword followed by case expression
  • Case expression—the expression in parentheses immediately following the case keyword. Valid expressions include constants (e.g. 1’b1), an expression that evaluates to a constant, or a vector
  • Case item—the expression that is compared against the case expression. Note that C-style break is implied following each case item statement
  • Case item statement—one or more statements that is executed if the case item matches the current case expression. If more than one statement is required, they must be enclosed with begin…end
  • Case default—optional, but can include statements to be executed if none of the defined case items match the current case expression

Wildcard Case Statement: casez

The plain case statement is simple but rigid—everything must be explicitly coded. In some situations, you may want to specify a case item that can match multiple case expressions. This is where “wildcard” case expressions casez and casex come in. casez allows “Z” and “?” to be treated as don’t care values in either the case expression and/or the case item when doing case comparison. For example, a case item 2’b1? (or 2’b1Z) in a casez statement can match case expression of 2’b10, 2’b11, 2’b1X, 2’b1Z. It is generally recommended to use “?” characters instead of “Z” characters in the case item to indicate don’t care bits.

Verilog “wildcard” case statements can have overlapping case items. If more than one case item can match a case expression, the first matching case item has priority. Thus, priority logic can be inferred from a case statement. The following code snippet illustrates how casez can be used to code priority logic. It simulates and synthesizes correctly as a priority decoder.

always @(irq) begin
  {int2, int1, int0} = 3'b000;
  casez (irq)
    3'b1?? : int2 = 1'b1;
    3'b?1? : int1 = 1'b1;
    3'b??1 : int0 = 1'b1;
    default: {int2, int1, int0} = 3'b000;
  endcase
end
Verilog casez priority decoder

The logic will look something like this:

Even though this may seem an elegant way to code a priority decoder, the priority intention may only be apparent to the most experienced coders. Therefore, it is generally recommended to code priority logic using the more explicit if…else statement to clearly convey the intention.

While wildcard case comparison can be useful, it also has its dangers. Imagine a potentially dangerous casez statement where the case expression is a vector and one bit resolves to a “Z”, perhaps due to a mistakenly unconnected input. That expression will match a case item with any value for the “Z” bit! To put in more concrete terms, if the LSB of irq in the above code snippet is unconnected such that the case expression evaluates to 3’b00Z, the third case item will still match and int0 will be set to 1, potentially masking a bug!

Even wilder: casex

Now that we understand the usage and dangers of casez, it is straight-forward to extend the discussion to casex. casex allows “Z”, “?”, and “X” to be treated as don’t care values in either the case expression and/or the case item when doing case comparison. That means, everything we discussed for casez also applies for casex, plus “X” is now also a wildcard. In my previous article on Verilog X Optimism I discussed how X’s can propagate around a design and mask design issues. These propagated X’s can easily cause problems when combined with casex statements. To avoid these problems, the recommendation from RTL Coding Styles That Yield Simulation and Synthesis Mismatches is not to use casex at all for synthesizable code.

Verilog case, casez, casex each has its place and use cases. Understanding the differences between them is key to using them correctly and avoiding bugs. It may also help you in your next job interview 🙂

Have you come across any improper usage of these constructs? What are your recommendations for using them, especially casex? Leave a comment below!

SystemVerilog Unique Case

SystemVerilog adds a possible unique modifier to case statements. How does that change the behaviour? Head over to my post SystemVerilog Unique And Priority – How Do I Use Them?

References

  1. RTL Coding Styles That Yield Simulation and Synthesis Mismatches
  2. “full_case parallel_case”, the Evil Twins of Verilog Synthesis

Quiz and Sample Source Code

Now it’s time for a quiz! How will each of the following variations of case statement behave when the case expression

  1. matches one of the non-default case items
  2. does not match any non-default case item
  3. contains all X’s (e.g. if the signal comes from an uninitialized memory)
  4. contains all Z’s (e.g. if the signal is unconnected)
  5. contains a single bit X
  6. contains a single bit Z

Case statement variations:

  1. Plain case statement
  2. Plain case with default case
  3. Casez
  4. Casez with default case
  5. Casex
  6. Casex with default case
  7. Unique case
  8. Unique case with default case
  9. Unique casez
  10. Unique casex

Stumped? Download the executable source code to find out the answer!

[lab_subscriber_download_form download_id=7]

The post Verilog twins: case, casez, casex. Which Should I Use? appeared first on Verilog Pro.

]]>
https://www.verilogpro.com/verilog-case-casez-casex/feed/ 18 96
SystemVerilog and Verilog X Optimism – Hardware-like X Propagation with Xprop https://www.verilogpro.com/x-propagation-with-vcs-xprop/ https://www.verilogpro.com/x-propagation-with-vcs-xprop/#comments Sun, 30 Aug 2015 08:19:49 +0000 http://www.verilogpro.com/?p=45 In part 2 of this series, SystemVerilog and Verilog X Optimism – What About X Pessimism?, I discussed several coding styles that help to reduce the risk of missing design bugs due to Verilog X optimism. In part 3, we will take a look at how proprietary simulator features help avoid the problem by smartly ... Read more

The post SystemVerilog and Verilog X Optimism – Hardware-like X Propagation with Xprop appeared first on Verilog Pro.

]]>
In part 2 of this series, SystemVerilog and Verilog X Optimism – What About X Pessimism?, I discussed several coding styles that help to reduce the risk of missing design bugs due to Verilog X optimism. In part 3, we will take a look at how proprietary simulator features help avoid the problem by smartly doing X propagation. Specifically, we will look at Synopsys VCS Xprop.

Like the name suggests, X propagation means propagating an X at the input of some logic to its outputs. Synopsys VCS Xprop can do so smartly, in many cases avoiding X optimism, making simulation behave closer to real hardware.

Xprop has three modes: xmerge, tmerge, and vmerge. Xmerge simply assigns X to outputs whenever any of the inputs are X. This behaviour is similar to what would be observed in gate level simulations, but can sometimes be even more pessimistic. With tmerge, when an input is X, the simulator will traverse both code paths assuming the input is 0 and 1, and compare the results. If the results are the same, the determinate result is assigned to the output. If the results differ, X is assigned to the output to do X propagation. Vermge basically disables Xprop, allowing classic Verilog X optimism.

Consider the if…else statement again from part 1 of the series:

always_ff @(posedge clk) begin
  if (cond)
    c <= a;
  else
    c <= b;
end
MUX21NAND to illustrate xprop

Let’s also add a gate level implementation of this multiplexer for comparison.

The truth table of Xprop Tmerge and Xmerge is as follows. You can see that Tmerge closely resembles actual hardware behaviour (though not always; depending on how the statement is implemented in gates, the last row may return X in gates).
abcondClassic Verilog SimulationGate Level SimulationActual HardwareXprop XmergeXprop Tmerge
00X000X0
01X1X0/1XX
10X0X0/1XX
11X1X1X1

Xprop does similar X propagation on sequential logic with ambiguous clock transitions. Recall from part 1 of this series that Verilog normally treats ambiguous clock transitions as valid clock transitions (for example, 0->1, 0->X, 0->Z, X->1, Z->1 are all considered posedge), which can trigger sequential logic when real hardware may not. With Xprop enabled, an indeterminate clock transition will corrupt the outputs of sequential logic triggered by that clock.

Xprop can be even more useful with low power simulations, where powered off logic will drive X’s on outputs to indicate it is off. One of the ways that unisolated paths can be detected is to ensure these X’s can propagate to somewhere visible by your test environment.

Enabling Xprop takes relatively little effort. You simply need to provide some additional options to VCS, and an Xprop configuration file. The configuration file lists the module name, module hierarchy, or instance name to turn Xprop on or off. From the VCS User Guide:

vcs -xprop[=tmerge|xmerge] [-xprop=xprop_config_file] [-xprop=unifiedInference] source_files

A sample Xprop configuration file is as follows:

merge = tmerge
tree     {testbench}                             {xpropOff}; // marks a module name and its submodules
instance {u_testbench.u_dut_top}                 {xpropOff}; // marks an instance name
instance {u_testbench.u_dut_top.u_subsystem_top} {xpropOn};
module   {my_sram_module}                        {xpropOff}; // marks a module name

In a recent project I worked on, after turning on Xprop and debugging the resulting X’s, two design bugs were immediately found. The first bug was a list of flip-flops without reset that fed into the control path, causing a block to hang and not respond after reset. The block simulated fine without Xprop due to X optimism (in fact, block level verification was already complete). But once Xprop was enabled, it did not respond after reset until all the control path flip-flops were fixed and properly reset. The second bug was an incorrectly coded arbiter that could select an out of range bit out of a bus. Without Xprop, the arbiter returned 0 when the index was out of range of the bus width. With Xprop enabled, the arbiter assigned X to the output when the select became out of range and the bug surfaced.

One caveat when using Xprop is it can falsely score code coverage, as the simulator does in fact execute multiple branches of code when an X input causes divergent code paths. To work around this issue, since Xprop generally catches reset and testbench issues that are pervasive throughout an environment, you may be able to run only a small subset of tests with Xprop enabled to catch X optimism bugs, and collect code coverage with the remaining tests with Xprop disabled. Xprop may also not solve all your X optimism woes. X-Propagation: An Alternative to Gate Level Simulation discusses some false negatives from actual designs that Xprop stumbled on. Lastly, Xprop is not guaranteed to propagate X’s in your entire design and you should take a look at the Xprop reports to see which parts of your design it was able to instrument.

Xprop is a relatively easy way to make Verilog simulations behave closer to actual hardware. Provided that all block verification environments are Xprop clean, you will likely see diminishing returns using it at a cluster or full-chip environment. However, given the relatively low effort to setup and debug Xprop, it is definitely a simulator option that is recommended for any block level verification environment.

What are your experiences with using xprop in your simulations, especially in low power simulations with UPF? Have you found bugs you otherwise wouldn’t have? Leave a comment below!

The post SystemVerilog and Verilog X Optimism – Hardware-like X Propagation with Xprop appeared first on Verilog Pro.

]]>
https://www.verilogpro.com/x-propagation-with-vcs-xprop/feed/ 16 45
SystemVerilog and Verilog X Optimism – What About X Pessimism? https://www.verilogpro.com/systemverilog-verilog-x-optimism-pessimism/ https://www.verilogpro.com/systemverilog-verilog-x-optimism-pessimism/#comments Mon, 24 Aug 2015 06:31:32 +0000 http://www.verilogpro.com/?p=20 In my previous post about SystemVerilog and Verilog X Optimism – You May Not Be Simulating What You Think, I discussed what is Verilog X optimism, and some coding styles that are prone to Verilog X optimism bugs. So how do you avoid potential bugs that Verilog X optimism can introduce? One technique that has ... Read more

The post SystemVerilog and Verilog X Optimism – What About X Pessimism? appeared first on Verilog Pro.

]]>
In my previous post about SystemVerilog and Verilog X Optimism – You May Not Be Simulating What You Think, I discussed what is Verilog X optimism, and some coding styles that are prone to Verilog X optimism bugs. So how do you avoid potential bugs that Verilog X optimism can introduce?

One technique that has been discussed in literature is to not use X’s at all, but make the values of flip-flops and memories at the beginning of simulation determine, and randomly assign 0’s and 1’s to them. This would have to be done using a tool as doing so manually would be impractical. The problem with this approach is how would you know whether you have covered the state space that could cause a problem? The number of possible states is of course 2n, which is likely impractical to cover. This technique could be interesting though if, for example, you want to know whether a particular X on an un-reset flop can cause a design problem. You could setup two simulations assigning 0 and 1 to the X, and verify that they both produce the same result.

Another option is you could swing to the opposite end of the spectrum, and attempt to make constructs X pessimistic. With this principle, you would code RTL in such a way that X’s will propagate through logic, thus guaranteeing all ambiguities will propagate to downstream code. However, being pessimistic, simulations may yield X where there is really no uncertainty in actual silicon behaviour.

Looking at our X optimistic if…else again, first we can try making it more pessimistic by adding a “default” statement to fallback on. Consider this coding style for an if…else statement:

always_comb begin
  if (sel)
    y = a;
  else if (!sel)
    y = b;
  else
    y = 'x;
end

This coding style does swing to the opposite end of the spectrum of X-pessimism. Even if a and b have the same value, an X on sel causes the output y to become X.

Similarly, we can modify the X optimistic case statement to propagate X from case selection expression to outputs:

always_comb begin
  case (sel)
    2'b00:   y = a;
    2'b01:   y = a;
    2'b10:   y = a;
    2'b11:   y = a;
    default: y = 'x;
  endcase
end

This coding style does address Verilog X optimism in case statements, but not in wildcard case statements like casex and casez. Both of these coding styles can be processed by synthesis tools without problems. However, some linting tools may flag X assignment, and it is sometimes a controversial practice.

A different way to address if…else X optimism is with the conditional operator:

condition ? expression_if_true : expression_if_false

expression_if_true is executed if condition evaluates to true, and similarly, expression_if_false is executed if condition evaluates to false. But additionally, if condition evaluates to unknown, then for each bit in the value of expression_if_true that differs from expression_if_false (or if the bit is X or Z) then X is assigned to that bit. The caveat, however, is if condition is multi-bit, then any bit in the condition that is a 1 causes the entire condition to be evaluated as true. Therefore, the conditional operator does propagate X to downstream logic, provided the condition is a single bit expression. While this works for simple expressions, it quickly becomes unwieldy to code complex nested if…else statements using this technique.

Stuart Sutherland in his paper I’m Still In Love With My X! recommends a method of trapping X’s with assertions. This method can be applied to any conditions that include input ports to a module to trap X’s at inputs. Quoting from the paper:

`define assert_condition (cond) \
  assert (^cond === 1'bx) \
  else $error("%m, ifcond = X")

always_comb begin
  `assert_condition(sel)
  if (sel)
    y = a;
  else
    y = b;
end

always_comb begin
  `assert_condition((a,b,c,d))
  `assert_condition(sel)
  case (sel)
    2'b00 : out = a;
    2'b01 : out = b;
    2'b10 : out = c;
    2'b11 : out = d;
  endcase
end

Since SystemVerilog assertions are ignored by synthesis tools, this code can be synthesized without modification.

If you’d like to learn more, I’d recommend the very comprehensive paper I’m Still In Love With My X!. In the next post in the series, I will discuss using proprietary simulator features like Synopsys VCS xprop to address X optimism.

What are your experiences with Verilog X optimism or X pessimism? How do you ensure your simulation is as accurate as possible? Leave a comment below!

The post SystemVerilog and Verilog X Optimism – What About X Pessimism? appeared first on Verilog Pro.

]]>
https://www.verilogpro.com/systemverilog-verilog-x-optimism-pessimism/feed/ 6 20
SystemVerilog and Verilog X Optimism – You May Not Be Simulating What You Think https://www.verilogpro.com/systemverilog-verilog-x-optimism/ https://www.verilogpro.com/systemverilog-verilog-x-optimism/#comments Wed, 19 Aug 2015 07:37:19 +0000 http://www.verilogpro.com/?p=4 Verilog and SystemVerilog define 4 different logic values for modeling hardware: 1, 0, X, and Z. 1 and 0 are obviously real logic levels that can exist in silicon. Z and X, however, are modeling abstractions: Z represents a high-impedance (an un-driven or tri-stated signal) state, while X represents an unknown or indeterminate logic value. X’s ... Read more

The post SystemVerilog and Verilog X Optimism – You May Not Be Simulating What You Think appeared first on Verilog Pro.

]]>
Verilog and SystemVerilog define 4 different logic values for modeling hardware: 1, 0, X, and Z. 1 and 0 are obviously real logic levels that can exist in silicon. Z and X, however, are modeling abstractions: Z represents a high-impedance (an un-driven or tri-stated signal) state, while X represents an unknown or indeterminate logic value.

X’s can be created intentionally or unintentionally. The most common occurrence of X is in uninitialized registers or memories; X is used to represent the unknown value of these memory elements prior to a reset. Other conditions that can generate X include signals that are being simultaneously driven to different logic values by different drivers, to indicate logic that is shutdown in low power simulations, out of range bit selects and array indices. Some designers assign X to signals to show that they are “don’t care” values, as a hint to the synthesis tool so it can assign either 1 or 0 to the signals during logic optimization. To aid debug, some designers also assign X to signals in code paths that are impossible to reach, causing the simulator to flag erroneous execution of these paths by corrupting signals to X. Intentionally assigning X to signals is a controversial practice, however, and may be flagged by linting tools.

Verilog X optimism refers to how simulations may incorrectly exhibit determinate behaviour even when inputs to logic are X and have indeterminate value. It can be dangerous and can mask real RTL bugs.

The first example of Verilog X optimism is a simple if…else statement:

always_ff @(posedge clk) begin
  if (cond)
    c <= a;
  else
    c <= b;
end

Verilog (and SystemVerilog) LRM states that when the condition of an if…else statement is X, the condition is interpreted as false, and the code following the else statement must be executed.

A very real potential design bug is imagine if cond is directly generated from the contents of a memory, for example a combinationally generated error signal from a SECDED decoder, the always block is trying to set an error interrupt to 1 using the decoder error signal as cond. In simulation, the memory contents are X prior to being written. The X can propagate through the decoder to cond, causing the else statement to be executed, assigning 0 to the error interrupt. Contrast this with real silicon behaviour, where upon power on, the memory contents are truly unknown and will likely immediately trigger a SECDED decoder error. cond is true, and the interrupt fires.

The second example of Verilog X optimism is a case statement:

always_ff @(posedge clk) begin
  case (cond)
    1'b0 : c = a;
    1'b1 : c = b;
  endcase
end

X logic value in the select input actually causes c to retain the value of the previous clock, a completely different behaviour than the desired multiplexer.

A third example of Verilog X optimism is how Verilog and SystemVerilog treats transitions to X or Z. The following are all valid logic transitions that will trigger a posedge operator

  • 0->1, 0->X, 0->Z, X->1, Z->1

A 0->X or X->1 transition may or may not be a real posedge transition in silicon. But in simulation, the simulator will behave as though a positive edge on the clock has occurred, latching data into any registers triggered by posedge.

This post described three different Verilog and SystemVerilog constructs that can cause simulation and synthesis mismatch due to Verilog X optimism. Some material for further reading are X-Optimism Elimination during RTL Verification and I’m Still In Love With My X!.

In part 2 of the series, Are You A Pessimist? Mitigating Risk of SystemVerilog and Verilog X Optimism, I will describe several ways to avoid X optimism in simulation, and uncover design bugs that may be masked by Verilog X optimism.

The post SystemVerilog and Verilog X Optimism – You May Not Be Simulating What You Think appeared first on Verilog Pro.

]]>
https://www.verilogpro.com/systemverilog-verilog-x-optimism/feed/ 1 4