GitHub

Verilog Conditional Operator

Just what the heck is that question mark doing.

Have you ever come across a strange looking piece of Verilog code that has a question mark in the middle of it? A question mark in the middle of a line of code looks so bizarre; they’re supposed to go at the end of sentences! However in Verilog the ? operator is a very useful one, but it does take a bit of getting used to.

The question mark is known in Verilog as a conditional operator though in other programming languages it also is referred to as a ternary operator , an inline if , or a ternary if . It is used as a short-hand way to write a conditional expression in Verilog (rather than using if/else statements). Let’s look at how it is used:

Here, condition is the check that the code is performing. This condition might be things like, “Is the value in A greater than the value in B?” or “Is A=1?”. Depending on if this condition evaluates to true, the first expression is chosen. If the condition evaluates to false, the part after the colon is chosen. I wrote an example of this. The code below is really elegant stuff. The way I look at the question mark operator is I say to myself, “Tell me about the value in r_Check. If it’s true, then return “HI THERE” if it’s false, then return “POTATO”. You can also use the conditional operator to assign signals , as shown with the signal w_Test1 in the example below. Assigning signals with the conditional operator is useful!

Nested Conditional Operators

There are examples in which it might be useful to combine two or more conditional operators in a single assignment. Consider the truth table below. The truth table shows a 2-input truth table. You need to know the value of both r_Sel[1] and r_Sel[0] to determine the value of the output w_Out. This could be achieved with a bunch of if-else if-else if combinations, or a case statement, but it’s much cleaner and simpler to use the conditional operator to achieve the same goal.

Learn Verilog

Leave A Comment Cancel reply

Save my name, email, and website in this browser for the next time I comment.

  • The Verilog-AMS Language
  • Analog Processes
  • Conditional Statements

Conditional Statements 

If statements .

An if statement evaluates an expression and executes the subsequent statement if the expression evaluates to true, otherwise it skips that statement. For example:

An if/else statement evaluates an expression and executes the statement before else if the expression evaluates to true, otherwise it evaluates the statement after the else . For example:

A common idiom is to nest if/else statements as follows:

In this case an if keyword binds to the next closest else keyword.

Case Statements 

A case statement tests and expression and then enumerates what actions should be taken for the various values that expression can take. For example:

If the needed case is not found, then no statements are executed. If multiple cases are found that match, the first one found is used (they are tried in order). If more that once cases should be associated with the same statement, they can be given in a comma separated list:

It is also possible to specify a default case:

Verilog if-else-if

If without else, if with else, if without else for single statement, if without else for multiple statements, if-else for single statement, if-else for multiple statements.

This conditional statement is used to make a decision on whether the statements within the if block should be executed or not.

  • If the expression evaluates to true (i.e. any non-zero value), all statements within that particular if block will be executed
  • If it evaluates to false (zero or 'x' or 'z'), the statements inside if block will not be executed
  • If there is an else statement and expression is false then statements within the else block will be executed.

If multiple statements need to be placed inside the if or else part, it needs to be enclosed within begin and end .

Hardware Implementation

if without an else part implies that the value remain unchanged for any condition that does not satisfy the expression inside if .

Value of output q is updated whenever d or en changes in value.

system verilog conditional assignment

Output q will get the value of input d at the positive edge of clock if rstn is high and describes the behavior of a D flop.

Note that the synthesized output indicates a flop with an output q .

system verilog conditional assignment

In the following example, the design module has a 4-bit output q that is incremented when mode is 1 and decrements when mode is 2 with if else construct. Note that the description does not specify what has to be done if mode is 0 or 3 which are valid values for a 2-bit variable. It is assumed that the circuit does nothing when mode is 1 and 3, but maintain exiting value of q . It is not recommended to leave such ambiguity in real design code, but is shown here to highlight the possibility.

The synthesized output may differ with availability of cells for a given technology library

Shown below is the synthesized output and it is worth to note that q got implemented as a 4-bit flop which has a pin CE to enable the flop. Note that this flop is enabled only when mode is 1 or 2 and not for other values. Output q is fed back through an adder and subtractor block into the input of the same flop through a mux which is again controlled by mode .

system verilog conditional assignment

Consider the same design from above with a 1-bit mode .

In this case, a regular flop without a CE pin is used along with a few multiplexers to choose the correct signal based on value of mode .

system verilog conditional assignment

Modeling Concurrent Functionality in Verilog

  • First Online: 01 March 2019

Cite this chapter

system verilog conditional assignment

  • Brock J. LaMeres 2  

84k Accesses

This chapter presents a set of built-in operators that will allow basic logic expressions to be modeled within a Verilog module. This chapter then presents a series of combinational logic model examples.

This is a preview of subscription content, log in via an institution to check access.

Access this chapter

  • Available as PDF
  • Read on any device
  • Instant download
  • Own it forever
  • Available as EPUB and PDF

Tax calculation will be finalised at checkout

Purchases are for personal use only

Institutional subscriptions

Author information

Authors and affiliations.

Department of Electrical & Computer Engineering, Montana State University, Bozeman, MT, USA

Brock J. LaMeres

You can also search for this author in PubMed   Google Scholar

Rights and permissions

Reprints and permissions

Copyright information

© 2019 Springer Nature Switzerland AG

About this chapter

LaMeres, B.J. (2019). Modeling Concurrent Functionality in Verilog. In: Quick Start Guide to Verilog. Springer, Cham. https://doi.org/10.1007/978-3-030-10552-5_3

Download citation

DOI : https://doi.org/10.1007/978-3-030-10552-5_3

