• Randomization
  • Random Stability
  • String Methods
  • Convert hex, int, bin to string
  • Convert string to hex, int, bin
  • 10 Useful Utilities

SystemVerilog Generate Construct ¶

Overview ¶.

The Generate construct is a very useful tool. You'll commonly see it used for these 3 purposes

  • Lazy instantiation of module items using a for-loop
  • Changing the structure or design of a module using SystemVerilog Parameters
  • Using generate with assertions for Functional and Formal Verification

Generate Overview

Before we begin, there's one important thing to understand about the generate construct. Generate blocks are evaluated during elaboration time and the result is determined before the simulation begins. In other words generate statements are NOT a run-time construct. If you think about it for a second, the generate construct is actually creating a circuit and we cannot add or remove hardware circuits on-the-fly, so it does make sense that a generate block is evaluated during elaboration.

Loop Generate Construct ¶

The loop generate construct provides an easy and concise method to create multiple instances of module items such as module instances, assign statements, assertions, interface instances and so on. Think of it as a cloning machine.

In essence it is a special type of for loop with the loop index variable of datatype genvar . Here's an interesting fact about genvar - it is an integer datatype that exists only during elaboration time and disappears at simulation time.

You may also find the testbench for the above example useful!

Check it out here

... and as you would expect, you can nest generate for-loops. Just make sure you use separate genvars for the outer and inner loop and take care while referencing these vars in your nested for-loop. This is a common place where mistakes are made.

Get Notified when a new article is published!

Conditional Generate Construct ¶

The conditional generate construct lets you alter the structure of your design based on Parameter values passed during module instantiation. This is tremendously useful while creating parameterized common RTL blocks for your design.

A simple example -

Another example - You've been given the task of creating a common CRC generator block. Other designers in the team should be able to choose between 1 of 3 polynomials for the CRC calculation.

Here is one way to do it - you provide a parameter called CRC_SEL , which is set when this module is instantiated, and this CRC_SEL param selects which CRC function is generated within the module. By using a generate block instead of a simple mux, you save a bunch of gates and flops because the CRC functions that are not required are never instantiated.

Generate CRC Example

The code is explained within comments. An important thing to notice is how the function is called using crc_poly.CRC16_D8() . Read more about it in the code comments.

Certain parts of the code have been omitted to keep the snippet short. Download complete working version of this example

scroll to see more code

For completion, follow the github link and also take a look at the testbench code for the above crc_gen module, you may find it useful. Here are some waves from the testbench stimulus.

CRC Gen Waves

Assertions and Formal Verification ¶

The generate construct is also very useful while writing assertions and this in-turn helps with Formal Verification.

If you have any experience with Formal Verification, then you'll know that Formal tools very quickly run into computational bounds while trying to prove properties. So, it is important to keep your properties short and simple.

For example, if you have an arbiter block with 8 REQquest inputs and 8 ACK outputs, then instead of writing a single assertion to cover all 8 REQ/ACK pairs, it is better to break it down into 8 individual assertions with 1 REQ/ACK pair per assertion.

Here's another example, this is a little more exciting.

Let's say you have a Client - Server system and you have to do Formal Verification on the RTL model of the Client and that of the Server individually. The assertions on the outputs of the Client become assumptions on the input of the Server , and vice-versa. So, instead of writing separate assertions for each of them, this is how you could build your Formal TB.

  • Write a common module with all the properties. Define a parameter to tell this "properties module" if the CLIENT_IS_DUT or if SERVER_IS_DUT .
  • Bind the properties module to the Client or Server and pass the appropriate Parameter when doing the bind
  • Use a recipe of Generate and the Macro constructs to define your properties as assert or assume .

Like this -

The above example was adapted from the book Formal Verification , Erik Seligman, et al. , Chapter 7: Page 194

Hierarchically Accessing Generated Blocks ¶

One thing that trips up people is how to access a module item that are located within a generate block.

A generate block is always given a name. If you don't name it, the compiler will automatically assign a generic name such as genblk01 , genblk02 and you will typically have to dump waves and look at your Visualizer tool to see what names were assigned.

To access a module item within a generate block, you have to hierarchically access it using <generate_blk_name>.<module_item_name> . This is why in example 2.2 we invoked the CRC polynomial function by calling crc_poly.CRC16_D8() (i.e., <generate_blk_name>.<function_name> ).

Here's a nice example from the SystemVerilog LRM 1800-2012 (example 4 section 27.5). Look at how you access the task and module instance defined within the case-generate block.

In a Nutshell ¶

Wrapping things up - this is what we discussed in this article:

  • How to use loop generate construct to create multiple instances of module items
  • How to use conditional generate construct to change the module's design based on SystemVerilog parameters
  • How to use loop and conditional generate constructs with assertions
  • How to hierarchically access module items within generate blocks

References ¶

  • easics crc tool
  • Formal Verification - Erik Seligman, et al.

Questions & Comments ¶

For questions or comments on this article, please use this GitHub Discussions link .

If you would like to be notified when a new article is published, please sign up. If you found this content useful then please consider supporting this site! šŸ«¶

Sign-up for the Newsletter

Every month or so I send out a newsletter with notable technical papers, notifications about new articles and lessons from my experience.

If you found this content useful then please consider supporting this site! šŸ«¶

Buy Me A Coffee

Verilog Pro

Verilog Generate Configurable RTL Designs

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.

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.

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.

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.

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.

  • 1364-2005 ā€“ IEEE Standard for Verilog Hardware Description Language