Published : 01 March 2019

Publisher Name : Springer, Cham

Print ISBN : 978-3-030-10551-8

Online ISBN : 978-3-030-10552-5

eBook Packages : Engineering Engineering (R0)

Share this chapter

Anyone you share the following link with will be able to read this content:

Sorry, a shareable link is not currently available for this article.

Provided by the Springer Nature SharedIt content-sharing initiative

  • Publish with us

Policies and ethics

  • Find a journal
  • Track your research

Modules, Controls, and Interfaces

3.1 modules.

Modules are the basic building blocks of SystemVerilog. It is intended to be a reusable component that can be connected to form a larger component. To declare a module, we can use the following syntax:

Notice that due to legacy reason, there is no namespace for modules. As a result, module_name has to be unique in your entire design. To declare the ports for the module, we can simply do

Keywords input and output are used to specify the direction of ports. There is another keyword, inout which makes the port bidirectional. inout is typically used for tri-state designs and we will not cover it in the book. If you are declaring multiple ports sharing the same port direction and types, you can omit the subsequential ones, as shown below. Notice that the code is equivalent. It is up to the designers to choose which style they want to follow. In this book we will use the more verbose version.

The type for the ports can be any integral values, such as logic, arrays, or struct. It can also be interface, which will be covered later in the chapter.

Notice that there is another style of declaring port, which is specifying port names first, then later on declare the port direction and types, typically called Non-ANSI style. This style is out-dated and we do not recommend use it in practice.

To declare variables inside the module, we can simply put definition inside the module endmodule .

In the example code we declare 8-bit value value . Notice that it is highly recommended to declare the variable type before using the variable. Although implicit logic declaration is supported in SystemVerilog, it is dangerous and usually triggers compiler warnings/errors.

3.1.1 Module Parameters

SystemVerilog allows the module definition parametrized by certain values, which makes the modules more reusable. For instance, suppose we have an ALU module parametrized by the data width, we can reuse the same definition for both 32-bit and 64-bit ALU instantiation.

To declare a parametrized module, we can use the following syntax, which is also the ANSI style.

In the example above, module mod_param is parametrized by two parameters WIDTH and VALUE . We immediately use WIDTH to parametrize the bit-width of in and out . Notice that we also specify the data type for VALUE . In general we recommend to specify the data type of a parameter. If it used for data width parametrization, int should suffice. In the example we also give the parameters a default value, which is highly recommended to do so.

There is another type of “parameter” called localparam . It is not parameterization per se, since its value cannot be changed through instantiation. However, for the sake of completeness we will cover it here. Local parameters are typically used for storing magic numbers. For state values, however, you should use enum instead.

In the example above we define a magic number VALUE to have the value of 42. We can later use VALUE whenever we need its value.

3.1.2 Module Instantiation and Hierarchy

Once we have a module definition, we can instantiate it in a parent module. Suppose we have a module definition as follows:

We can instantiate the child module as follows:

In the example above, we first declares three variables, clk , in , out , which will be wired to our child instance. To instantiate the child module, we create an instance called child_inst . To specify the port wiring, we use .child_port_name(parent_var_name) syntax. It means to wire parent_var_name from the parent module to child_port_name port from the child instance.

There is another short-hand to instantiate the child module in our case. Since the child_port_name is identical to parent_var_name , we can do the following

(.*) tells the compiler to automatically find and wire matching variable from the parent module. You can even override the default matching with extra connections, as shown below, which wires clk_in to child_inst ’s clk and leaves the rest to the default matching.

Although it may simplify the code and make it more readable, because the matching only relies on the name, it may be matched to an unexpected wire. We recommend to only use this style when the design is simple.

To instantiate a module with different parameter values other than the default ones, we can do the following, using the module mod_param defined earlier.

In the example above we override the parameter value WIDTH with 16. Notice that we have to manually change the bit-width of in and out . A better way to do is the following, where the bit-width is only specified by a single parameter in the parent scope.:

To access variables through hierarchy, we can do child_inst.out from the parent module. We only recommend to do so in test bench, in instead of RTL code for synthesis.

A design style where all the logic are specified through module instantiation rather than the procedural blocks is called structural Verilog. Unless you are very experienced in RTL design or have a particular need in physical design, we highly recommend not to use such style in RTL design. It will reduce the synthesis quality and make verification more difficult. We will discuss the benefit of another style, behavioral Verilog, where design logics are specified through procedural blocks.

3.2 Continuous Assignment

Continuous assignment wires the values on the right hand side to the left side. Continuous in its name implies that whenever an operand in the right-hand expression changes, the whole right-hand side expression shall be evaluated and its result will be assigned to the left hand side. This is used to model combinational circuit where the output of the circuit updates its value whenever the input values change.

To use continuous assignment, we can do

You can of course use more complex expression such as

There are couple rules apply to continuous assignments:

  • Continuous assignment can only appear in the scope of a module, that is, its lexical parent should be module . You cannot declare a continuous assignment in other scopes such as procedural blocks or functions, which we will cover shortly.

Each bit of left hand side can only be assigned to once as continuous assignments. For instance, it is illegal to do something below, where bit a[1] is assigned twice.

The left hand can only be a net/variable, or a select of a vector/net, or a concatenation. For the case of concatenation, the operator can be seen as “unpacked” in the concatenation order, as shown below, where the sum of a , b , and cin is split into cout and sum . Since cout is only 1-bit, it gets the value of carry out. ```SystemVerilog // a 4-bit adder with carry logic [3:0] a; logic [3:0] b; logic [3:0] sum; logic cin; logic cout;

assign {cout, sum} = a + b + cin; Notice you can also perform a continuous assignment when declaring a variable as initialization, as below: SystemVerilog logic [3:0] d = 4’h1; Although it works well for ASIC with constant initialization, it will only work with a subset of FPGA boards and you shall check the targeted compiler when using this syntax. We recommend not to use this syntax if the code is intended to be portable. However, the following syntax: SystemVerilog logic a = b & c; ``` may not be synthesizable for some synthesis tools. We highly recommend to use continuous assignment for this use case.

3.3 Procedural Blocks

Procedural blocks, also known as processes , are the building blocks for a module’s logic. There are five different procedural blocks:

  • initial procedure
  • always_comb
  • always_latch
  • final procedure

We will cover each procedural blocks in details in a slightly different order. We will not cover the Verilog-95 procedural block always here since it is out-dated and error-prone compared to the new syntax.

3.3.1 always_comb : Modeling Combination Logic

The keyword always_comb denotes the combinational nature of the procedure block: every logic contained inside the block will be synthesized into combinational circuits. The general syntax for always_comb is shown below:

begin and end are needed if there are more than one statements in the block. If there is only one statement, we can drop the begin and end , e.g.

It is up to the design style in your project whether such syntax is allowed. In this bool we will use begin and end regardless of the number of statements inside the block.

There are several rules applies to always_comb :

  • Similar to assign , the bits on the left hand side can only assigned in a single always_comb . Some simulator may not error out when there is multiple always_comb blocking assigning to the same bit, but that is undefined behavior. You cannot mix the bit assignment with other procedural blocks either.
  • The evaluation of each statement is in-order. The simulator will go through each statement from top to bottom and evaluate them.

The simulator will re-evaluate the block whenever a variable on the right-hand side changes. However, there are several exceptions. One major exception is that there is no “self-triggering”. When the variable both exists on the left hand and right hand side, updating that variable will not trigger re-evaluation, as shown below:

  • One benefit of using always_comb is that it forces synthesis tool to check your code based on the design intention. If any variable inside always_comb is inferred as a latch, the tool shall issue a warning or error. We will discuss under which condition latch inference happens when we introduce conditional control constructs.
  • always_comb is also sensitive to the contents of a function, which we will cover shortly.

In simulator, the simulator will evaluate the always_comb once after the initial and always procedures have been started.

3.3.2 always_latch : Modeling Latched Combinational Logic

The always_latch construct functionally is identical to always_comb except for the fact that it tells the synthesis tools to check whether enclosed logic presents latched logic. All the other rules applied to always_comb are applicable to always_latch .

3.3.3 always_ff : Modeling Sequential Logic

The syntax for always_ff is shown below:

The signal list inside @() is called sensitivity list, which tells the synthesis tools that the signal updates are triggered by the sensitivity list. Keyword posedge implies that the procedure shall be evaluated at the positive (rising) edge of the signal, and negedge implies the negative (falling) edge of the signal. All the signals in the sensitivity list should be 1-bit.

For RTL design, there are generally two different ways to implement a reset, i.e. synchronous reset and asynchronous reset. The are mainly distinguished by whether to include reset signal in the always_ff sensitivity list. If reset signal is included, then it is asynchronous reset, meaning the reset can happen independently of the clock. In ASIC design, there are advantage and disadvantages of using asynchronous reset:

  • Asynchronous reset can result in cleaner data path, if the technology library has async-reset flip-flops, which is typically the case. This implies that we can push the limit for data-path timing.
  • Because of the additional triggering of reset signal, asynchronous reset results in slightly bigger circuits. However, in a modern ASIC design where there are billion gates, adding one or two gates to each register is not a big problem.
  • For asynchronous design, if the assertion/de-assertion of reset signal is close to the clock edge, the circuit will go to a metastable state, and as a result the reset state could be lost.

Whether to use synchronous or asynchronous reset depends on your design needs and style guide, as long it is used consistently. In this book we will use asynchronous reset whenever necessary.

Another aspect of the reset is posedge/negedge reset. If negedge is used in the sensitivity list, it is said to reset low , and reset high for posedge . Due to some legacy reasons, modern ASIC technology only offer registers with reset low. As a result, if the design uses posedge reset, an inverter gate will be used with the standard cell. Again, adding one gate for each register is not that much an issue when modern ASIC designs. Whether to use reset high or low depends on your style guide. In this book we will use reset low.

Notice that due to naming convention, if the reset is reset low, we usually suffix _n at the end of the signal name to signify that it is negedge reset, e.g., rst_n , reset_n . In this book we will follow this convention.

In additional to the sensitivity list, always_ff also uses a special assignment called nonblocking assignment . Contract to normal assignment, called blocking assignment where = is used, nonblocking assignment uses <= . All the assignments in always_ff should be nonblocking assignment, and nonblocking assignment can only be used inside always_ff , for synthesis purpose. Although mixing blocking and nonblocking assignments is allowed in test bench code, it is strongly discouraged.

The simulation semantics for nonblocking assignment is also different from blocking assignment. As the name suggests, the value update is not “blocking”, that is, the left hand side is not updated immediately, as shown in the example below.

In the always_ff block, when the simulator evaluate the first assignment a <= b , it will evaluate the right hand side first, store the result value internally, and then proceed to the next statement. After every statement is evaluated, the simulator will update the left hand side at the same time . Hence a will get b ’s value before the clock edge and b gets a ’s.