Share this:

  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Twitter (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on Pocket (Opens in new window)
  • Click to email a link to a friend (Opens in new window)
  • Click to print (Opens in new window)

17 thoughts on “Verilog Generate Configurable RTL Designs”

Great article! I often use for-generate loops but I didnā€™t know about existing of if- and case-generate conditions šŸ™‚

Thanks for the Posting. I recently saw a ā€œconditional generateā€ code that I wasnā€™t aware of. Good to see this detailed explanation.

This is superb. Thanks for this great resource Jason!

Is there a way to use generate blocks for signal declarations? When I check with the systemverilog LRM I see this sentence, “A generate block may not contain port declarations, specify blocks, or specparam declarations.”.

What about internal signal declarations? For instance, if I have two different structs with different fields inside the structs, and I want to choose between the two types for an internal signal declaration type (note not a port) something like the below gives a compile error saying the signal is not declared.

parameter integer unsigned some_parameter = 1;

generate if (some_parameter == 1) begin type_a x; end else if (some_parameter = 0) begin type_b x; end endgenerate

Then at some point below X is referenced.

Thanks in advance!

Hi Sami. That’s an interesting question. I tried something similar to your code and also encountered an error during compilation that the type is unknown. I suspect it’s because generate statement is only resolved during elaboration, but the type needs to already exist during compilation. How I usually handle something like that is if the parameter only affects a field within the struct, I parameterize that field to use the parameter, rather than use generate to select between two structs. Also, you can pass a TYPE as a parameter into a module, and use that TYPE in the struct. That may also achieve what you’re looking for.

Thanks for the quick reply! Yes, what you suggested is the first approached I tried. I have parameters for the width of the fields in the struct. My hope with that approach is if the width parameter is set to 0 then that field should not exist. I wasn’t sure if that would be reality of what would happen. I noticed that in simulations that field shows up with negative index. For example:

parameter FIELD_WIDTH = 0; … logic [FIELD_WIDTH-1:0] x;

This will show up as x[-1:0] in waveforms. What I don’t know is if this is a tool quirk or this will actually synthesize to two bits.

Hi Jason, Great article. Is it possible to change the loop index inside the generate block itself? For example, in a generate block, I have an if statement which generates two instantiations and in the else statement it instantiates one. So I’d like to increment the loop index in the if statement itself. Is it possible? Regards, Kunal

Nice post. Seeing that you are using instances of logic gates. Guess I can instantiate any other module, but what about processes such as “always” or “assign”?

Yes you can also wrap always and assign statements in a generate.

Beautifully written, terse and clear!

Great article but there are several errors,

module for_loop_synthesis (i_Clock); input i_Clock; integer ii=0; reg [3:0] r_Shift_With_For = 4’h1; reg [3:0] r_Shift_Regular = 4’h1;

// Performs a shift left using a for loop always @(posedge i_Clock) begin for(ii=0; ii<3; ii=ii+1) r_Shift_With_For[ii+1] <= r_Shift_With_For[ii]; end

// Performs a shift left using regular statements always @(posedge i_Clock) begin r_Shift_Regular[1] <= r_Shift_Regular[0]; r_Shift_Regular[2] <= r_Shift_Regular[1]; r_Shift_Regular[3] <= r_Shift_Regular[2]; end endmodule

module for_loop_synthesis_tb (); // Testbench reg r_Clock = 1'b0; // Instantiate the Unit Under Test (UUT) for_loop_synthesis UUT (.i_Clock(r_Clock)); always #10 r_Clock = !r_Clock; endmodule

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

generate genvar gi; // generate and endgenerate is optional // generate (optional) for (gi=0; gi<SIZE; gi=gi+1) begin : genbit assign bin[gi] = ^gray[SIZE-1:gi]; end // endgenerate (optional) endgenerate endmodule

module gray2bin_tb(); //Testbench reg [15:0] in,out;

gray2bin #(16) DUT ( .gray(in), .bin(out) );

integer jj=0;

initial begin

for(jj=0;jj<16;jj=jj+1) begin in=jj; #5; end end

i have corrected all of them for you

thank you for making this cheers

Thanks for spotting the typo!

Nice work… Keep going…. Nice page…

Sir gray2bin code is synthesizable yes not and can I implement on the FPGA board

Yes it is certainly synthesizable and can be implemented in FPGA.

Nice article Jason, very helpful for beginners.

in the for loop,what is the meaning of the” gi=0; gi<SIZE; gi=gi+1″ .should gi < size

Leave a Comment Cancel reply

Notify me of follow-up comments by email.

Notify me of new posts by email.

This site uses Akismet to reduce spam. Learn how your comment data is processed .

Verilog generate block

A generate block allows to multiply module instances or perform conditional instantiation of any module. It provides the ability for the design to be built based on Verilog parameters. These statements are particularly convenient when the same operation or module instance needs to be repeated multiple times or if certain code has to be conditionally included based on given Verilog parameters.

A generate block cannot contain port, parameter, specparam declarations or specify blocks. However, other module items and other generate blocks are allowed. All generate instantiations are coded within a module and between the keywords generate and endgenerate .

Generate for loop

  • Generate if else
  • Generate case

A half adder will be instantiated N times in another top level design module called my_design using a generate for loop construct. The loop variable has to be declared using the keyword genvar which tells the tool that this variable is to be specifically used during elaboration of the generate block.

The testbench parameter is used to control the number of half adder instances in the design. When N is 2, my_design will have two instances of half adder.

a[0] and b[0] gives the output sum[0] and cout[0] while a[1] and b[1] gives the output sum[1] and cout[1] .

See that elaborated RTL does indeed have two half adder instances generated by the generate block.

verilog generate genvar assignment

Generate if

Shown below is an example using an if else inside a generate construct to select between two different multiplexer implementations. The first design uses an assign statement to implement a mux while the second design uses a case statement. A parameter called USE_CASE is defined in the top level design module to select between the two choices.

Testbench instantiates the top level module my_design and sets the parameter USE_CASE to 1 so that it instantiates the design using case statement.

When the parameter USE_CASE is 1, it can be seen from the simulation log that the multiplexer design using case statement is instantiated. And when USE_CASE is zero, the multiplexer design using assign statement is instantiated. This is visible from the display statement that gets printed in the simulation log.

Generate Case

A generate case allows modules, initial and always blocks to be instantiated in another module based on a case expression to select one of the many choices.

Note that because a half adder is instantiated, cin does not have any effect on the outputs sum and cout .

DMCA.com Protection Status

VLSI Verify

Generate Blocks in Verilog

The generate statement in Verilog is a very useful construct that generates synthesizable code during elaboration time dynamically. The simulator provides an elaborated code of the ā€˜generateā€™ block. It provides the below facilities:

  • To generate multiple module instances or code repetition.
  • Conditionally instantiate a block of code based on the Verilog parameter, however, the parameter is not permitted in the generate statement.

Ā  It basically provides control on variables, functions, tasks, and instantiation declarations. A generate block has been written within generate and endgenerate keywords.

Types of generate instantiation

  • Verilog gate primitives
  • Continuous assignments
  • Initial and always blocks
  • User-defined primitives

Letā€™s see what is allowed within the scope of a generate block.

Ā  Ā  Ā  Ā A. Data types

  • integer, real
  • time, realtime

Ā  Ā  Ā  Ā B. Function and task

Note: Function and task are not allowed within a generate loop, but they are allowed in generate block.

Below module items/declarations are not allowed within the scope of a generate block

  • Port declarations like input, output, and inoutĀ 
  • specify blocks
  • parameters and local parameters

Methods to write generate statements

Generate loop.

  • Generate conditional (includes generate if-else and generate case)

The generate loop is similar to the for loop statement, but it uses genvar keyword as a loop variable.Ā 

  • The genvar keyword is only used during the evaluation of generate block and does not exist during the simulation of the design. It needs to be used by a generate loop.
  • Generate loop provides flexibility to reduce code lines by replacing repetitive statements to a single statement like for loop.
  • Similar to a for loop, generate loops also can be nested with different genvar as an index variable.

Example: Using always block inside generate block

This code expands to

Example: Ripple Carry Adder

Generate conditional.

A generate block allows conditionally instantiated using if-else-if construct and case keyword.

Example: generate If-else

In the below example, based on parameter sel full adder or half-adder design is instantiated. By default, parameter sel = 0 means half adder will be instantiated. But from the testbench code, parameter sel = 1 is passed to instantiate full adder. $display can not be used within generate block without initial block, otherwise, it throws an error '$display' is an invalid generate scope construct.

Example: generate case

Similarly, the above example if-else generate block can alternatively use case statement as specified in the below example.

Verilog Tutorials

Generate blocks ¶

V. hunter adams ([email protected]) ¶, introduction ¶.

Generate blocks are a mechanism by which we can generate lots of Verilog. To quote the Sutherland HDL guide, "generate blocks provide control over the creation of many types of module items. A generate block must be defined within a module, and is used to generate code within that module."

Within these generate blocks, you can do things like declare variables, instantiate modules, wire modules together, etc. And furthermore, you can do these things conditionally. Usually, the conditions within a generate block will depend on the value of one or a number of genvars .

A genvar is "an integer variable which must be a positive value. They may only be used within a generate block. Genvar variables only have a value during elaboration, and do not exist during simulation. Genvar variables must be declared within the module where the genvar is used. They may be declared either inside or outside of a generate block." ( Evans and Sutherland HDL guide ).

Because generate blocks are only evaluated at elaboration, any conditionals within a generate block must have arguments which are constant expressions. Recall that the generate blocks are being used to build hardware. It therefore must be the case that all conditionals are evaluatable at compile time. If not, that would suggest we were building hardware at runtime, which doesn't make sense.

Video discussion of the content on this page ¶

Example ¶

Suppose that we wanted to construct a shift register, like the one shown below.

missing

We'll consider two ways of doing this. One using a generate block, and the other without using a generate block. Hopefully, by doing so, the utility of the generate block will be clear.

A shift register without using a generate block ¶

Let us first construct this circuit without using a generate block.

Please note that I have implemented this shift register such that we can replace a section with the generate block. In practice, you may implement this a bit more concisely. This is optimized instead for clarity. The output of this module will be 8 0's, then 8 1's, then 8 0's, etc.

A shift register using a generate block ¶

Instead of building all of the repetitive logic above, we could instead use a generate block. We would do so by creating a separate flip-flop module. This is the logic which we will use the generate block to create many copies of.

We can then use a generate block to instantiate and connect a bunch of copies of this module.

Genvar is a variable used in a generate loop.

Description:

A genvar is a variable used in generate-for loop. It stores positive integer values. It differs from other Verilog variables in that it can be assigned values and changed during compilation and elaboration time.

The genvar must be declared within the module where it is used, but it can be declared either inside or outside of the generate loop.

Writing Reusable Verilog Code using Generate and Parameters

In this blog post we look at the use of verilog parameters and the generate statement to write verilog code which is reusable . This includes examples of a parameterized module , a generate for block , generate if block and generate case block .

As with most programming languages, we should try to make as much of our code as possible reusable.

This allows us to reduce development time for future projects as we can more easily port code from one design to another.

We have two constructs available to us in verilog which can help us to write reusable code - parameters and generate statements.

Both of these constructs allow us to create more generic code which we can easily modify to suit our needs when we instantiate a component.

In the rest of this post, we look at both of these constructs in more detail.

Verilog Parameter

In verilog, parameters are a local form of constant which can be assigned a value when we instantiate a module .

As parameters have a limited scope , we can call the same verilog module multiple times and assign different values to the parameter. This allows us to configure the behaviour of a module on the fly.

As we discussed in the post on verilog modules , we must define an interface to a module when we write one.

We can then use this interface to interconnect a number of different modules within our FPGA design.

As a part of this interface, we can declare parameters as well as the inputs and outputs of the module.

The verilog code snippet below shows the method we use to declare a parameter in a module. When we declare a parameter in a verilog module like this, we call this a parameterized module.

The <parameter_name> field in the verilog code above is used to give an identifier to our parameters.

We use this identifier to call the parameter value within our code, much like with a normal variable.

We can also assign a default value to our parameter using the <default_value> field in the example above.

This is useful as it allows us to instantiate the component without having to specifically assign a value to the parameter.

When we instantiate a module in a verilog design unit, we can assign a value to the parameter using either named association or positional association . This is exactly the same as assigning a signal to an input or output on the module.

However when we write code which uses the verilog 1995 standard, we can only use positional association to assign values to a parameter.

The verilog code snippet below shows the methods we use to assign a value to a parameter when instantiating a module.

Verilog Parameterized Module Example

In order to better understand how we use parameters in verilog, let's consider a basic example.

For this example, let's consider a design which requires twoĀ  synchronous counters . One of these counters is 8 bits wide whilst the other is 12 bits wide.

To implement this circuit, we could write two different counter components which have different widths. However, this is an inefficient way of coding our circuit.

Instead, we will write a single counter circuit and use a parameter to change the number of bits in the output.

As it is not important to understanding how we use parameterized modules, we will exclude the functional code in this example.

Instead, we will look only at how we declare and instantiate a parameterized module in verilog.

The verilog code snippet below shows how we would write the interface for the parameterized counter module.

In this example we see how we can use a parameter to adjust the size of a signal in verilog.

Rather than using a fixed number to declare the port width, we substitute the parameter value into the port declaration.

This is one of the most common use cases for parameters in verilog.

In the verilog code above, we defined the default value of the BITS parameter as 8.

As a result of this, we only need to assign the parameter a value when we want an output that isn't 8 bits.

The code snippet below shows how we would instantiate this module when we want a 12 bit output.

In this instance, we must over ride the default value of the parameter when we instantiate the verilog module.

Although we use named association in the example above, we can also use positional association to assign values to a parameter in verilog.

The code snippet below shows how we would use positional association to assign the value of 12 to the BITS parameter.

Verilog Generate Statements

We use the generate statement in verilog to either conditionally or iteratively generate blocks of code in our design.

This allows us to selectively include or exclude blocks of code or to create multiple instances of a given code block.

We can only use the generate statement in concurrent verilog code blocks. This means we can't include it within always blocks or initial blocks .

In addition to this, we have to use either an if statement , case statement or a for loop in conjunction with the generate keyword.

We use the if and case generate statements to conditionally generate code whilst the for generate statement iteratively generates code.

We can write any valid verilog code which we require inside generate blocks. This includes always blocks , module instantiations and other generate statements.

The generate block was introduced in the verilog 2001 standard . As a result of this, we can't use this construct in verilog 1995 based designs.

Let's look at the three different types of generate block which we can use in our verilog designs.

Generate For Loop in Verilog

We can use a verilog for loop within a generate block to iteratively create multiple instances of a piece of code.

We typcially use the generate for loop approach to describe hardware which has a regular and repetitive structure.

For example, we may wish to describe a number of RAM modules which we want to control using a single bus .

If we use a generate block rather than manually instantiating all of the modules then we can reduce our code overhead.

The code snippet below shows the general syntax for the generate for block in verilog.

As we can see from this example, the syntax for this approach is virtually identical to the syntax we saw in the post on the verilog for loop .

However, there are two important differences between this approach and the normal for loops.

First of all, we must declare the loop variable using the genvar type.

The second difference is that we declare the loop within a generate block rather than a normal procedural block such as a verilog always block .

This difference is important as it alters the fundamental behaviour of the code.

When we write a generate for block we are actually telling the verilog compiler to create multiple instances of the code block.

In contrast, when we use the normal for loop we are telling the verilog complier to create a single instance of the code block but execute it multiple times.

As an example, let's look a very simple use case where we want to assign data to a 2 bit vector.

The verilog code below shows how we would do this using a generate for and a for loop. In both cases, the functionality of the code is the same but the structure produced is very different.

If we were to unroll the for loop example, we would get the code show below.

In constrast, unrolling the generate for code would result in the code shown below.

From this, we can see how the generate for is fundamentally different to the for loop.

Verilog Generate For Example

To better demonstrate how the verilog generate for statement works, let's consider a basic example.

For this example, we will use an array of 3 RAM modules which are connected to the same bus.

Each of the RAM modules has a write enable port, a 4-bit address input and 4-bit data input. These signals are all connected to the same bus.

In addition, each of the RAMs has a 4-bit data output bus and an enable signal, which are independent for each RAM block.

The circuit diagram shows the circuit we are going to describe.

We need to declare a 3 bit vector which we can then use to connect to the RAM enable ports. We can then connect a different bit to each of the RAM blocks based on the value of the loop variable.

For the data output bus, we could create a 12 bit vector and connect the read data output to different 4-bit slices of the vector.

However, a more elegant solution is to use an array which consists of 3 4-bit vectors. Again, we can then use the loop variable to assign different elements of this array as required.

The verilog code snippet below shows how we would code this circuit using the for generate statement.

After synthesizing this code, we get the circuit shown below.

If Generate Statement in Verilog

We use the generate if block in verilog to conditionally include blocks of verilog code in our design.

We can use the generate if statement when we have code that we only want to use under certain conditions.

One example of this is when we want to include a function in our design specifically for testing.

We can use a generate if statement to make sure that we only include this function with debug builds and not with production builds.

The code snippet below shows the general syntax for the verilog generate if statement.

As we can see from this example, the syntax for this approach is virtually identical to the syntax we saw in the post on the verilog if statement .

However, there is a fundamental difference between these two approaches.

When we write a generate if statement we are actually telling the verilog compiler to create an instance of the code block based on some condition.

This means that only one of the branches is compiled and any other branch is excluded from compilation. As a result of this, only one of the branches can ever be used within our design.

In contrast, when we use the if statement the entire if statement will get compiled and each branch of the statement can be executed.

Each time the if statement code is triggered during simulation, the condition is evaluated to determine which branch to execute.

Verilog Generate If Example

To better demonstrate how the verilog generate if statement works, let's consider a basic example.

For this example, we will write a test function which outputs the value of a 4-bit counter.

As this is a test function, we only need this to be active when we are using a debug build.

When we build a production version of our code, we tie the counter outputs to ground instead.

We will use a parameter to determine when we should build a debug version.

The code snippet below shows the implementation of this example.

When we set the debug_build variable to 1, the synthesizer produces the circuit shown below. In this case, the synthesis tool has produced a four bit counter circuit.

However, when we set the debug_build parameter to 0 then the synthesis tool produces the circuit shown below. In this instance, the synthesis tool has tied all bits of the count signal to ground.

Case Generate in Verilog

We use the generate case statement in verilog to conditionally include blocks of verilog code in our design.

The generate case statement essentially performs the same function as the generate if statement.

This means we can also use the generate case statement when we have code which we only want to include in our design under certain conditions.

For example, we could design a test function which we only want to include in debug builds.

We can then use the generate case statement to determine which version of the code gets built.

The code snippet below shows the general syntax for the generate case statement in verilog.

As we can see from this example, the syntax for this approach is virtually identical to the syntax we saw in the post on the verilog case statement .

When we write a generate case statement we are actually telling the verilog compiler to create an instance of the code block based on a given condition.

In contrast, when we use the case statement the entire case statement will get compiled and each branch of the statement can be executed

Each time the case statement code is triggered during simulation, the condition is evaluated to determine which branch to execute.

Verilog Generate Case Example

To better demonstrate how the verilog generate case statement works, let's consider a basic example.

As the case generate statement performs a similar function to the if generate statement, we will look at the same example again.

This means that we will write a test function which outputs the value of a 4-bit counter.

When we build a production version of our code, we tie the the counter outputs to ground instead.

The verilog code below shows the implementation of this example using the generate case statement.

What is the benefit of using parameterized modules?

We can configure the functionality of the module when we instantiate it. This allows us to make our code easier to reuse.

What do we use generate blocks for in veirlog?

We use them to control the way that our designs are compiled and built. They allow us to conditionally include blocks of code in our design at compilation time.

What is the main difference between a for loop and a generate for block?

The generate for block is evaluated at compile time, meaning only one branch of the code block is ever compiled. All the code of a for loop is compiled and it is evaluated continuously during simulations.

Write a generate for block which instantiates 2 16 bit synchronous counters. The two counters should use the parameterized module example from earlier in this post.

Write a generate for block which instantiates either an 8 bit counter or a 16 bit counter, based on the value of a parameter. The two counters should use the parameterized module example from earlier in this post. You can use either a generate case or a generate if block to write this code.

Using Tasks and Functions in Verilog

In this post we look at how we use tasks and functions in verilog. Collectively, these are known as subprograms and they allow us to write verilog code which is reusable .

As with most programming languages, we should try to make as much of our verilog code as possible reusable. This allows us to reduce development time for future projects as we can more easily port code from one design to another.

Whilst functions should be familiar to anyone with experience in other programming languages, tasks are less common in other languages.

There are two main differences between functions and tasks.

When we write a verilog function, it performs a calculation and returns a single value.

In contrast, a verilog task executes a number of sequential statements but doesn't return a value. Instead, the task can have an unlimited number of outputs

In addition to this, verilog functions execute immediately and can't contain time consuming constructs such as delays, posedge macros or wait statements

A verilog task , on the other hand, can contain time consuming constructs.

We will discuss both of these constructs in depth in the rest of this post. This includes giving examples of how we write and call functions and tasks in verilog.

Verilog Function

In verilog, a function is a subprogram which takes one or more input values, performs some calculation and returns an output value.

We use functions to implement small portions of code which we want to use in multiple places in our design.

By using a function instead of repeating the same code in several places, we make our code more maintainable .

We write the code for functions in the verilog module which we will use to call the function.

The code snippet below shows the general syntax for a function in verilog.

We must give every function a name, as denoted by the <name> field in the above example.

We can either declare the inputs inline with the function declaration or as part of the function body. The method we use to declare the input arguments has no affect on the performance of the function.

However, when we use inline declaration we can also omit the begin and end keywords if we want to.

We use the <arguments> field in the above example to declare the inputs to our function.

We use the <return_type> field to declare which verilog data type the function returns. If we exclude this part of the function declaration, then the function will return a 1 bit value by default.

When we return a value we do it by assigning a value to the name of the function. The code snippet below shows how we would simply return the input to a function. We can also simulate this example on EDA playground .

Rules for Using Functions in Verilog

Although functions are often fairly simple, there are a few basic rules which we must follow when we write a verilog function.

One of the most important rules of a function is that they can't contain any time consuming constructs such as delays, posedge macros or wait statements.

When we want to write a subprogram which consumes time we should use a verilog task instead.

As a result of this, we are also not able to call tasks from within a function. In contrast, we can call another function from within the body of a function.

As functions execute immediately, we can only use blocking assignment in our verilog functions.

When we write functions in verilog, we can declare and use local variables . This means that we can declare variables in the function which can't be accessed outside of the function it is declared in.

In addition to this, we can also access all global variables within a verilog function.

For example, if we declare a function within a module block then all of the variables declared in that module can be accessed and modified by the function.

The table below summarises the rules for using a function in verilog.

Verilog Function Example

To better demonstrate how to use a verilog function, let's consider a basic example.

For this example, we will write a function which takes 2 input arguments and returns the sum of them.

We use verilog integer types for the input arguments and the return types.

We must also make use of the verilog addition operator in order to calculate the sum of the inputs.

The code snippet below shows the implementation of this example function in verilog.

As we have previously discussed, there are two methods we can use to declare verilog functions and both of these are shown in the code below.

We can also simulate this example using EDA playground .

Calling a Function in Verilog

When we want to use a function in another part of our verilog design, we have to call it. The method we use to do this is similar to other programming languages.

When we call a function we pass parameters to the function in the same order as we declared them. This is known as positional association and it means that the order we declare our arguments in is very important.

The code snippet below shows how we would use positional association to call the addition example function .

In the example below, in_a would map to the a argument and in_b would map to b.

Automatic Functions in Verilog

We can also use the verilog automatic keyword to declare a function as reentrant .

However, the automatic keyword was introduced in the verilog 2001 standard meaning that we can't write reentrant functions when working with the verilog 1995 standard.

When we declare a function as reentrant, the variables and arguments within the function are dynamically allocated . In contrast, normal functions use static allocation for internal variables and arguments.

When we we write a normal function, all of the memory which is used to perform the processing of the function is allocated only once. This is process is known as static memory allocation in computer science.

As a result of this, our simulation software must execute the function in it's entirety before it can use the function again.

This also means that the memory the function uses is never deallocated . As a result of this, any values stored in this memory will maintain their value between calls to the function.

In contrast, functions which use the automatic keyword allocate memory whenever the function is called. The memory is then deallocated once the function has finished with it.

This process is known as automatic or dynamic memory allocation in computer science.

As a result of this, our simulation software can execute multiple instances of an automatic function.

We can use the automatic keyword to write recursive functions in verilog. This means we can create functions which call themselves to perform a calculation.

As an example, one common use case for recursive functions is calculating the factorial of a given number.

The code snippet below shows how we would use the automatic keyword to write a recursive function in verilog. We can also simulate this example using  EDA playground .

Verilog Task

We use verilog tasks to write small sections of code that we can reuse throughout our design.

Unlike functions, we can use time consuming constructs such as wait, posedge or delays (#) within a task. As a result of this, we can use both blocking and non-blocking assignment in verilog tasks.

Verilog tasks can also have any number of inputs and can also generate any number of outputs. This is in contrast to functions which can only return a single value.

These features mean tasks are best used to implement simple pieces of code which are repeated several times in our design. A good example of this would be driving the pins on a known interface, such as SPI or I2C .

We often write the code for tasks in the verilog module which will be used to call the task.

When we do this, we can also read or write any of the module's global variables inside of the task body.

We can also create global tasks which are shared by all modules in a given file. To do this we simply write the code for the task outside of the module declarations in the file.

The code snippet below shows the general syntax for a task in verilog.

As with functions, there are two ways in which we can declare a task but the performance of both approaches is the same.

We must give every task a name, as denoted by the <name> field above.

When we write tasks in verilog, we can declare and use local variables. This means that we can create variables in the task which can't be accessed outside of the task it is declared in.

In addition to this, we can also access all global variables within a verilog task.

Unlike verilog functions, we can call another task from within a task. We can also make calls to functions from within a task.

Verilog Task Example

Let's consider a simple example to better demonstrate how to write a verilog task.

For this example, we will write a basic task which can be used to generate a pulse. The length of the pulse can be specified when we call the task in our design.

In order to do this, we must declare a single time type input in our task.

We will generate the pulse on a global reg type signal so there is no need to declare any outputs for the task.

The verilog code below shows the implementation of this example using the two different styles of task. We can also simulate this example on EDA playground .

Although this example is quite simple, we can see here how we can use the verilog delay operator (#) in a task. If we attempted to write this code in a function, this would cause an error when we tried to compile it.

We can also see from this example that we don't return a value in the same way as we do with a function.

Instead, we simply assign values to any signals that we have access to either as inputs or as global variables.

We can include and drive as many signals as we want when we write a task in verilog.

Calling a Task in Verilog

As with functions, we must call a task when we want to use it in another part of our verilog design.

The method we use to do this is similar to the method used to call a function.

However, there is one important difference between calling tasks and functions in verilog.

When we call a task in verilog, we can't use it as part of an expression in the same way as we can a function.

We should instead think of task calls as being a short hand way of including a block of code into our design.

As with functions, we use positional association to pass paramaters to the task when we call it.

This simply means that we pass parameters to the task in the same order as we declared them when we wrote the task code.

The code snippet below shows how we would use positional association to call the pulse_generate task which we previously considered.

In this case, the pulse_length input is mapped to the pulse_time variable and the pulse output is mapped to the pulse_out variable.

Automatic Tasks in Verilog

We can also use the automatic keyword with verilog tasks in order to make them reentrant. Again, this keyword was introduced in the verilog 2001 standard meaning it can't be used with verilog 1995 compatible code.

As we talked about previously, using the automatic keyword means that our simulation tool uses dynamic memory allocation.

As with functions, tasks use static memory allocation by default which means that only one instance of a task can be run by the simulation software.

In contrast, tasks which use the automatic keyword allocate memory whenever the task is called. The memory is then freed once the task has finished with it.

Let's consider a basic example to show automatic tasks are used and how they differ from normals task.

For this example, we will use a simple task which increments the value of a local variable by a given amount.

We can then run this a number of times in a simulation tool to see how the local variable behaves using an automatic task and a normal task.

The code below shows how we write a static task to implement this example.

Running this code in the icarus verilog simulation tool results in the following output:

As we can see from this, the value of the local variable i is static and stored in a single memory location.

As a result of this, the value of i is persistent and it maintains it's value between calls to the task.

When we call the task we are incrementing the value which is already stored in the given memory location.

The code snippet below shows the same task except that this time we use the automatic keyword.

From this we can now see how the local variable i is dynamic and is created whenever the task is called. After it has been created, it is then assigned the value of 1.

When the task has finished running, the dynamically allocated memory is freed and the local variable no longer exists.

There are two main differences between tasks and functions, what are they?

A task can have ore than one output but a function can only have one. A function can not consume time but a task can.

What is the difference between an automatic function and a normal function in verilog?

Normal verilog functions use static memory allocation whereas automatic functions use dynamic memory allocation.

Write the code for a function which takes 3 integer inputs and returns the product of them.

Write the code for a task which returns the sum of 2 numbers. However, the result should be delayed by a fixed amount of time before it is returned. The task should take three inputs - one of time type which sets the delay time and the 2 integers that will be summed together. In addition, the task should have one output which is the result of the summation.

An Introduction to Loops in Verilog

In this post, we talk about the different types of loop which we can use in verilog - the for loop , while loop , forever loop and repeat loop .

As we saw in our previous post on sequential statements in verilog , there are a number of statements which we can only use within procedural blocks.

We use these statements to control the way that data is assigned in our verilog design.

The four different types of loop which we can use in verilog are also sequential statements which we use to assign data in our designs.

As a result of this, we can only write loops inside of procedural blocks such as an always block or initial block .

In the rest of this post, we talk about how each of these loops is used in verilog. We then consider a short example for each of these constructs to show how we use them in practise.

Loops in Verilog

We use loops in verilog to execute the same code a number of times.

The most commonly used loop in verilog is the for loop . We use this loop to execute a block of code a fixed number of times.

We can also use the repeat keyword in verilog which performs a similar function to the for loop. However, we generally prefer to use the for loop rather than the repeat keyword in verilog designs.

The other type of loop which we commonly use in verilog is the while loop . We use this loop to execute a part of our code for as long as a given condition is true.

Let's take a closer look at each of these types of loop.

Verilog forever loop

We use the forever loop in verilog to create a block of code which will execute continuously, much like an infinite loop in other programming languages.

This is in contrast to the other types of loop in verilog, such as the for loop and while loop, which only run a fixed number of times.

As we see saw in our previous post on verilog testbenches , one of the most common use cases for the forever loop is generating a clock signal in a verilog test bench.

The forever loop can not be synthesized meaning that we can only use it in our test bench code.

The code snippet below shows the general syntax for the verilog forever loop.

Forever loop example

To better demonstrate how we use the forever loop in practise let's consider an example.

For this example we will generate a clock signal with a frequency of 10MHz which we could use inside of a test bench.

To do this, we firstly assign our signal to an initial value. We then use the forever block to invert the signal at regular intervals.

The code snippet below shows how we would implement this clock example in verilog.

There are two important things to say about this example.

Firstly, note that we use the verilog initial block which is another example of a procedural statement. Any code which we write in an initial block is executed once at the beginning of a simulation.

We almost always use initial blocks rather than always blocks in our testbench code. The reason for this is that they only execute once and we typically only need to run our test once.

The other important thing to note here is the use of the # symbol to model time delays in verilog .

In order for this example to work properly we would need to include the verilog timescale compiler directive in our code.

We use the timescale compiler directive to specify the time unit and resolution of our simulations.

In this instance, we need to set the time units to ns as is shown in the code snippet below.

Verilog repeat loop

We use the repeat loop to execute a given block of verilog code a fixed number of times.

We specify the number of times the code block will execute in the repeat loop declaration.

Although we most commonly use the repeat loop in verilog test benches, we can also use it in sythesizable code.

However, we have to take care when using this construct synthesizable code as we can only use it to describe repetitive structures.

The code snippet below shows the general syntax of the verilog repeat loop

We use the <number> field to determine how many times the repeat loop is executed.

The repeat loop is very similar to the for loop in verilog as they both execute code a fixed number of times.

The main difference between these two types of loop is that the for loop includes a local variable which we can reference inside the loop. The value of this variable is updated on every iteration of the loop.

In contrast, the repeat loop doesn't include this local loop variable. As a result of this, the repeat loop is actually less verbose than the for loop in instances where we don't need this variable.

Repeat Loop Example

The repeat loop is a relatively straight forward construct. However, let's consider a basic example to better demonstrate how it works.

For this example, let's suppose that we have a signal in our design that we want to toggle whenever there is a rising edge on another signal in our design.

The waveform below shows the functionality which we are trying to achieve in this example loop.

However, we only want this toggle action to be effective a total of six times.

We can easily implement this in a repeat block, as shown in the code snippet below.

We can see in this example that we have set the <number> field to 6. As a result of this, the repeat loop will run a total of six times before terminating.

We then use the posedge macro which we talk about in the post on the verilog always block . This macro tells us when a rising edge has occurred on the sig_a signal in our code.

In verilog we use the @ symbol to tell our code to wait for an event to occur.

This simply means that the code will pause at this line and wait for the condition in the brackets to evaluate as true. Once this happens, the code will carry on running.

In this example, we use this operator to block the execution of our repeat loop until a rising edge is detected on the sig_a signal.

Finally, we can use the not verilog bit wise operator (~) to invert the sig_b signal whenever a rising edge has been detected.

The waveform below shows the simulation result of this code, as taken from the icarus verilog simulator and output to the gtkwave wave viewer.

Verilog while Loop

We use the while loop to execute a part of our verilog code for as long as a given condition is true.

The specified condition is evaluated before each iteration of the loop.

As a result of this, all of the code in the block will execute in each valid iteration.

This happens even if the condition changes so that it no longer evaluates to true whilst the code in the block is running.

We can think of the while loop as an if statement that executes repeatedly.

As while loops are generally not synthesizable, we often use them in our testbenches to generate stimulus.

The code snippet below shows the general syntax for a while loop in verilog.

We use the <condition> field in the above construct to determine when the execution of the loop is stopped.

while loop Example

To better demonstrate how we use the while loop in verilog, let's consider a basic example.

For this example, we will create an integer type variable which is increased from 0 to 3. We then print the value of this variable on each iteration of the loop.

Although this is a trivial example, it demonstrates the fundamental principles of the while loop.

The code snippet below shows how we would implement this example. This example can also be simulated on EDA playground .

This example assumes that the iter variable has already been declared and assigned an intial value of 0.

In every iteration of the loop, the second line of the code within the loop body increments the iter variable.

The <condition> field in this example is set so that the loop only executes when the iter variable is less than 4. As a result of this, the iter variable is incremented from 0 to 3 in this loop.

We use the $display system task , which we discussed in a previous post, to print the value of the iter variable on each iteration of the loop. The %0d operator indicates that the variable should be printed as a decimal number.

Verilog For Loop

When writing verilog code, we use the for loop to execute a block of code a fixed number of times.

As with the while loop, the for loop will execute for as long as a given condition is true. The specified condition is evaluated before each iteration of the loop.

We specify this condition as part of the for loop declaration. This condition is used to control the number of times the loop is executed.

Although it is commonly used in testbenches, we can also use the for loop in synthesizable verilog code.

When we use the for loop in synthesizable code, we typically use it to replicate sections of our hardware. One of the most common examples of this is a shift register.

As we previously mentioned, the for loop is very similar to the repeat loop. The main difference is that the for loop uses a local variable which can be used in our loop code.

The code snippet below shows the syntax we use in a verilog for loop.

We use the <initial_condition> field to set the initial value of our loop variable. We must declare the variable that we use in our loop before we can use it in our code.

The <stop_condition> field is the conditional statement which determines how many times the loop runs. The for loop will continue to execute until this field evaluates as false.

We use the <increment> field to determine how the loop variable is updated in every iteration of the loop.

Verilog for loop example

To better demonstrate how we use the for loop in verilog, let's consider a basic example.

For this example, we will write a simple four bit serial shift register using the verilog for loop. Implementing a shift register is actually one of the most common use cases of the for loop.

The shift register can be implemented using a simple verilog array .

We can then assign the input to the shift register to the first element of the array. We then use a for loop to shift the existing contents of the array to the left by one place.

The verilog code snippet below shows how we would implement this shift register using a for loop.

The first thing to notice in this code is that we use a loop variable (i) to reference an element of the array in our loop. We must declare this loop variable before we use it in our code.

As our shift array has four bits, we set the <stop_condition> field so that the loop executes only when the loop variable (i) is less than four.

Finally, we set the <increment> field so that the loop variable is incremented by one in every iteration. This allows us to iterate over every element in the array.

In this example, we make use of non-blocking assignment . The reason for this is that a shift register is an example of a sequential logic circuit.

Therefore, we would have to write this code inside of a clocked verilog always block to properly model a shift register.

Which type of loop do we use to create code which runs continuously?

The forever loop executes continuously.

Which function do we normally use a forever loop to implement in a verilog testbench?

The forever loop is commonly used to implement a clock signal in a verilog testbench

What is the main difference between a for loop and a repeat loop?

The for loop includes a local loop variable which is incremented on every iteration of the loop.

Write a for loop which implements an 8 bit shift register.

Rewrite the previous exercise so that it is implemented using a while loop.

If Statements and Case Statements in Verilog

In this post we talk about two of the most commonly used constructs in verilog - the if statement and the case statement.

We have seen in a previous post how use procedural blocks such as the always block to write verilog code which is executed sequentially .

We can also use a number of statements within procedural blocks which control the way that signals are assigned in our verilog designs. Collectively, these statements are known as sequential statements.

The case statement and the if statement are both examples of sequential statements in verilog.

In the rest of this post, we talk about how both of these statements are used in verilog. We then consider a short example for both of these constructs to show how we use them in practise.

Verilog If Statement

The if statement is a conditional statement which uses boolean conditions to determine which blocks of verilog code to execute.

Whenever a condition evaluates as true, the code branch associated with that condition is executed.

This statement is similar to if statements used in other programming languages such as C.

The verilog code snippet below shows the basic syntax for the if statement.

We can exclude the else and else if branches from the statement if we don't need them.

In fact, we have already seen this in the post on always blocks where we used the posedge macro to detect the rising edge of a clock signal.

We can include as many else if branches as necessary to properly model the underlying circuit.

The if statement uses boolean conditions to determine which lines of code to execute.

In the snippet above, these expressions are given by <expression1> and <expression2>.

These expressions are sequentially evaluated and the code associated with the expression is executed if it evaluates to true.

Only one branch of an if statement will ever execute. This is normally the first expression which evaluates as true.

The only exception to this occurs when none of the expressions are true. In this instance, the code in the else branch will execute.

When we omit the else branch in our if statement code then none of the branches will execute in this case.

The code associated with each branch can include any valid verilog code, including further if statements. This approach is known as nested if statements.

When using this type of code in verilog, we should take care to limit the number of nested statements as it can lead to difficulties in meeting timing.

If Statement Example

We have already seen a practical example of the if statement when modelling flip flops in the post on the verilog always block .

To demonstrate this construct more thoroughly, let's consider an example of a clocked multiplexor.

In this instance, we will use an asynchronously resettable D type flip flop to register the output of a multiplexor .

The circuit diagram below shows the circuit which we will use in this example.

The code snippet below shows how we implement this using a single always block and an if statement.

In this example, we use the first if statement to set the output of the flip flop to 0b whenever reset is active.

When the reset is not active, then the always block has been triggered by the rising edge of the clock. We use the else branch of the first if statement to capture this condition.

We use a second if statement to model the behaviour of the multiplexor circuit. This is an example of a nested if statement in verilog.

When the addr signal is 0b, we assign the output of the flip flop to input a. We use the first branch of the nested if statement to capture this condition.

We then use the else branch of the nested if statement to capture the case when the addr signal is 1b.

It is also possible for us to use an else-if type statement here but the else statement is more succinct. The behaviour is the same in both cases as the signal can only ever be 0b or 1b in a real circuit.

Verilog Case Statement

We use the verilog case statement to select a block of code to execute based on the value of a given signal in our design.

When we write a case statement in verilog we specify an input signal to monitor and evaluate.

The value of this signal is then compared with the values specified in each branch of the case statement.

Once a match is found for the input signal value, the branch associated with that value will execute.

The verilog case statement performs the same function as the switch statement in the C programming language.

The code snippet below shows the general syntax for the case statement in verilog.

It is possible to exclude the default branch of the statement, although this is not advisable. If the default branch is excluded then all valid values of the <variable> must have it's own branch.

As with the if statement, the code associated with each branch can include any valid verilog code.

This includes further sequential statements, such as if or case statements. Again, we should try to limit the number of nested statements as it makes it easier to meet our timing requirements.

Case Statement Example

To better demonstrate the way we use the case statement in verilog, let's consider a basic example.

For this example we will look at a simple four to one multiplexor circuit.

We frequently use the case statement to model large multiplexors in verilog as it produces more readable code than continuous assignment based implementations.

The code snippet below shows how we would implement this circuit using a case statement.

This example shows how simple it is to model a multiplexor using the case statement in verilog. In fact, the case statement provides the most intuitive way of modelling a multiplexor in verilog.

Although this example is quite straight forward, there are a few important points which we should consider in more detail.

The first thing to note in this example is that we use blocking assignment . The reason for this is that we are modelling combinational logic and non-blocking assignment normally leads to flip flops being placed in our design.

Another thing to note here is that we could remove the default keyword from this example. We would then explicitly list the value of addr required to output the value of d instead.

However, we have included the default keyword in this example to demonstrate how it should be used.

Which blocks do we use to write sequential statements in a verilog design?

Sequential statements can only be written within a procedural block such as an always block or initial block.

Which keywords can we exclude from the if statement when they are not required?

We can exclude the else and else if keywords if they are not needed.

How many branches of the if statement can be executed at one time?

A maximum of one branch in an if statement can execute at any time.

When can we exclude the default branch from the case statement?

We can exclude the default branch if all valid values of the input signal are explicitly listed.

Use a case statement to write the code for a six to one multiplexor.

Rewrite the six to one multiplexor from the last exercise so that it uses an if statement.

An introduction to SystemVerilog Data Types

In this post, we talk about the most commonly used data types in SystemVerilog. This includes a discussion of data representation , 2 state vs 4 state types, binary data types and numerical data types .

Although SystemVerilog is considered to be a loosely typed language, we must still declare a data type for every port or signal in our SystemVerilog design.

The type which we specify is used to define the characteristics of our data.

We can use types which interpret data purely as a logical value, for example. We can also use types which interpret our data as if it were a numeric value.

When we assign data to a signal in SystemVerilog, the data is implicitly converted to the correct type in most cases.

As a result, there is often no need necessary to explicitly perform type conversions in verilog.

As SystemVerilog is an extension of verilog, we can use all of the existing verilog data types in our code.

In addition to this, a number of new types were introduced as a part of the SystemVerilog standard.

In this post, we look at both the older verilog data types and the newer SystemVerilog types.

Representing Data in SystemVerilog

When we write SystemVerilog, we often need to represent digital data in our code. We can express this data as either a binary , hexadecimal or octal value.

Unlike in other programming languages, we also need to define the number of bits we have in our data representation.

This is because we are describing hardware circuits when we use SystemVerilog. Therefore, we can create data busses which contain as many bits as we choose.

The code snippet below shows the general syntax we use to represent digital data in SystemVerilog.

2 State vs 4 State Data Types

In verilog, we can assign four different states to the individual bits in our data. These different states are shown in the table below.

We require the high impedance and unknown states to more accurately represent the underlying hardware we are describing in verilog.

However, a number of 2 state data types were introduced as a part of the SystemVerilog extension.

These types are intended to be simpler, more efficient versions of the 4 state types from verilog.

When we use the 2 state data types in SystemVerilog, we can only assign a logical 1 or 0 to the individual bits. Therefore, we should only use these data types in SystemVerilog testbenches.

As they have half as many states, the SystemVerilog 2 state types use half as much memory as the 4 state verilog types.

As a result of this, the 2 state types have faster execution times than their equivalent 4 state type.

The table below shows all of the different types which we can use in SystemVerilog. It also shows which types use the old verilog 4 state encoding and which use 2 state encoding.

Each of these types is described in more depth in the following sections.

Basic SystemVerilog Data Types

Broadly speaking, we can split the SystemVerilog types into two distinct families.

One of these families consists of types which are used to model basic binary data in our code.

We mainly use these types to model simple logic components, connections and busses.

The second family consists of types which we use to model numerical data.

We use these types to model either whole or decimal numbers in our SystemVerilog code.

Regardless of the exact type of data we are using, we always use the same syntax to declare a variable in SystemVerilog. The code snippet below shows this general syntax.

We use the <type_name> field in the above example to declare the type of variable we have. We simply replace this field with the name of the type.

The <size> field is used to declare how many bits are in our variable. We discuss the format of the <size> field in more detail in the section on SystemVerilog vector types .

Finally, we use the <variable_name> field to give a unique name to our variable.

As an example, the verilog code below declares an integer type variable and assigns it a value of 100.

Let's look at all of the basic SystemVerilog types in more detail.

Binary Data Types

In verilog, we can split the binary data types into groups - net types and variables types.

We use these two different groups to model different elements of our digital circuits.

We use the net types to model point to point connections in our digital circuits. They are unable to store values on their own and must be driven with data.

We primarily use the variable types to model registers or flip flops in our design. These types can store data, meaning that their behaviour is similar to variables in other programming languages such as C.

Both of these families contain a number of different types which we can use in our code.

However, the most commonly used of the net types is the wire type whilst the most commonly used variable type is the reg type.

Therefore, we will only consider the reg and wire types in this pos. However, all of the types available in verilog are discussed in more detail in the post on verilog data types .

The subtle differences between the reg and wire types often leads to confusion for new verilog designers.

In fact, this can even be difficult for experienced developers to fully understand.

Therefore, the logic type was introduced in SystemVerilog. This type is intended to replace both the reg and wire types from verilog.

We can still use the verilog reg and wire types to model binary data in our SystemVerilog designs. However, it is much simpler to use the basic logic type.

In the rest of this section, we discuss these three different data types in more detail.

Verilog Wire Type

In verilog we use the wire type to describe physical connections between different components in our design. As a result of this, the wire type can't be used to store data values or drive data.

To better demonstrate when we would use the wire type in a verilog design, consider the circuit diagram shown below.

In this circuit, we would use the wire type to connect the output of the multiplexor to the input of the flip flop.

We normally use continuous assignment to drive data onto a wire type. To do this we must use the assign keyword, as shown in the code snippet below. We talk about continuous assignment in more detail in a later blog post.

We can not use the wire type in procedural code such as always blocks. The always block is also discussed in more detail in a later blog post.

Verilog Reg Type

Unlike the wire type, we use the reg type to store data values in our verilog designs. When we assign a value to a reg type, it maintains this until it is assigned a new value.

The reg type is generally more intuitive to understand than the wire type as the behavior is similar to variables in other programming languages such as C.

We most commonly use the reg type to model the behaviour of flip flops in verilog. However, the reg type can also be used to model combinational logic circuits in some cases.

To better demonstrate when we would use the reg type in a verilog design, consider the circuit diagram shown below.

In this circuit, we would use the reg type to model the flip flop output as it effectively stores a single bit of data.

We must use the reg type within blocks of procedural code such as an always block, as shown in the code snippet below.

SystemVerilog logic Type

As the difference between reg and wire types often causes confusion, a new binary data type was introduced in SystemVerilog.

The logic type was introduced as a replacement for both the reg and wire types.

As the logic type combines the characteristics of the reg and wire types, we can use it in pretty much any part of our SystemVerilog code.

This is in contrast to the reg and wire types which can only be used in procedural blocks and continuous assignment respectively.

All of this makes the logic type much more flexible. As a result, it is easier to understand and work with.

As a general rule, we should use the logic type for any binary signal in our SystemVerilog design.

The only exception to this is when we want to drive a signal from more than one source.

As it's illegal to drive a logic type from more than one source, we would have to use a wire type for this.

To demonstrate how we use the logic type, let's consider a basic example.

For this example, we will look at the simple example circuit below. This is the same circuit we previously looked at when discussing the reg type.

As we saw in the previous example, we could use a reg type to model the flip flop output.

In addition to this, we could also model the multiplexor output with a wire type.

However, when we write code in SystemVerilog it is much easier to use the logic type to model both of these signals.

The code snippet below shows how we would model this circuit using the logic type in SystemVerilog.

SystemVerilog bit Type

In addition to the logic type, the bit type was also introduced as a part of the SystemVerilog language.

We can use the bit type in either procedural blocks or in continuous assignment.

This behavior means that the bit type is almost identical to the logic type which we previously discussed.

However, there is one important difference between these two data types.

Unlike the logic type, the bit type uses 2 states rather than 4.

As a result of this, we can't use this type to model unknown state or high impedance.

However, the bit type uses half the amount of memory that the logic type requires as it has less states. This can speed up the execution time of our simulations.

These characteristics mean that the bit type is less suitable than the logic type for SystemVerilog designs.

However, we should use the bit type rather than the logic type in our SystemVerilog testbenches due to its faster execution time.

Vector Types in SystemVerilog

All of the examples which we have looked at so far have consisted of a single bit of data.

However, we often use data busses to transfer data within a digital circuit.

In SystemVerilog, we can use vector types to create data buses. This allows us to declare a signal which has more than one bit.

The code snippet below shows the general syntax which we use to declare a vector type in SystemVerilog.

When we define the size of the vector we must specify the most significant and least significant bits (MSB and LSB). Therefore, the <size> field takes the form [MSB:LSB].

For example, to declare a 4 bit little endian type vector we would use the construct [3:0].

As we talked about earlier in this post, we can represent data using binary, hex, octal or decimal formats. When we assign data to a vector we can use any of these representations.

The SystemVerilog code below shows how we would declare a 4 bit wide logic type. We also see how we can use the different data representations to assign the value of 1010b to the variable.

Numeric Data Types

The types which we have considered so far in this post are all used to model digital data.

However, we can also represent data numerically in our SystemVerilog designs.

In SystemVerilog, we can group the numerical data types into two families.

The first family consists of types which are used to represent whole numbers.

The most commonly used types in this family are the integer and int type.

The second family consists of types which are used to model decimal numbers in SystemVerilog.

There are considerably less types in this family but the most commonly used of these types is the real type.

Let's take a closer look at all of the numerical types available to us in SystemVerilog.

Verilog Integer Type

The most commonly used type for numerical data in verilog is the integer type.

We normally use this type for internal signals rather than for module ports. 

By default, the integer is a 32 bit 2s complement number which we can use to represent any whole number in our SystemVerilog design. When we use an integer type, we assign numerical rather than binary values to the variable.

As we can also assign numeric values to the reg and logic types, we typically use integers for constants or loop variables in SystemVerilog.

Our synthesis tools will automatically trim any unused bits in our integer type. For example, if we declare an integer constant with a value of 255 then our synthesis tool will trim this down to 8 bits.

The code snippet below shows how we declare and assign an integer type in SystemVerilog.

We can also use the unsigned keyword to change the encoding of an integer type signal from signed to unsigned.

The code snippet below shows how we would declare an unsigned integer type in SystemVerilog.

SystemVerilog int Type

By default, the int type is a 32 bit signed number which we can use to model whole numbers in SystemVerilog.

The int type was introduced as a part of the SystemVerilog extension and it is virtually identical to the verilog integer type.

However, the key difference between the integer and int types is that the int type uses only 2 states. Therefore, we can treat the SystemVerilog int type as being exactly equivalent to the C int type .

As the int type only uses 2 states, we can't use this type to model high impedance or unknown states.

This means that the 4 state integer type is generally preferable for FPGA designs as it gives a more realistic model of the underlying hardware.

However, the int type has faster execution times that the integer type because it only requires half the amount of memory.

Therefore, we should generally use the int type instead of the integer type within our SystemVerilog testbenches.

The code snippet below shows how we would declare and use the int type in SystemVerilog.

We can also use the unsigned keyword to change the encoding of an int type signal from signed to unsigned.

The code snippet below shows how we would declare an unsigned int type in SystemVerilog.

SystemVerilog byte Type

The SystemVerilog byte type is an 8 bit data type which we can use to model whole numbers.

By default, the byte type is is encoded as a signed 2s complement number. As a result of this, it can only accept values from -127 to 127.

However, we can also declare the the byte type to be encoded as an unsigned number. When we do this, we can assign values from 0 to 255 to the byte type.

Like the int type, the byte type only uses 2 states. As a result of this, we should avoid using this type in SystemVerilog based designs.

However, we should use the byte type in our testbenches as it uses less memory and can reduce execution times.

The code snippet below shows how we declare and use the byte type in SystemVerilog.

We can also use the unsigned keyword to change the encoding of a byte type signal from signed to unsigned.

The code snippet below shows how we declare an unsigned byte type in SystemVerilog.

SystemVerilog shortint type

The SystemVerilog shortint type is a 16 bit data type which we can use to model whole numbers.

By default, the shortint type is encoded as a signed 2s complement number. As a result of this, it can only accept values from -32767 to 32767.

However, we can also declare the the shortint type to be encoded as an unsigned number. When we do this, we can assign values from 0 to 65535 to the shortint type.

Like the int type, the shortint type only has 2 states. As a result of this, we should avoid using this type in SystemVerilog based designs.

However, we should use the shortint type in our testbenches as it uses less memory and can reduce execution times.

The code snippet below shows how we declare and use the shortint type in SystemVerilog.

We can also use the unsigned keyword to change the encoding of a shortint type signal from signed to unsigned.

The code snippet below shows how we declare an unsigned shortint type in SystemVerilog.

SystemVerilog longint type

The final 2 state integer type which was introduced as part of the SystemVerilog language is the longint type.

The longint type uses 64 bits and is encoded as a signed 2s complement number.

We can use the longint type to store numbers which are too large to store in either the int or integer types.

As the longint type uses 2 states, it is generally only used within SystemVerilog testbenches.

The code snippet below shows how we declare and use the longint type in SystemVerilog.

We can also use the unsigned keyword to change the encoding of a longint type signal from signed to unsigned.

The code snippet below shows how we declare an unsigned longint type in SystemVerilog.

Verilog real Type

In verilog, the second most commonly used numerical type is the real type.

We use this type to store non-integer numbers, i.e. numbers which also have a decimal part.

The real type is implemented as a 64 bit IEEE 754 floating point number in SystemVerilog.

As a result of this, it can't be directly synthesized and we typically only use the real type in our testbench code.

We can use either decimal or engineering type notation to assign values to the real type.

The code snippet below shows how we declare a real type and assign data to it.

The real type is equivalent to the double type in C .

SystemVerilog shortreal Type

In addition to the real type, the SystemVerilog extension introduced the shortreal type.

As with the real type, we use this type to store non-integer numbers.

The difference between the shortreal and the real type is that the shortreal only uses 32 bits.

The shortreal is implemented as an IEEE 754 single precision floating point number in SystemVerilog.

As a result of this, we can't use the shortreal type in synthesizable code.

We can use either decimal or engineering type notation to assign values to the shortreal type.

The code snippet below shows how we declare a shortreal type and assign data to it.

We can think of the shortreal type as being equivalent to the float type in C .

What is the difference between types which use 4 states and types which use 2 states? When we would use a 2 state type instead of a 4 state type.

The 2 state type can't be used to model high impedance or unknown signals. The 2 state types are more suitable for testbenches as they have faster execution times.

Which types of data can we represent in our SystemVerilog design?

Binary, hexidecimal, octal and whole decimal numbers. We can also represent decimal numbers but this is not synthesizable.

What is the difference between the verilog reg and wire types?

The wire type is used to model connections in our design and canā€™t store values. The reg type can store data values and behaves like variables in other programming languages.

Write some SystemVerilog code to declare an 8 bit logic type and assign it the value of AAh

Write the code to declare an unsigned int type and assign the value of 100 to it.

How to Write a Basic Module in SystemVerilog

This post is the first in a series which discusses how SystemVerilog is used in FPGA design. We begin by looking at the way we structure a SystemVerilog design using the module keyword. This includes a discussion of parameters , ports and instantiation as well as a full example .

SystemVerilog is actually a superset of the Verilog language. As a result of this, if you have already read the post on verilog modules then most of this post should already be familiar. The only exception to this is the section on nested modules . These are a new feature which were introduced as a part of the SystemVerilog language.

Structuring SystemVerilog Designs

When we design FPGAs, it is important to remember one fundamental principle ā€“ we are designing hardware and not writing a computer program.

Therefore, we must describe the behavior of a number of different components which we then connect together. This expectation is reflected in the way that we structure our SystemVerilog design files.

As with every electronic component, we need to know the external interface to our component. This information allows us to connect it to other components in our system.

We also need to know how the component behaves so that we can use it in our system.

In SystemVerilog, we use a construct called a module to define this information. The SystemVerilog module is equivalent to the entity architecture pair in VHDL .

The code snippet below shows the general syntax for the declaration of a module in SystemVerilog.

In this construct, <module_name> would be the name of the module which is being designed. Although we can declare a number of modules in a single file, it is good practise to have one file corresponding to one module.

It is also good practice to keep the name of the file and the module the same. This makes it simpler to manage large designs with many components. 

We don't need to include the <module_name> field after the endmodule keyword. Although this can make the code easier to follow, it is optional and is often omitted.

In SystemVerilog we use the // characters to denote that we are writing a comment .

We use comments to include important information about our code which others may find useful. The SystemVerilog compiler ignores anything which we write in our comments.

In the SystemVerilog code snippet above, we can see this in practice as comments are used to describe the functionality of the code.

Parameters in SystemVerilog Modules

Parameters are a local form of constant which we can use to configure a module in SystemVerilog.

When we instantiate our module in another part of our design, we can assign the parameters a value to configure the behavior of the module.

As parameters have a limited  scope , we can call the same module multiple times and assign different values to the parameters each time.

Therefore, parameters allow us to modify the behaviour of our module on the go.

Parameters are an optional part of the SyetmVerilog module declaration and in the majority of cases we won't need to include them.

However, parameters allow us to write more generic module interfaces which are easier to  reuse  in other SystemVerilog designs.

After we have declared a parameter in our module, we can use it in the same way as a normal variable.

However, we must remember that it is a constant value so we can only read it. As a result of this, we can only assign a value to the parameter when it is declared.

We discuss SystemVerilog parameters in more depth in a later post.

Functional Code

We use the space underneath the module IO declaration to define how our module functions.

We most commonly use RTL for this but we can also write structural code or describe primitives.

These topics are discussed in more detail in later SystemVerilog tutorials .

When we have finished writing the code which describes the behaviour of our module, we use the endmodule keyword.

Any code which we write after this keyword will not be included in our module.

SystemVerilog Module Ports

We use ports within the module declaration to define the inputs and output of a SystemVerilog module.

We can think of module ports as being equivalent to pins in a traditional electronic component.

The code snippet below shows the general syntax we use to declare ports.

The <port_name> field in the module declaration is used to give a unique name to the port.

We use the <direction> field in the above construct to declare our ports as either input, output or inout. This correspond to inputs, outputs and bidirectional ports respectively. 

We use the <data_type> field to declare the type of data the port expects.

The logic type was introduced in SystemVerilog to replace the verilog reg and wire types. This is the most commonly used type in SystemVerilog and we use this type to represent any binary data.

There are several other data types which we can use in SystemVerilog and we discuss these in more detail in the next post.

We may also wish to use multi bit,  vector  type ports in our module. When this is the case, we can use the <size> field to declare the number of bits in the port.

When we define the size of a vector type, we must indicate the most significant and least significant bit (MSB and LSB) in the vector. Therefore, we use the construct [MSB:LSB] when declaring the size of a port.

The example below shows the declaration of an 8 bit little-endian input called example_in.

Little endian data is the most commonly used convention in FPGA design. In this example, bit 7 is the most significant bit.

We could also define the MSB as being in position 0 if we declare the size field as [0:7]. This convention, which is known as big-endian data, is used less frequently when designing FPGAs.

SystemVerilog Module Instantiation

We can invoke a SystemVerilog module which we have already written in another part of a design. This process of invoking modules in SystemVerilog is known as instantiation.

Each time we instantiate a module, we create a unique object which has its own name, parameters and IO connections.

In a SystemVerilog design, we refer to every instantiated module as an instance of the module. We use instantiation to create a number of different instances which we use to build a more complex design.

We can think of module instantiation in SystemVerilog as being equivalent to placing a component in a traditional electronic circuit.

Once we have created all of the instances we require in our design, we must interconnect them to create a complete system. This is exactly the same as wiring components together in a traditional electronic system.

SystemVerilog provides us with two methods we can use for module instantiation - named instantiation and positional instantiation .

Positional Module Instantiation

When using the positional instantiation approach in SystemVerilog, we use an ordered list to connect the module ports. The order of the list we use must match the order in which the ports were declared in our module.

As an example, if we declare the clock first, followed by a reset then we must connect the clock signal to the module IO first.

The SystemVerilog code snippet below shows the general syntax for positional module instantiation.

The <module_name> field must match the name we gave the module when it was declared.

We use the <instance_name> field to give a unique name to an instantiated module in our design.

As the order of our ports may change as our design evolves, this method can be difficult to maintain.

Positional Module Instantiation Example

Let's consider a basic practical example to show how we use positional instantiation in practise.

For this example, we will create an instance of the simple circuit shown below.

When we use positional instantiation, the order of the ports in the module declaration is important. The code snippet below shows how we would declare a module for this circuit.

Finally, the SystemVerilog code snippet below shows how we would create an instance of this module using positional instantiation.

Named Module Instantiation

When we use named module instantiation in SystemVerilog, we explicitly define the name of the port we are connecting our signal to.

Unlike positional instantiation, the order in which we declare the ports is not important.

This method is generally preferable to positional instantiation as it produces code which is easier to read and understand.

It is also easier to maintain as we can modify ports without having to worry about the order in which we declare them.

The SystemVerilog code snippet below shows the general syntax for named module instantiation.

The <module_name>, <parameter_name> and <port_name> fields must match the names we used when defining the module.

The <instance_name> has the same function for both positional and named instantiations.

Named Module Instantiation Example

Let's consider a basic practical example to show how we use named instantiation in practise.

For this example, we will create an instance of the simple circuit shown below. This is the same circuit we previously used in the positional instantiation example.

The SystemVerilog code snippet below shows how we would create an instance of this module using named instantiation.

Nested Modules in SystemVerilog

When we design an FPGA using verilog, we can not declare a module inside of another module.

Instead, we must declare a module in another part of our design and only then can we instantiate it inside another module.

However, this changed in SystemVerilog and we can now write nested modules. As a result, we can declare a module inside of another module in SystemVerilog.

The code snippet below shows the method we use to declare a nested module in SystemVerilog.

Both the modules in this example use the same syntax which we talked about in the section on SystemVerilog modules .

When we write a nested module, the scope is limited to the module we declared it in. As a result, we can only instantiate a nested module inside of the module it is contained in.

The nested module also has visibility of all the signals in the outer module. However, the signals contained in the nested module are not visible to the outer module.

Nested Module Example

To better demonstrate how we use nested modules in SystemVerilog, let's consider a basic example.

For this example, we will write a module to implement the simple circuit shown below. We used this same circuit in our previous examples for named and positional association.

In order to show how we use nested modules, we will write the and gate as a separate module.

The SystemVerilog code below shows how we would structure this circuit using a nested module.

In this example, we can see how we declare the top level of the module using the same method we discussed before . We have labeled the top level module or_and in this example.

We then declare a section module inside of the top level module which we would use to implement an and gate. In this example, we have labeled the nested module and_gate.

We would still need to instantiate the nested module inside of the top level if we wanted to connect it to our design. If we don't do this, our nested module is simply ignored by our SystemVerilog tools.

SystemVerilog Module Example

In order to fully understand all of the concepts which we have discussed in this post, let's look at a basic example.

In this example, we will create a synchronous counter circuit which uses a parameter and then instantiate two instances of it. One of these instantiations will have 12-bits in the output whilst the other will have only 8 bits.

We will exclude the RTL for these modules here as we have not yet learnt how to write this. Instead we will simply define the IO of our modules and the interconnection between them.

The counter module will have two inputs - clock and reset - and a single output - the counter value.

In addition to this, we will also require a single parameter which we will use to define the number of bits in the output.

The SystemVerilog code snippet below shows the declaration of our counter module.

We now need a module which we can use to instantiate two instances of this counter. This module will have two inputs - clock and reset - and two outputs coming from the instantiated counters.

In the counter module, we defined the default counter output as 8 bits. This means that we can instantiate the 8 bit counter without overriding the parameter value.

However, when we instantiate the 12 bit counter, we must also override the value of the WIDTH parameter and set it to 12.

The code snippet below shows the code for this module when using named instantiation to connect to the ports.

What do we use a module for in SystemVerilog?

We use modules to define the behaviour of a component in SystemVerilog.

List the three different types of direction a port can have.

Inputs (input keyword), outputs (output keyword) and bidirectional (inout keyword).

What is the difference between named and positional instantiation? Which one is easier to maintain and why?

We use an ordered list to connect ports when using positional instantiation. In contrast to this, we explicitly define the port we are connecting to when we use named instantiation. Named instantiation is easier to maintain as the code is not affected if we change the order of the ports int he module declaration.

Which feature was added to SystemVerilog modules which is not available in verilog?

In SystemVerilog we can declare a module inside of another module (nested modules). However, we can't use nested modules in verilog.

Write a module declaration for the circuit shown below.

How to Write a Basic Verilog Testbench

In this post we look at how we use Verilog to write a basic testbench. We start by looking at the architecture of a Verilog testbench before considering some key concepts in verilog testbench design. This includes modelling time in verilog , the initial block , verilog-initial-block and the verilog system tasks . Finally, we go through a complete verilog testbench example .

When using verilog to design digital circuits, we normally also create a testbench to stimulate the code and ensure that it functions as expected.

We can write our testbench using a variety of languages, with VHDL , Verilog and System Verilog being the most popular.

System Verilog is widely adopted in industry and is probably the most common language to use. If you are hoping to design FPGAs professionally, then it will be important to learn this skill at some point.

As it is better to focus on one language as a time, this blog post introduces the basic principles of testbench design in verilog. This allows us to test designs while working through the verilog tutorials on this site.

If you are interested in learning more about testbench design using either  verilog  or  SystemVerilog , then there are several excellent courses paid course available on sites such as  udemy .

Architecture of a Basic Testbench

Testbenches consist of non- synthesizable verilog code which generates inputs to the design and checks that the outputs are correct.

The diagram below shows the typical architecture of a simple testbench.

The stimulus block generates the inputs to our FPGA design and the output checker tests the outputs to ensure they have the correct values.

The stimulus and output checker will be in separate files for larger designs. It is also possible to include all of these different elements in a single file. 

The main purpose of this post is to introduce the skills which will allow us to test our solutions to the exercises on this site.

Therefore, we don't discuss the output checking block as it adds unnecessary complexity.

Instead, we can use a simulation tool which allows for waveforms to be viewed directly. The freely available software packages from Xilinx ( Vivado ) and Intel ( Quartus ) both offer this capability.

Alternatively, open source tools such as icarus verilog can be used in conjunction with GTKWave to run verilog simulations. 

We can also make use of EDA playground which is a free online verilog simulation tool.

In this case, we would need to use system tasks to monitor the outputs of our design. This gives us a textual output which we can use to check the state of our signals at given times in our simulation.

Instantiating the DUT

The first step in writing a testbench is creating a verilog module which acts as the top level of the test.

Unlike the verilog modules we have discussed so far, we want to create a module which has no inputs or outputs in this case. This is because we want the testbench module to be totally self contained.

The code snippet below shows the syntax for an empty module which we can use as our testbench.

After we have created a testbench module, we must then instantiate the design which we are testing. This allows us to connect signals to the design in order to stimulate the code.

We have already discussed how we instantiate modules in the previous post on verilog modules . However, the code snippet below shows how this is done using named instantiation.

Once we have done this, we are ready to start writing our stimulus to the FPGA. This includes generating the clock and reset, as well creating test data to send to the FPGA.

In order to this we need to use some verilog constructs which we have not yet encountered - initial blocks , forever loops and time consuming statements.

We will look at these in more detail before we go through a complete verilog testbench example .

Modelling Time in Verilog

One of the key differences between testbench code and design code is that we don't need to synthesize the testbench.

As a result of this, we can use special constructs which consume time. In fact, this is crucial for creating test stimulus.

We have a construct available to us in Verilog which enables us to model delays. In verilog, we use the # character followed by a number of time units to model delays.

As an example, the verilog code below shows an example of using the delay operator to wait for 10 time units.

One important thing to note here is that there is no semi-colon at the end of the code. When we write code to model a delay in Verilog, this would actually result in compilation errors.

It is also common to write the delay in the same line of code as the assignment. This effectively acts as a scheduler, meaning that the change in signal is scheduled to take place after the delay time.

The code snippet below shows an example of this type of code.

Timescale Compiler Directive

So far, we have talked about delays which are ten units of time. This is fairly meaningless until we actually define what time units we should use.

In order to specify the time units that we use during simulation, we use a verilog compiler directive which specifies the time unit and resolution. We only need to do this once in our testbench and it should be done outside of a module.

The code snippet below shows the compiler directive we use to specify the time units in verilog.

We use the <unit_time> field to specify the main time unit of our testbench and the <resolution> field to define the resolution of the time units in our simulation.

The <resolution> field is important as we can use non-integer numbers to specify the delay in our verilog code. For example, if we want to have a delay of 10.5ns, we could simply write #10.5 as the delay.

Therefore, the <resolution> field in the compiler directive determines the smallest time step we can actually model in our Verilog code.

Both of the fields in this compiler directive take a time type such as 1ps or 1ns.

Verilog initial block

In the post on always blocks in verilog , we saw how we can use procedural blocks to execute code sequentially .

Another type of procedural block which we can use in verilog is known as the initial block.

Any code which we write inside an initial block is executed once, and only once, at the beginning of a simulation.

The verilog code below shows the syntax we use for an initial block.

Unlike the always block, verilog code written within initial block is not synthesizable . As a result of this, we use them almost exclusively for simulation purposes.

However, we can also use initial blocks in our verilog RTL to initialise signals.

When we write stimulus code in our verilog testbench we almost always use the initial block.

To give a better understanding of how we use the initial block to write stimulus in verilog, let's consider a basic example.

For this example imagine that we want to test a basic two input and gate.

To do this, we would need code which generates each of the four possible input combinations.

In addition, we would also need to use the delay operator in order to wait for some time between generating the inputs.

This is important as it allows time for the signals to propagate through our design.

The verilog code below shows the method we would use to write this test within an initial block.

Although we haven't yet discussed loops, they can be used to perform important functions in Verilog. In fact, we will discuss verilog loops in detail in a later post in this series

However, there is one important type of loop which we can use in a verilog testbench - the forever loop.

When we use this construct we are actually creating an infinite loop . This means we create a section of code which runs contimnuously during our simulation.

The verilog code below shows the syntax we use to write forever loops.

When writing code in other programming languages, we would likely consider an infinite loop as a serious bug which should be avoided.

However, we must remember that verilog is not like other programming languages. When we write verilog code we are describing hardware and not writing software.

Therefore, we have at least one case where we can use an infinite loop - to generate a clock signal in our verilog testbench.

To do this, we need a way of continually inverting the signal at regular intervals. The forever loop provides us with an easy method to implement this.

The verilog code below shows how we can use the forever loop to generate a clock in our testbench. It is important to note that any loops we write must be contained with in a procedural block or generate block .

Verilog System Tasks

When we write testbenches in verilog, we have some inbuilt tasks and functions which we can use to help us.

Collectively, these are known as system tasks or system functions and we can identify them easily as they always begin wtih a dollar symbol.

There are actually several of these tasks available. However, we will only look at three of the most commonly used verilog system tasks - $display, $monitor and $time.

The $display function is one of the most commonly used system tasks in verilog. We use this to output a message which is displayed on the console during simulation.

We use the $display macro in a very similar way to the printf function in C .

This means we can easily create text statements in our testbench and use them to display information about the status of our simulation.

We can also use a special character (%) in the string to display signals in our design. When we do this we must also include a format letter which tells the task what format to display the variable in.

The most commonly used format codes are b (binary), d (decimal) and h (hex). We can also include a number in front of this format code to determine the number of digits to display.

The verilog code below shows the general syntax for the $display system task. This code snippet also includes an example use case.

The full list of different formats we can use with the $display system task are shown in the table below.

The $monitor function is very similar to the $display function, except that it has slightly more intelligent behaviour.

We use this function to monitor the value of signals in our testbench and display a message whenever one of these signals changes state.

All system tasks are actually ignored by the synthesizer so we could even include $monitor statements in our verilog RTL code, although this is not common.

The general syntax for this system task is shown in the code snippet below. This code snippet also includes an example use case.

The final system task which we commonly use in testbenches is the $time function. We use this system task to get the current simulation time.

In our verilog testbenches, we commonly use the $time function together with either the $display or $monitor tasks to display the time in our messages.

The verilog code below shows how we use the $time and $display tasks together to create a message.

Verilog Testbench Example

Now that we have discussed the most important topics for testbench design, let's consider a compete example.

We will use a very simple circuit for this and build a testbench which generates every possible input combination.

The circuit shown below is the one we will use for this example. This consists of a simple two input and gate as well as a flip flip.

1. Create a Testbench Module

The first thing we do in the testbench is declare an empty module to write our testbench code in.

The code snippet below shows the declaration of the module for this testbench.

Note that it is good practise to keep the name of the design being tested and the testbench similar. Normally this is done by simply appending _tb or _test to the end of the design name.

2. Instantiate the DUT

Now that we have a blank testbench module to work with, we need to instantiate the design we are going to test.

As named instantiation is generally easy to maintain than positional instantiation, as well as being easier to understand, this is the method we use.

The code snippet below shows how we would instantiate the DUT, assuming that the signals clk, in_1, in_b and out_q are declared previously.

3. Generate the Clock and Reset

The next thing we do is generate a clock and reset signal in our verilog testbench.

In both cases, we can write the code for this within an initial block. We then use the verilog delay operator to schedule the changes of state.

In the case of the clock signal, we use the forever keyword to continually run the clock signal during our tests.

Using this construct, we schedule an inversion every 1 ns, giving a clock frequency of 500MHz.

This frequency is chosen purely to give a fast simulation time. In reality, 500MHz clock rates in FPGAs are difficult to achieve and the testbench clock frequency should match the frequency of the hardware clock.

The verilog code below shows how the clock and the reset signals are generated in our testbench.

4. Write the Stimulus

The final part of the testbench that we need to write is the test stimulus.

In order to test the circuit we need to generate each of the four possible input combinations in turn. We then need to wait for a short time while the signals propagate through our code block.

To do this, we assign the inputs a value and then use the verilog delay operator to allow for propagation through the FPGA.

We also want to monitor the values of the inputs and outputs, which we can do with the $monitor verilog system task.

The code snippet below shows the code for this.

Full Example Code

The verilog code below shows the testbench example in its entirety.

When using a basic testbench architecture which block generates inputs to the DUT?

The stimulus block is used to generate inputs to the DUT.

Write an empty verilog module which can be used as a verilog testbench.

Why is named instantiation generally preferable to positional instantiation.

It is easier to maintain our code as the module connections are explicitly given.

What is the difference between the $display and $monitor verilog system tasks.

The $display task runs once whenever it is called. The $monitor task monitors a number of signals and displays a message whenever one of them changes state,

Write some verilog code which generates stimulus for a 3 input AND gate with a delay of 10 ns each time the inputs change state.

Using the Always Block to Model Sequential Logic in Verilog

In this post, we discuss one of the most important constructs in verilog - the always block .

As we discussed in the post on verilog operators , there are two main classes of digital circuit which we can model in verilog ā€“  combinational  and  sequential .

In contrast to combinational logic, sequential circuits use a clock and require storage elements such as flip flops .

As a result, the output signals are synchronised to the circuit clock and changes do not occur immediately. 

We use the always block to write code which executes sequentially in verilog. This is crucial when describing sequential logic circuits in verilog.

As always, there are a number of exercises at the end of this post.

However, it is worth reading the blog on writing a basic testbench in verilog before tackling these exercises. This will allow us to simulate some of the circuits which we design in these exercises.

The Always Block in Verilog

When we write verilog, we use procedural blocks to create statements which are executed sequentially. Procedural blocks are particularly important for the modelling of sequential digital circuits.

In contrast, verilog continuous assignment statements execute concurrently (i.e. in parallel) in our designs. This matches the nature of the underlying circuits, which consist of a number of separate logic gates.

The always block is one of the most commonly used procedural blocks in verilog. Whenever one of the signals in the sensitivity list changes state, all of the statements in the always block execute in sequence.

The verilog code below shows the general syntax for the always block. We talk about the sensitivity list in more depth in the next section.

We need to be careful when using this construct as there are some features which are unique to verilog.

In particular, beginners often find it difficult to understand the way that signals are updated in an always block.

When we use always blocks, we can update the value of our signals either in parallel or sequentially. This depends on whether we use blocking or non-blocking assignment , which we discuss in more depth later in this post.

In order to be an effective verilog designer, it is important that we have a good understanding of the always block.

Let's look at some of the key features of the always block in more detail.

Sensitivity Lists

Any code which we write within an always block runs continuously. This means that the statements within the code block are executed in sequence until we reach the last line.

Once the last line in the sequence has been executed, the program then loops back to the first line. All of the statements in our always block are then executed in sequence again.

However, this behaviour is not representative of a real circuit which will remain in a steady state until one of the input signals changes state.

We use the sensitivity list in the alway block to emulate this behaviour.

To do this, the code within the always block will only execute after one of the signals in the sensitivity list changes state.

Flip Flop Example

Letā€™s consider how we would model a basic D type flip flop using the always block as an example.

As with all clocked flip flops, the output of a D type flip flop only changes state when there is a positive clock edge.

As a result of this, we include the clock signal in the sensitivity list so that the always block only executes when there is a rising edge on the clock signal.

The verilog code below shows how we would model a D type flip flop using the always block.

In this code example, we use the posedge macro to determine when there is a transition from 0 to 1.

The single line of code within the always block is executed when this macro evaluates as true. This line of code assigns the value of D to the output signal (Q).

When we use the posedge macro in verilog, all other changes of state are simply ignored. This is exactly what we would expect from a D type flip flop.

Verilog also has a negedge macro which has the opposite functionality. When we use this macro, the always block will execute whenever the clock changes from 1 to 0.

We can also omit this macro altogether. In this case, the code executes whenever a signal in the sensitivity list changes state.

We should only ever use the posedge macro for clock signals in our verilog design. This is because synthesis tools will attempt to utilize clock resources within the FPGA to implement it.

Multiple Signals in a Sensitivity List

There are instances when we will want to include more than one signal in the sensitivity list.

A common example of this is when we write code to model the behaviour of flip flops with asynchronous resets .

When this is the case, we need the flip flop model to perform an action whenever the reset or clock signals change state.

To do this we simply list both of the signals inside the sensitivity list and separate them with a comma.

The code snippet below shows how we write such a flip flop.

As this example uses an active high reset, we again use the posedge macro in the sensitivity list.

An active high reset means that the reset is only active when it is equal to one.

We then use a construct known as an if statement to determine whether the always block triggered by the reset signal or the clock signal.

We will discuss the verilog if statement in a future blog post, although itā€™s functionality is fairly self explanatory.

When working with code which is verilog 1995 compatible we must separate the signals in the sensitivity list using the or keyword instead or a comma.

The code snippet below shows how we would model an asynchronously resettable flip flop using verilog 1995.

Blocking and Non-Blocking Assignment in Verilog

In the code examples we have seen so far in this series of posts, we have used two different types of assignment operators.

This is because verilog has two different types of assignment ā€“ blocking and non-blocking.

When we write code with non-blocking assignments we use the <= symbol whilst blocking code uses the = symbol.

When we use continuous assignment in verilog , we can only use blocking assignment.

However, we can use both types of assignment in procedural block.

Blocking assignment typically results in our synthesis tools implementing combinational logic circuits. In contrast, non-blocking assignment normally results in sequential circuits after synthesis.

Blocking assignment is the simplest of the two techniques to understand. When we assign signals using blocking assignment in verilog, our signals update their value as soon as the line of code is executed.

We commonly use this type of assignment to write combinational logic in verilog. However, in some circumstances we can use it to create sequential circuits.

In contrast, signals which use the non-blocking technique are not updated immediately after assignment. Instead, verilog uses assignment scheduling to update the values.

This is a little tricky to understand, so letā€™s consider it in a bit more depth.

Assignment Scheduling

When we write verilog code using non-blocking assignment, our code still executes sequentially. However, the signals which we are assigning do not get updated in this way.

To demonstrate why this is the case, letā€™s consider the twisted ring counter circuit below.

First, letā€™s look at the behaviour if the signals did update immediately.

If we assume that the output of both flip flops is 0b when a clock edge occurs, then the second line in the code will set the output of DFF1 to 1b.

We can then see that the line of code immediately beneath this would set the output of DFF2 to 1. This is clearly not the intended behaviour of this circuit.

To overcome this issue, non blocking assignment in verilog uses scheduled assignment.

As a result, changes to our signals donā€™t occur immediately after assignment but are instead scheduled to occur at some point in the future.

Normally, the signals update their value at the end of a simulation cycle. This refers to the time it takes the simulator to execute all of the code for a given time step.

To better demonstrate the way scheduled assignment works, letā€™s again consider the simple dual flip flop circuit.

When a rising edge is detected, the simulator firstly executes the statement to update DFF1. Once this line has been executed, an update to the output of DFF1 is scheduled to happen.

The simulator then runs the second line of code, this time using the original value of the DFF1 flip flop and schedules the update of DFF2. 

As there are only two statements in this design, the simulation cycle is now complete. At this point, all of the scheduled changes are applied and the values are updated for both flip flops.

Synthesis Example

To further demonstrate the difference between blocking and non blocking assignments in verilog, we will again model a basic two flip flop twisted ring counter circuit. The code snippet below shows he implementation of this circuit.

However, we can also look at the output of a synthesis tool such as vivado to see a diagram of the resulting circuit. The circuit diagram below shows this circuit.

We can see that there are two flip flops in the circuit whilst the not gate is implemented using LUT1.

Now letā€™s take at look at the circuit we would get if we used blocking assignment in the code.

The verilog code below shows how we could (incorrectly) attempt to model this circuit using blocking assignment.

This results in the circuit shown below after synthesis.

We can see from this that using non blocking has resulted in the removal of the second flip flop from our circuit.

The reason for this should be fairly obvious, given what we have learnt about blocking assignment so far.

As the value of the q_dff2 is immediately assigned to the same value as q_dff1, our circuit model does not imply that there should be a flip flop in this signal path.

This example actually shows us one of the most important differences between blocking and non blocking assignment in verilog.

When we use non-blocking assignment, the synthesis tool will always place a flip flop in the circuit. This means that we can only use non blocking assignment to model sequential logic.

In contrast, we can use blocking assignment to create either sequential or combinational circuits.

However, we should only use blocking assignment to model combinational logic circuits in verilog. The main reason for this is that our code will be much easier to understand and maintain.

Combinational Logic in Always Blocks

Up to this point, we have only considered the modelling of sequential circuits using always block.

Although this is the most common use case, we can also model combinational logic using this approach.

As an example, the code below shows how we can use an always block to model the AND-OR circuit which we discussed in the post on continuous assignment in verilog .

We see that this code is almost identical to the example we looked at in the post on continuous assignment.

The only major difference here is the fact that we have encased it within an always block. We also remove the verilog assign keyword from the statement as we no longer need it.

We can also see from this example how the sensitivity list is more complex for combinational circuits than sequential circuits.

There are actually two methods which we can use to write the sensitivity list when modelling combinational logic circuits.

The first method we can use is to list each of the inputs to the circuit separated by either by the or keyword or by a comma. This is the method we have used in the example code above.

In addition to this, we can also use the * character to tell our verilog tools to automatically decide which signals to include in the sensitivity list.

This technique is preferable as it has the advantage of being easier to maintain. However, this method was introduced as part of the verilog 2001 standard meaning it can't be used with verilog 1995 code.

The code snippet below shows how we would use both of these methods.

Generally speaking, using the always block to model combinational logic adds  boiler plate code  to our design.

Therefore, we only use the always block to model combinational logic circuits in a few circumstances where it can simplify the modelling of complex combinational logic.

Multiplexors

One instance where it can be useful to use an always block to model combinational logic is when we want to model a multiplexor .

In this case, we can use a construct known as the case statement to model the multiplexor. In comparison to the methods we discussed in the post on modelling combinational logic in verilog , this provides a simpler and more intuitive way of modelling large multiplexors.

We talk about the verilog case statement in more detailed in a future blog post. However, the code snippet below shows how we would use the case statement to model a simple four to one multiplexor.

The case statement is fairly simple to understand, as it uses a variable to select one of a number of branches to execute.

We can include as many different branches as we require in the case statement.

In addition, we use the default branch to catch any values which we havenā€™t explicitly listed.

In order to use this as a multiplexor, we use the variable as if it were the address pins.

We can then assign the output of the multiplexor to the required value based on which branch we are executing.

What is the difference between continuous assignment and procedural blocks (such as the always block) in verilog?

We use procedural blocks such as the always block to execute code sequentially in verilog. In contrast, continuous assignment is executed in parallel.

Why do we use sensitivity lists in the verilog always block?

They define the list of signals that an always will wait on before resuming the execution of code.

What is the difference between blocking and non-blocking assignment in verilog?

When we use blocking assignment all signal assignments take effect immediately. In contrast, when we use non-blocking assignment our signals are updated using assignment scheduling.

Which type of assignment can we use in continuous assignment? What about in procedural blocks?

When we write code using continuous assignment, we can only use blocking assignment.

We can use both types of assignment within a verilog procedural block. However, non-blocking assignment normally results in a sequential implementation after synthesis. In contrast to this, blocking assignment normally results in a combinational implementation.

Write the code for a 4 input NAND gate using an always block

Write the code for the circuit shown below.

Using Continuous Assignment to Model Combinational Logic in Verilog

In this post, we talk about continuous assignment in verilog using the assign keyword. We then look at how we can model basic logic gates and multiplexors in verilog using continuous assignment.

There are two main classes of digital circuit which we can model in verilog ā€“ combinational and sequential .

Combinational logic is the simplest of the two, consisting solely of basic logic gates, such as ANDs, ORs and NOTs. When the circuit input changes, the output changes almost immediately (there is a small delay as signals propagate through the circuit).

In contrast, sequential circuits use a clock and require storage elements such as flip flops . As a result, output changes are synchronized to the circuit clock and are not immediate.

In this post, we talk about the techniques we can use to design combinational logic circuits in verilog. In the next post, we will discuss the techniques we use to model basic sequential circuits .

Continuous Assignment in Verilog

We use continuous assignment to drive data onto verilog net types in our designs. As a result of this, we often use continuous assignment to model combinational logic circuits.

We can actually use two different methods to implement continuous assignment in verilog.

The first of these is known as explicit continuous assignment. This is the most commonly used method for continuous assignment in verilog.

In addition, we can also use implicit continuous assignment, or net declaration assignment as it is also known. This method is less common but it can allow us to write less code.

Let's look at both of these techniques in more detail.

Explicit Continuous Assignment

We normally use the assign keyword when we want to use continuous assignment in verilog. This approach is known as explicit continuous assignment.

The verilog code below shows the general syntax for continuous assignment using the assign keyword.

The <variable> field in the code above is the name of the signal which we are assigning data to. We can only use continuous assignment to assign data to net type variables.

The <value> field can be a fixed value or we can create an expression using the verilog operators we discussed in a previous post. We can use either variable or net types in this expression.

When we use continuous assignment, the <variable> value changes whenever one of the signals in the <value> field changes state.

The code snippet below shows the most basic example of continuous assignment in verilog. In this case, whenever the b signal changes states, the value of a is updated so that it is equal to b.

Net Declaration Assignment

We can also use implicit continuous assignment in our verilog designs. This approach is also commonly known as net declaration assignment in verilog.

When we use net declaration assignment, we place a continuous assignment in the statement which declares our signal. This can allow us to reduce the amount of code we have to write.

To use net declaration assignment in verilog, we use the = symbol to assign a value to a signal when we declare it.

The code snippet below shows the general syntax we use for net declaration assignment.

The variable and value fields have the same function for both explicit continuous assignment and net declaration assignment.

As an example, the verilog code below shows how we would use net declaration assignment to assign the value of b to signal a.

Modelling Combinational Logic Circuits in Verilog

We use continuous assignment and the verilog operators to model basic combinational logic circuits in verilog.

To show we would do this, let's look at the very basic example of a three input and gate as shown below.

To model this circuit in verilog, we use the assign keyword to drive the data on to the and_out output. This means that the and_out signal must be declared as a net type variable, such as a wire.

We can then use the bit wise and operator (&) to model the behavior of the and gate.

The code snippet below shows how we would model this three input and gate in verilog.

This example shows how simple it is to design basic combinational logic circuits in verilog. If we need to change the functionality of the logic gate, we can simply use a different verilog bit wise operator .

If we need to build a more complex combinational logic circuit, it is also possible for us to use a mixture of different bit wise operators.

To demonstrate this, let's consider the basic circuit shown below as an example.

To model this circuit in verilog, we need to use a mixture of the bit wise and (&) and or (|) operators. The code snippet below shows how we would implement this circuit in verilog.

Again, this code is relatively straight forward to understand as it makes use of the verilog bit wise operators which we discussed in the last post.

However, we need to make sure that we use brackets to model more complex logic circuit. Not only does this ensure that the circuit operates properly, it also makes our code easier to read and maintain.

Modelling Multiplexors in Verilog

Multiplexors are another component which are commonly used in combinational logic circuits.

In verilog, there are a number of ways we can model these components.

One of these methods uses a construct known as an always block . We normally use this construct to model sequential logic circuits, which is the topic of the next post in this series. Therefore, we will look at this approach in more detail the next blog post.

In the rest of this post, we will look at the other methods we can use to model multiplexors.

Verilog Conditional Operator

As we talked about in a previous blog, there is a conditional operator in verilog . This functions in the same way as the conditional operator in the C programming language.

To use the conditional operator, we write a logical expression before the ? operator which is then evaluated to see if it is true or false.

The output is assigned to one of two values depending on whether the expression is true or false.

The verilog code below shows the general syntax which the conditional operator uses.

From this example, it is clear how we can create a basic two to one multiplexor using this operator.

However, let's look at the example of a simple 2 to 1 multiplexor as shown in the circuit diagram below.

The code snippet below shows how we would use the conditional operator to model this multiplexor in verilog.

Nested Conditional Operators

Although this is not common, we can also write code to build larger multiplexors by nesting conditional operators.

To show how this is done, let's consider a basic 4 to 1 multiplexor as shown in the circuit below.

To model this in verilog using the conditional operator, we treat the multiplexor circuit as if it were a pair of two input multiplexors.

This means one multiplexor will select between inputs A and B whilst the other selects between C and D. Both of these multiplexors use the LSB of the address signal as the address pin.

To create the full four input multiplexor, we would then need another multiplexor.

This takes the outputs from the first two multiplexors and uses the MSB of the address signal to select between them.

The code snippet below shows the simplest way to do this. This code uses the signals mux1 and mux2 which we defined in the last example.

However, we could easily remove the mux1 and mux2 signals from this code and instead use nested conditional operators.

This reduces the amount of code that we would have to write without affecting the functionality.

The code snippet below shows how we would do this.

As we can see from this example, when we use conditional operators to model multiplexors in verilog, the code can quickly become difficult to understand. Therefore, we should only use this method to model small multiplexors.

Arrays as Multiplexors

It is also possible for us to use verilog arrays to build simple multiplexors.

To do this we combine all of the multiplexor inputs into a single array type and use the address to point at an element in the array.

To get a better idea of how this works in practise, let's consider a basic four to one multiplexor as an example.

The first thing we must do is combine our input signals into an array. There are two ways in which we can do this.

Firstly, we can declare an array and then assign all of the individual bits, as shown in the verilog code below.

Alternatively we can use the verilog concatenation operator , which allows us to assign the entire array in one line of code.

To do this, we use a pair of curly braces - { } - and list the elements we wish to include in the array inside of them.

When we use the concatenation operator we can also declare and assign the variable in one statement, as long as we use a net type.

The verilog code below shows how we can use the concatenation operator to populate an array.

As verilog is a loosely typed language , we can use the two bit addr signal as if it were an integer type. This signal then acts as a pointer that determines which of the four elements to select.

The code snippet below demonstrates this method in practise. As the mux output is a wire, we must use continuous assignment in this instance.

What is the difference between implicit and explicit continuous assignment?

When we use implicit continuous assignment we assign the variable a value when we declare. When we use explicit continuous assignment we use the assign keyword to assign a value.

Write the code for a 2 to 1 multiplexor using any of the methods discussed we discussed.

Write the code for circuit below using both implicit and explicit continuous assignment.

An Introduction to the Verilog Operators

In this post, we talk about the different operators which we can use in verilog. These operators provide us with a way to process the digital data in our verilog designs.

This processing can be extremely simple, as is the case with simple logic gates . However, we may also need to perform complex logical or mathematical operations on our data.

In any case, verilog provides us with a number of operators which allow us to perform a wide range of different calculations or operations on our data.

In most instances when we use verilog operators, we create boolean expressions or logic circuits which we want to synthesize . However, there are also some operators which we can't use to write synthesizable code.

Let's take a closer look at the various different types of operator which we can use in our verilog code.

Verilog Bit Wise Operators

We use the bit wise operators to combine a number of single bit inputs into a single bit output. In addition, we can also use the bit wise operators on verilog vector types .

We most commonly use the bit wise operators to model logic gates in verilog.

The table below shows the full list of bit wise operators which we can use in verilog.

The verilog code below shows how we use each of these operators in practise.

Verilog Arithmetic Operators

We use arithmetic operators to perform basic mathematic functions on our variables. These operators should already be familiar as they are mostly replications of common mathematic symbols.

However, these operators also require some consideration when we use them with synthesizable code.

The plus, minus and multiplication operators can all be synthesised by most modern tools.

However, this can often result in sub-optimal logical performance. As a result, it can be necessary to design logic circuits which specifically perform these functions.

Alternatively, we may wish to use DSP blocks within our FPGA to perform these operations more efficiently.

We should never use the modulus, exponential or divide operators for synthesizable code as most tools will be unable to handle them.

The table below shows the full list of arithmetic operators in Verilog.

The code snippet below shows how we use each of these operators in practise.

Verilog Relational Operators

We use relational operators to compare the value of two different variables in verilog. The result of this comparison returns either a logical 1 or 0 , representing true and false respectively.

These operators are similar to what we would see in other programming languages such as C or Java .

In addition to this, most of these operators are also commonly used in basic mathematics expressions so they should already feel familiar.

The table below shows the full list of relational operators in Verilog.

The verilog code below shows how we use each of the relational operators in practise.

Verilog Logical Operators

The verilog logical operators are similar to the bit-wise operators we have already seen.

However, rather than using these operators to model gates we use them to combine relational operators. As a result, we can build more complex expressions which can perform more than one comparison.

As with relational operators, these expressions return either a 1 (true) or 0 (false).

There are only three logical operators which we can use in verilog. Again, these are similar to operators which are used in languages such as C or Java.

The table below shows the full list of logical operators in Verilog.

The verilog code below shows how we use each of the logical operators in practise.

Again, it is important that we use parentheses to separate the different elements in our expressions when using these operators.

Verilog Shift Operators

In addition to the operators we have already seen, there are a few extra operators which we can use for specific logical functions.

One of the most useful and commonly used of these special functions are the shift operators, which are shown in the table below.

When designing digital circuits, we frequently make use of shift operations . As a result, verilog provides us with a simple technique for implementing these functions.

The shift operator actually requires two arguments. The first of these is the name of the signal which we want to shift. The second argument is the number of bits we want to shift.

When we use the logical shift operators, all the blank positions are filled with 0b after the signal has been shifted by the required number of bits.

In contrast, the arithmetic shift operators preserve the sign of the shifted signal. As a result of this, they should only be used with the verilog signed types .

The code snippet below shows how we use the shift operators in practise.

In verilog, we use a construct known as the conditional operator to assign data to a signal based on a conditional statement .

This operator may already be familiar as it is also used in other programming languages such as C and Java . However, in this case it is known as the ternary operator.

The code snippet below shows the general syntax for the verilog conditional operator.

When the expression given in the <condition> field evaluates as true, then the output is set to the value given in the <true> field.

If the conditional expression evaluates as false, then the output is set to the value given by the <false> field.

The code snippet below shows a practical example of the verilog conditional operator. In the a future post in this series, we see how we can use the conditional operator model multiplexors .

Concatenation and Replication Operators

The final types of verilog operator which we can use are the concatenation and replication operators.

In both instances, the output of these operators are a vector type. However, the inputs to both of these operators can be either single bit or vector types.

Both of these verilog operators are show in the table below.

We use the verilog concatenation operator to combine two or more signals into a vector.

As an example, we may have 2 single bit signals which we want to combine to use as an address for a multiplexor.

To use the concatenation operator, we list the signals which we wish to combine within the curly brackets. We separate this list of signals using a comma.

When we use the verilog concatenation operator, the bits in the output match the order in which they are listed inside the brackets.

For example, the code snippet below would result in a output vector which has the value 0011b.

We use the replication operator to assign the same value to a number of bits in a vector.

For example, if we wanted to assign all of the bits of a vector to 0b then we would use the replication operator.

When we use the replication operator we have to specify both the signal or value we want to replicate and the number of times we want to replicate it.

The verilog code below show how we use the concatenation and replication operators in practice.

Which type of operators do we use to model logic gates in verilog?

We use the bit wise operators to model logic gates in verilog.

Two of the arithmetic operators should not be used with synthesizable code ā€“ name them.

The division and modulus operators canā€™t be synthesized.

What is the difference between the bit wise and logical operators?

The bit wise operators work on individual bits whereas the logical operators are used to combine logical expressions.

What is the difference between the logical shift operators and the arithmetic shift operators.

The logical shift operators pad the blank positions with 0b whereas the arithmetic operator preserves the sign of the signal.

Sign up free for exclusive content.

Don't Miss Out

We are about to launch exclusive video content. Sign up to hear about it first.

generate loops in Verilog

Generate loops in verilog.

Traditional for and while loops are ā€œbehavioralā€ loops. Verilog also supports structural loops that create repeated instances of a submodule, or repeated assignments. This capability uses the generate syntax. As an example, we will implement a classic ripple carry adder using structural syntax.

You may be familiar with the standard full adder logic module, defined in the code below.

The ripple-carry adder is a string of full-adder modules, where the c_out (carry out) from each module connects to the c_in (carry in) of the next module. This is illustrated in the schematic figure shown below.

In Verilog, a structural model is a direct description of a schematic like the one in the figure. Some models require many repetitions of the same submodule, and it is inefficient to code each instantiation separately. The generate statement allows us to place and connect all the modules automatically. An additional benefit is that generate loops can be parameterized , so the structure is adjustable.

The code below describes an N bit ripple-carry adder with default size N= 4 .

To connect the carry chain, we declared a wire vector named c , corresponding to the blue labels in the schematic diagram. The wire signals can be directly connected to submodule ports. To connect the end-points at c_in and c_out , we use assign statements.

Assigned Tasks

In the src/ directory, create files named full_adder.v and adder.v containing the module definitions given above.

Simulate the Design

Edit the testbench template in testbench.v and make the following additions:

  • To create an exhaustive test spanning all input combinations, use the concatenation operator to make simultaneous assignments to a , b , and c_in like this:

Place this assignment in the always block labeled ā€œCREATE STIMULIā€.

  • Since a and b have four bits, and c_in has one bit, there are a total of 9 input bits. To verify all input combinations, we need to simulate 2^9 cases. The clk_count signal takes gets a unique value every clock cycle, so after 2^9 cycles we should have incremented through all the test cases. Find the termination condition in the testbench, and change it so that it stops when clk_count equals 2^9.

After making the changes, run make simulate to verify the behavior. You should see a table of outputs like this:

Notice that a , b , and s are printed as decimal values thanks to the %d option in the $write statements:

Scroll through the results and verify that the correct sum and carry are produced.

View the Hierarchy

Next open build.tcl and notice that some changes were made. The synth_design line has some extra commands that request a partial synthesis:

The options here request that Vivado perform ā€œrtlā€ synthesis (not targeted to any specific FPGA chip), and preserve the design hierarchy. After synthesis, the result is written to a file called rtl-synth.v . Run make implement to perform the partial synthesis, then open the rtl-synth.v file. You should see that the generate loop is expanded into four full_adder modules that look like this:

The generate loop is assigned a default name, genblk1 . The instances within genblk1 are referenced like a vector as genblk1[ 0 ] , genblk1[ 1 ] , and so on. Within each genblk1 instance, the full_adder instances are named genblk1[ 0 ].fa , genblk1[ 1 ].fa , and so on.

Named generate blocks

When testing and debugging a design, default names like genblk1 can be mysterious and confusing. Verilog allows named generate loops. Modify the generate loop in adder.v so that it has the name myadder , like this:

Then modify the $write and $fwrite statements in testbench.v so that it prints submodule signals for myadder[ 0 ].fa . The generic format for referencing sub-module signals is submodule1.submodule2.signal_name , like this:

Repeat the make simulate and make implement actions. In the file rtl-synth.v you should notice that the generate instances are now named myadder[ 0 ].fa , myadder[ 1 ].fa , and so on.

Examine the final results in test_result.txt and verify that the submodule signals are correct.

Turn in your work using git :

Indicate on Canvas that your assignment is done.

IMAGES

  1. é—œę–¼genvar及generateē”Øę³•ēš„ēø½ēµć€Verilog怑

    verilog generate genvar assignment

  2. Cascading of structural Model in verilog using generate and For Loop

    verilog generate genvar assignment

  3. Verilog初ēŗ§ę•™ē؋ļ¼ˆ12ļ¼‰Verilogäø­ēš„generate块-äŗ‘ē¤¾åŒŗ-华äøŗäŗ‘

    verilog generate genvar assignment

  4. Systemverilog generate : Where to use generate statement in Verilog

    verilog generate genvar assignment

  5. verilog

    verilog generate genvar assignment

  6. 怐Verilogē¼–ēØ‹ć€‘generate for态generate if态generate caseēš„ē”Øę³•

    verilog generate genvar assignment

VIDEO

  1. #33 "generate" in verilog

  2. Verilog Loops: A Guide to Generate Blocks with Examples

  3. Systemverilog generate : Where to use generate statement in Verilog & Systemverilog

  4. For loop inside generate statement in Verilog

  5. Verilog Tutorial 10 -- Generate Blocks

  6. Verilog Generate Block/"generate for" loop explained with examples #verilog

COMMENTS

  1. Incrementing Multiple Genvars in Verilog Generate Statement

    I'm trying to create a multi-stage comparator in verilog and I can't figure out how to increment multiple genvars in a single generate loop. I'm trying the following: genvar i,j; //Level 1 generat...

  2. SystemVerilog Generate Construct

    Overview. The Generate construct is a very useful tool. You'll commonly see it used for these 3 purposes. Lazy instantiation of module items using a for-loop. Changing the structure or design of a module using SystemVerilog Parameters. Using generate with assertions for Functional and Formal Verification. Generate Overview.

  3. Verilog Generate Configurable RTL Designs

    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

  4. Verilog generate block

    A generate block allows to multiply module instances or perform conditional instantiation of any module. It provides the ability for the design to be built based on Verilog parameters. These statements are particularly convenient when the same operation or module instance needs to be repeated multiple times or if certain code has to be conditionally included based on given Verilog parameters.

  5. Generate Blocks

    The generate statement in Verilog is a very useful construct that generates synthesizable code during elaboration time dynamically. The simulator provides an elaborated code of the 'generate' block. It provides the below facilities: To generate multiple module instances or code repetition. Conditionally instantiate a block of code based on ...

  6. Writing Reusable Verilog Code using Generate and Parameters

    The verilog code below shows how we would do this using a generate for and a for loop. In both cases, the functionality of the code is the same but the structure produced is very different. // Example using the for loop. always @(posedge clock) begin. for (i = 0; i < 2; i = i + 1) begin. sig_a[i] = 1'b0;

  7. Generate

    Description: A generate loop permits generating multiple instances of modules and primitives, as well as generating multiple occurences of variables, nets, tasks, functions, continuous assignments, initial and always procedural blocks. The index variable used in a generate loop must be a genvar variable. This variable can be declared inside the ...

  8. Generate blocks

    A genvar is "an integer variable which must be a positive value. They may only be used within a generate block. Genvar variables only have a value during elaboration, and do not exist during simulation. Genvar variables must be declared within the module where the genvar is used. They may be declared either inside or outside of a generate block."

  9. how to use generate for multiple module instantiation in verilog

    Tour Start here for a quick overview of the site Help Center Detailed answers to any questions you might have Meta Discuss the workings and policies of this site

  10. Genvar

    A genvar is a variable used in generate-for loop. It stores positive integer values. It differs from other Verilog variables in that it can be assigned values and changed during compilation and elaboration time. The genvar must be declared within the module where it is used, but it can be declared either inside or outside of the generate loop.

  11. Accessing task of generate block using hierarchy path

    The index created by a generate loop or array if instances cannot be accessed via a dynamic variable. This restriction is mainly because of Verilog parametrization and a few other features that has the potential to make each instance unique. In a regular array, every element is the exact same type and has the exact same sub-components.

  12. Writing Reusable Verilog Code using Generate and Parameters

    Verilog Generate Case Example. To better demonstrate how the verilog generate case statement works, let's consider a basic example. As the case generate statement performs a similar function to the if generate statement, we will look at the same example again. This means that we will write a test function which outputs the value of a 4-bit counter.

  13. generate loops in Verilog

    Generate Loops in Verilog. Traditional for and while loops are "behavioral" loops. Verilog also supports structural loops that create repeated instances of a submodule, or repeated assignments. This capability uses the generate syntax. As an example, we will implement a classic ripple carry adder using structural syntax.

  14. Accessing a generate block hierarchy

    I'm trying to access a verilog hierarchy which was generated by a generate block - but I'm having some problems with it. for example: verilog file: (let's assume it is located at "testbench" hierarchy, and an interface named "some_interface" is already defined) genvar i; generate for (i=0;i<3;i++) begin : GENERATE_HEADER

  15. digital logic

    109 4. 4. A genvar is just a loop iterator that gets unrolled into a constant at compilation. It is not a variable. You need to explain what kind of array you want; an array of wires, variables, or module instantiations. It might help to show a piece of code without using generate that manually describes what you want to do.

  16. syntax

    It is possible to avoid always @* when you want to do more advanced math with genvar loops. Use localparam and a function.. Make k a localparam derived from the genvars with a function, and use k as originally intended.. The getk function seems to violate the principles of code reuse by basically recreating the loops from the generate block, but getk allows each unrolled loop iteration to ...

  17. verilog

    Initialize and increment a genvar variable inside nested for loop of system verilog Hot Network Questions My PhD supervisor is doing nothing and is probably involved in academic misconduct.