In the always_comb block, however, the simulator will update the left hand side immediately after evaluating the right hand side, before going to the next statement, hence blocking . In this case, after the first assignment, both a and b will be 1.

This nonblocking assignment simulation semantic is designed to mimic the actual physical behavior. In the physical circuit, as long as there is no timing violation, at the clock edge, the flip-flop will take whatever values on its input wires and do an update. It does not care about whats the immediate value between the clock edges. If you wire two flip-flops in a loop, as shown in the example, at the clock edge, the flip-flop can only grab each other’s old value, since the update has not happened yet.

This semantics also allows priority coding in always_comb , as shown below:

Since it is blocking assignment, although after the first statement, a becomes 0, after the second assignment, a is re-assigned to b . This kind of coding style is perfectly legal and sometimes preferred, as we will discuss in the book.

However, if you do that in always_ff with non-blocking assignment, the result is undetermined. Different simulators and synthesis tools may have different interpretation and you may see inconsistent simulation and synthesis result. This kind of usage should be prohibited.

Similar to other always blocks, variable can only be assigned inside the same always_ff block.

3.3.4 initial Procedure

An initial procedure will execute when the simulator starts and will only execute once. In ASIC design, initial procedure is not synthesizable and will be ignored during synthesis - most synthesis tools will report a warning.

The most common way to use initial procedure is for test bench, where stimulus are provided in initial procedure to drive the simulation. We will discuss more in details when we discuss test bench design.

An example of initial is provided below:

3.3.5 final Procedure

Similar to initial procedure, final will be executed at the end of simulation and will only be executed once. If there are multiple final procedures, they will be executed in arbitrary order. final procedures are usually used for display simulation statistics or cleaning up the simulation environment.

3.3.6 Functions

Similar to C/C++, functions in SystemVerilog allows designers to reuse useful logic. The syntax for function is shown below:

For functions that has return type, keyword return must to be used to indicate return value. In old Verilog-95, return value can be assigned via function_name = return_value; . This style is outdated and we will use keyword return instead.

There is another style of writing functions that allows multiple outputs:

In the example above, logic b and c will be assigned after the function call. This is similar to reference arguments in C++.

If your function is recursive, keyword automatic is needed so that the tools will allocate separate stack space when simulate. We will discuss the reasoning when we introduce the variable scoping rules.

Functions in SystemVerilog is synthesizable with certain restrictions:

  • Functions cannot have any timing controls statements, such as details and semaphore, or any calls to constructs that have timing controls statements, such as tasks.
  • Recursive functions must be able to fully elaborate during synthesis. Synthesis tools typically inline function and unroll the recursion. Undetermined recursion does not guarantee a finite and fixed number of recursion, thus cannot be realized into hardware. This is similar to recursive template in C++, where the template expansion happens during compilation.

To call the function, there are general two ways:

Style 1 is similar to function calls in other software programming languages and style 2 is similar to module instantiation in SystemVerilog. In general, if the function only has a few arguments and does not use input / output in their function signature, we will use style 1, and style 2 otherwise.

If the return value of a function call is not needed, most compilers will issue a warning or error. We need to cast the return value to void to avoid this issue:

3.3.7 Tasks

Tasks are very similar to function except the following things:

  • Tasks allow timing controls in their enclosed logic. The timing control can be delay, fork, and other statements.
  • Tasks do not have a return type.

Although some synthesis tools might be able to synthesize tasks that do not have timing control statements, we highly recommend you to use functions for RTL design, and tasks for simulation and verification.

The general syntax for task is shown below:

3.4 Procedural Statements

Procedural statements, as the name suggests, can only exist inside the procedural blocks such as always_comb and function . There are many types of procedural statements and we will cover the following types:

  • Selection statement: if and case statement
  • Loop statements: for and while
  • Jump statements: continue , and break

We will cover loop and jump statement together since they are often used together.

3.4.1 if Statement

The syntax for if statement is shown below

Whether to omit begin ... end when there is only one statement depends on the style guide. In this book we will omit begin ... end whenever it makes code easier to read.

Although expr can actually be a multi-bit expression, since the condition is evaluated against zero, it is generally suggested to make it 1-bit. For instance,

should be written as

for clarity. Like C/C++, dangling else can also be a problem when begin ... end is omitted for nested if statement. In this case we suggest you always use begin ... end block.

3.4.1.1 Additional Keywords for if Statement

SystemVerilog offers several keywords to if statement can be useful to check the correctness of implementation:

unique . An error will be issued during simulation if no condition matches unless there is an else statement. For instance

However, if unique0 is used, there will be any violation report. In general, use unique0 whenever there are some cases not covered by the conditions, but serve no logic.

priority explicitly tells tools that if there is overlaps in the conditions, use the lexically precedent condition first. This is useful to prevent inconsistent behavior between simulators and synthesis tool, where the simulator by default checks conditions in order whereas the synthesis may compile a parallel circuit due to synthesis macros or commands. This ensures the designer’s intent get passes to various tools in a consistent manner.

These keywords are introduced to remove inconsistency between simulator and synthesis tools. Unless explicitly specified in the design style guide, we recommend use these keywords as much as possible. However, in some cases, this keywords may increase the workload for formal verification tools, thus prohibited in some design companies. Again, these keywords usage depends on your project specific design style guide.

3.4.1.2 Latch Created from if Statement

A latch is created if the logic’s value depends on its previous value. If not specified properly, variables used inside if statement will be inferred as a latch during synthesis, resulting in undesired behavior. In the example below, we create a latch unintentionally:

If a = 1 , we will have b = 1 . However, if a = 0 , b will not be set, thus retaining its old value. As a result, we have created latch a . Notice that since we are using always_comb keyword, synthesis will report either a warning or error once a latch is inferred. There are usually two ways to solve latch issue:

  • Specify a default value at the beginning of the always_comb block: ```SystemVerilog always_comb begin b = 0; if (a) b = 1; end
  • Fully specify the if conditions: SystemVerilog always_comb begin if (a) b = 1; else b = 0; end

Choosing which one to use depends on the logic. Sometimes setting default value makes code simpler and sometimes fully specified if statement makes the code more readable. It is up to designer to choose how to avoid latch.

In additional to always_comb , you can also explicitly create a latch inside always_ff , especially asynchronous reset is used:

In the example above we are missing the condition where rst_n is high. Since a ’s value change only depends on rst_n (asynchronous reset), a actually does not depends on the clock edge. Hence the synthesis tool will infer a latch, instead of a flip-flop. Then again, since we use always_ff , an error/warning will be issued from synthesis tools.

To avoid creating latch, besides being careful when writing the logic, we can also resort to commercial SystemVerilog linters or even an elaboration analysis from synthesis tools. We will not cover linter in this book.

3.4.1.3 if Statement with Reset Logic

Although allowed by the language specification, stacking two if statements in the always_ff is not allowed in some synthesis tools, such as Design Compiler ® :

The code above will trigger ELAB-302 error since it contains two if statements in the always_ff block. There are two solutions for that:

  • If a and b are totally separate, use two always_ff instead.
  • If a and b are related, e.g. sharing the same input conditions, merge these two if statements into one if statement.

3.4.2 case Statement

case statement is similar to switch statement in C/C++ with some semantic difference due to the nature of hardware design. The general syntax for case is shown below:

Notice that we do not have break statement inside each case condition clause, which is the major difference compared to that of C/C++. As a result, there is no switch fall through in SystemVerilog. To take into the intentional fall through use case (shown in C++ below), we can put multiple conditions in the same case statement.

If there is only one statement for a particular case condition, we can omit begin...end . However, if any conditions has more than one statement, we recommend to use begin...end to enclose all conditions for readability.

SystemVerilog also allows to use range and wildcards as conditions using inside keyword, which is shown below:

In the example, ? is regarded as don’t care , which means it will match with any 4-state bit. For instance, if a = 4'b0xxxx , it will match with the first case. [8:12] is a range construct that is lower and upper bound inclusive. For instance, if a = 4'b1001 , it will match with the second case. If nothing matches, it will go to the default condition.

Like if statement, we can add modifiers to case statement. The most commonly used is unique . Keep in mind that the default case statement has priority. That is, if two conditions overlap, the execution will follow the first condition lexically. The synthesis tools are required to obey this convention as well. To produce optimal circuit, physical design engineers often use synthesis directives to remove such priority. However, removing priority creates an inconsistency between the simulator and synthesis tools, resulting in potential bug that can only be caught during gate-level simulation. Using unique forces the simulator to check if there is any conditions overlapping, which guarantee the consistency among tools. However, although using unique is highly recommended whenever possible, in some large designs, we may see exponential growth in runtime with some tools, e.g. Formality® from Synopsys®.

3.4.3 Loop Statements

Like C/C++, SystemVerilog also offers for and while loop for control logic. However, since the synthesis tools need to compile the logic into logical gates that compute in finite and deterministic cycles, the loop-bound has to be known during compile time; otherwise the tool either reports an error, or generated unwanted logic.

The general syntax for for loop is shown below:

Here we use i as int since it doesn’t matter whether i is 2-state for 4-state values. In situation where index i is used in arithmetics with 4-logic values, we need to declare i as 4-state variable. Notice that the loop upper bound 42 is a static value known during compilation time. If we use a variable as upper bound, a latch will be used since the synthesis tool assumes the upper bound could be 0, in such case the value update follows the latch inferring rules. However, we can disregard such rules when using for loops in test bench code, since the simulator is less picky.

The general syntax for while loop is shown below:

Unlike for loop, most synthesis tools cannot take while loop construct, since that requires compile-time full elaboration of the loop body, which can be tricky to do. We can convert the loop body into a for loop if needed. Again, there is no such restriction in the test bench code.

There is a variant of while loop in SystemVerilog, i.e.  do...while , which has similar semantics as C/C++. The syntax for do...while loop is

To exit the loop body early, we can use the break statement, similar to C/C++. Some synthesis tools will optimize the circuit when it notices the break statement.

3.5 Generate statements

SystemVerilog allows users to “dynamically” create circuit logic using generate construct. Users can use for-loops or conditional generation to meta-program the circuit. However, such meta-programming has limitations:

  • Generate is evaluated during elaboration time. Hence we cannot add or remove circuit during runtime.
  • We cannot create ports using generate; net, such as logic , is allowed.

3.5.1 Loop generate

The syntax for loop generate is shown below:

Notice that unlike normal for loop, we need to declare the loop variable i using keyword genvar . However, genvar is used as an integer during elaboration to evaluate generated constructs. The loop bounds has to be known during compilation elaboration time, otherwise an error will be thrown. Loop generate can also be nested together to create more complex circuit logics.

When creating instance using generate statement, the hierarchy name is slightly different. We will cover the naming convention in next section.

3.5.2 Conditional generate

In many cases we need to conditionally generate logic based on the parametrization. SystemVerilog supports conditional generate constructs, as long as they can be elaborated statically during compilation time. The “conditional” part is typically done via if or case statements where the condition expression value is known. For instance, we can do the following conditional generate:

In the example we create instances based on the value of the parameter value . When module GenMod is instantiated with different parameter values, we will create instances accordingly. We can also create variables and connecting them inside the generate statement. For instance

3.6 Named Blocks, Scope Rules, and Hierarchical Names

SystemVerilog offers couple language features that allow programmers debug their hardware design easier than before. One demand for reliable debugging is the ability to address every signal by name hierarchically. With that ability, we can add assertions or verification tasks to the signals of interest without changing the design. This feature is achieved by allowing named blocks and defining scope rules.

3.6.1 Named Blocks

In SystemVerilog, you can name any code blocks defined by begin ... end , which can be used later on for hierarchical data access. The general syntax for naming code blocks is shown below:

In the example above, we created two named blocks with the name name_1 and name_2 respectively. Notice that people also add the names next to the end keyword so that it would be symmetric to the name used next to begin , which is, again, a form of coding style.

Notice that there are several cases where named blocks are highly recommend since it helps you to debug the code:

  • The code blocks are generated through generate construct. By language specification, if the generated code block is not named, it will obtain genblk{NUM} as its identifier, where {NNUM} is substituted with the index of generated code blocks in the module scope.
  • In cases such as functions or always_comb where you declare a temporary variable inside the scope, it is always a good practice to name the block so that you can refer to it later when debugging with the waveform, or setting up assertions.
  • Using labels will also help to reader to identify the scope. This particular helpful when the number of lines in the code block is more than 20 lines. This is particular true for functions and modules.

From now on we will use named block whenever appropriate.

3.6.2 Scope Rules

Similar to software programming languages, SystemVerilog has a set of scoping rules. The following constructs define a new scope:

  • begin-end blocks (named or unnamed)
  • fork-join blocks (named or unnamed)
  • Generate blocks

We have covered most of the constructs so far and will cover the reset of it later in the book.

In general, variables created in outer scope can be accessed in an inner scope, and illegal access the other way around. Two scopes at the same levels are isolated as well. Although it is somewhat unrelated to the scope rules, variable declaration inside a begin-end blocks need to follow ANSI-C style, that is, variable declaration has to be at the beginning at the scope. Declaration with assignment counts as normal statement, hence is illegal in the middle of statements, as shown below:

3.6.3 Hierarchical Names

In SystemVerilog, every identifier has a unique hierarchical path name . To do so, we can use “dot-notation” where . is used to access the child scope. There are some rules when resolving hierarchical names:

  • You can always access child scope variables.
  • If the parent scope is visible to the child scope, the child scope can use parent scope’s identify to access other scopes/variables.

Here is an example of accessing hierarchical names:

In general all the identifiers are public in a scope, meaning you are always be able to access it hierarchically. One exception is class variables declared as local , which will be covered later in the book.

3.6.4 Lifetime

Variables declared inside a module and interface have a static life lifetime by default. That means that all the variable will be instantiated/mapped at the beginning of simulation time, and will remain the same through out the entire simulation time. Variables declared inside a function or task by default are static. That means if a function is called multiple times without finishing, e.g. recursive calls or called through fork , the static variables will be overridden unexpectedly. To solve this issue, we can either declare the function/task automatic , e.g.  function automatic foo() , or declare the variable automatic, e.g.  automatic logic var . All variables declared inside an automatic function/task is by default non-static, as their lifetime is set to the life-time of the parent scope, which is similar to local variable declared inside a function in C/C++. Notice that automatic works with synthesizable code as well, even though the semantics are defined in terms of simulation environment.

3.7 Interface

Interface is a new construct introduced by SystemVerilog to encapsulate the reusable communication between different entities such as design and verification blocks. The concept of “interface” is similar to that of software programming language such as C#. However, instead of providing public accessible functions, interface in SystemVerilog defines a bundle of port names, connections, and functions associated with the ports.

Interface can be used as ports or internal wires that connects instances. Below is an example of configuration bus interface and its instantiation and usage. Notice that if the interface does not have any input/output ports, we need to instantiate it with () . Module config_reg takes a “generic” type port using the keyword interface . As a result, the module can take any be instantiated with any interface wires as long as it has required definitions. In other words, the config bus is abstracted away at the config_reg level, a concept similar to object-oriented programming’s generic concept. The legality will be check at compile time as usual.

You can also explicitly specify the interface type in the module definition, as shown below:

Below is an example of interface that has input and output ports. Notice that the instantiate is similar to that of module.

SystemVerilog also allows using keyword modport to create a “new interface” within the interface by specifying the directions of wires within the scope of the interface, as shown in the example below.

Because modport allows us to specify wire direction, the compiler can check the connections and make sure there is no multiple drivers on the same wire. By default, any wire declared inside the interface as inout connectivity. As a result, some synthesis tools may give warnings even though the connections is correct, hence modport is recommended. Since the wires are all connected together, we don’t need to explicit wire the connection inside the interface, i.e.  master.clk is the same signal as slave.clk . modport construct can be used as any other interface when connected to module instances. Again, you can also specify the interface type in the module definition as below:

Like module definitions, interface in SystemVerilog also supports parametrization. The syntax is identical to that of modules, as shown below.

You can also add functions and tasks to the interface. Notice that for synthesizable RTL code we are limited to functions or tasks without any timing control logic. In addition, the function also has to be declared as automatic . Below is an example about interface with functions.

Continuous Assignment and Combinational Logic in SystemVerilog

In this post, we primarily talk about the concept of continuous assignment in SystemVerilog . We also look at how we can use this in conjunction with the SystemVerilog operators to model basic combinational logic circuits .

However, continuous assignment is a feature which is entirely inherited from verilog so anyone who is already familiar with verilog can skip this post.

There are two main classes of digital circuit which we can model in SystemVerilog – 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 the rest of this post, we talk about the main techniques we can use to design combinational logic circuits in SystemVerilog.

In the next post, we will discuss the techniques we use to  model basic sequential circuits .

Continuous Assignment in SystemVerilog

In verilog based designs, we use continuous assignment to drive data on verilog net types . As a result of this, we use continuous assignment to model combinational logic circuits.

In SystemVerilog, we often use the logic data type rather than the verilog net or reg types. This is because the behavior of the logic type is generally more intuitive than the reg and wire types.

Despite this, we still make use of continuous assignment in SystemVerilog as it provides a convenient way of modelling combinational logic circuits.

We can use continuous assignment with either the logic type or with net types such as wire.

In SystemVerilog, we can actually use two different methods to implement continuous assignment.

The first of these is known as explicit continuous assignment. This is the most commonly used method for continuous assignment in SystemVerilog.

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 SystemVerilog. This approach is known as explicit continuous assignment.

The SystemVerilog code below shows the general syntax for continuous assignment using the assign keyword.

In this construct, we use the <variable> field to give the name of the signal which we are assigning data to. As we mentioned earlier, we can only use continuous assignment to assign data to net or logic type variables.

The <value> field can be a fixed value or we can create an expression using the SystemVerilog operators we discussed in a previous post.

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 SystemVerilog. 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 SystemVerilog designs. This approach is also commonly known as net declaration assignment in SystemVerilog.

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 SystemVerilog, 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 SystemVerilog code below shows how we would use net declaration assignment to assign the value of b to signal a.

Modelling Combinational Logic Circuits in SystemVerilog

We use continuous assignment and the SystemVerilog operators to model basic combinational logic circuits in SystemVerilog.

In order to show we would do this, let's look at the very basic example of a three input and gate as shown below.

In order to model this circuit in SystemVerilog, we must use the assign keyword to drive the data on to the and_out output.

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 SystemVerilog.

This example shows how simple it is to design basic combinational logic circuits in SystemVerilog. If we need to change the functionality of the logic gate, we can simply use a different SystemVerilog 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.

In order to model this circuit in SystemVerilog, 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 SystemVerilog.

Again, this code is relatively straight forward to understand as it makes use of the SystemVerilog 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 SystemVerilog

Multiplexors are another component which are commonly used in combinational logic circuits.

In SystemVerilog, there are a number of ways we can model these components.

One of these methods uses a construct known as an always block which we will discuss in detail in the next post. Therefore, we will not discuss this approach to modelling multiplexors in this post.

However, we will look at the other methods we can use to model multiplexors in the rest of this post.

  • SystemVerilog Conditional Operator

As we talked about in a previous post, there is a conditional operator in SystemVerilog . 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 SystemVerilog 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 SystemVerilog.

  • 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.

In order to model this in SystemVerilog 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.

The SystemVerilog code shown below demonstrates how we would implement this.

To create the full four input multiplexor, we would then need another multiplexor.

This multiplexor then takes the output of the other two multiplexors and uses the MSB of the address signal to select between.

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 basic SystemVerilog arrays to build simple multiplexors.

In order 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.

In order 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 SystemVerilog code below.

Alternatively we can use the SystemVerilog concatenation operator , which allows us to assign the entire array in one line of code.

In order 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.

The SystemVerilog code below shows how we can use the concatenation operator to populate an array.

As SystemVerilog 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.

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. In contrast, 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 in this post.

Write the code for circuit below using both implicit and explicit continuous assignment.

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Save my name, email, and website in this browser for the next time I comment.

Table of Contents

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.

IMAGES

  1. 😍 Verilog assignment. Conditional Operator. 2019-02-03

    system verilog conditional assignment

  2. PPT

    system verilog conditional assignment

  3. 😍 Verilog assignment. Conditional Operator. 2019-02-03

    system verilog conditional assignment

  4. 😍 Verilog assignment. Conditional Operator. 2019-02-03

    system verilog conditional assignment

  5. Implementation of 4 bit Comparator in Verilog || Conditional Operator || Electronics Hub PK

    system verilog conditional assignment

  6. Conditional assignment in verilog

    system verilog conditional assignment

VIDEO

  1. Digital Design With Verilog @NPTEL 2024 Assignment 10 Solutions

  2. System Design Through Verilog NPTEL week 3 Assignment 3

  3. System Design Through Verilog Assignment 5 Week 5 Answers

  4. System Design Through Verilog Assignment 4 Week 4 Solutions

  5. Randomization in SystemVerilog

  6. System Design Through Verilog Assignment 6 week 6 Answers

COMMENTS

  1. Conditional Operator

    The question mark is known in Verilog as a conditional operator though in other programming languages it also is referred to as a ternary operator, an inline if, or a ternary if. It is used as a short-hand way to write a conditional expression in Verilog (rather than using if/else statements). Let's look at how it is used:

  2. Verilog Conditional Statements

    In Verilog, conditional statements are used to control the flow of execution based on certain conditions. There are several types of conditional statements in Verilog listed below. Conditional Operator <variable> = <condition> ? <expression_1> : <expression_2>; The conditional operator allows you to assign a value to a variable based on a ...

  3. Generate Conditional Assignment Statements in Verilog

    Generate Conditional Assignment Statements in Verilog. Ask Question Asked 9 years, 6 months ago. Modified 9 years, 6 months ago. Viewed 5k times 1 I'm trying to create a simple crossbar style interconnect between N masters and M slaves. ... system-verilog; Share. Improve this question. Follow edited Nov 7, 2014 at 16:18. Greg. 18.5k 5 5 ...

  4. Conditional Operator (?:)

    The conditional operator (?:), also known as the ternary operator, is a unique operator in SystemVerilog that takes three operands: a condition, a value if the condition is true, and a value if the condition is false. It serves as a shorthand way of writing an if-else statement. The syntax is as follows: condition ? value_if_true : value_if_false.

  5. If Statements and Case Statements in SystemVerilog

    SystemVerilog If Statement. The if statement is a conditional statement which uses boolean conditions to determine which blocks of SystemVerilog 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 ...

  6. System Verilog Operators: A Comprehensive Guide

    Assignment Operators in System Verilog. In System Verilog, we use assignment operators to assign values to variables. ... Another conditional operator in System Verilog is the implication operator. The implication operator is used to create logical implications. It takes two operands and returns a Boolean value.

  7. PDF System Verilog

    System Verilog - Part 3 I The if statement creates combinatorial logic that outputs a value depending upon the value of a conditional expression. I If the conditional expression evaluates true, the following signal assignment is made. Othewise, that assignment is skipped and the next statement is evaluated. I Note use of blocking assignment ...

  8. PDF Digital Logic & SystemVerilog

    This Unit: Digital Logic & Verilog Mem CPU I/O System software App App App 2. CIS 5710 | Prof Joseph Devietti •Digital logic •P&H, Appendix C ... •SV ?conditional assignment operator •aka ternary operator, as it takes 3 inputs •Much more useful (and common) in Verilog than in C/Java 21

  9. Conditional Statements

    A case statement tests and expression and then enumerates what actions should be taken for the various values that expression can take. For example: case (sel) 0: out = in0; 1: out = in1; 2: out = in2; 3: out = in3; endcase. If the needed case is not found, then no statements are executed.

  10. Verilog conditional assignments without using procedural blocks like

    You can simplify the procedural block by using an implicit sensitivity list, always @* in Verilog and preferably always_comb in SystemVerilog. And you should not be using non-blocking assignments in combinational logic. always_comb case (sel) 2'b00 : out = a; 2'b01 : out = b; 2'b10 : out = c; 2'b11 : out = d; endcase

  11. Storing value of a conditional assignment in Verilog

    Assume I have the follow assignment wire COND; assign COND = A & B; The values of A and B are changing between true and false, however, once they both hit 1 at the same time and COND = 1; I wish to keep this COND as a true value (sort of like a trigger) instead of it reverting to 0 when A and B changes.

  12. ?: conditional operator in Verilog

    Assigned Tasks. This assignment uses only a testbench simulation, with no module to implement. Open the file src/testbench.v and examine how it is organized. It uses the conditional operator in an always block to assign q = a^b (XOR) when enabled, else q= 0.. Run make simulate to test the operation. Verify that the console output is correct.

  13. Verilog if-else-if

    This conditional statement is used to make a decision on whether the statements within the if block should be executed or not.. If the expression evaluates to true (i.e. any non-zero value), all statements within that particular if block will be executed; If it evaluates to false (zero or 'x' or 'z'), the statements inside if block will not be executed; If there is an else statement and ...

  14. Conditional Assignment

    The concurrent conditional signal assignment statement is a shorthand for a collection of simple signal assignments contained in an if statement, which is in turn contained in a process statement. Let us look at some examples and show how each conditional signal assignment can be transformed into an equivalent process statement.

  15. Procedural Assignments

    Procedural Assignments are a central aspect of SystemVerilog that are used to define the behavior of variables within procedural blocks such as initial, always, task, and function.These types of assignments differ significantly from Continuous Assignments as they are executed sequentially, following the flow of control in the procedural block, and not instantly upon changes to dependent variables.

  16. Modeling Concurrent Functionality in Verilog

    Section 3.3: Continuous Assignment with Conditional Operators. 3.3.1. Design a Verilog model to implement the behavior described by the 3-input minterm list shown in Fig. 3.1. Use continuous assignment with conditional operators. Declare your module and ports to match the block diagram provided. Use the type wire for your ports. 3.3.2

  17. SystemVerilog for RTL Modeling, Simulation, and Verification

    3.2 Continuous Assignment. ... SystemVerilog supports conditional generate constructs, as long as they can be elaborated statically during compilation time. The "conditional" part is typically done via if or case statements where the condition expression value is known. For instance, we can do the following conditional generate:

  18. verilog

    @newbie: I don't think if-else versus conditional assignment affect synthesis. When it comes to debugging, it is much easier to set breakpoints on different sections of a nested if-else statement, but a conditional assignment is usually considered a single break point. ... system-verilog; or ask your own question. The Overflow Blog Upcoming ...

  19. Continuous Assignment and Combinational Logic in SystemVerilog

    This approach is known as explicit continuous assignment. The SystemVerilog code below shows the general syntax for continuous assignment using the assign keyword. assign <variable> = <value>; In this construct, we use the <variable> field to give the name of the signal which we are assigning data to.

  20. <= Assignment Operator in Verilog

    For example, in this code, when you're using a non-blocking assignment, its action won't be registered until the next clock cycle. This means that the order of the assignments is irrelevant and will produce the same result. The other assignment operator, '=', is referred to as a blocking assignment. When '=' assignment is used, for the purposes ...