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.

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

Coming Soon...

We are current preparing some exciting new premium content. Sign up to hear more.

Sign Up to our Mailing List

Join our mailing list and be the first to hear about our latest FPGA themed articles and tutorials .

Numbers in Verilog

Welcome to my ongoing series covering mathematics and algorithms with FPGAs. This series begins with the basics of Verilog numbers, then considers fixed-point, division, square roots and CORDIC before covering more complex algorithms, such as data compression.

In this first post, we consider integers, dig into the challenges of signed numbers and expressions, and then finish with a bit of arithmetic.

Share your thoughts with @WillFlux on Mastodon or Twitter . If you like what I do, sponsor me . 🙏

Series Outline

  • Numbers in Verilog (this post) - introduction to numbers in Verilog
  • Vectors and Arrays - working with Verilog vectors and arrays
  • Multiplication with DSPs - efficient FPGA multiplication
  • Fixed-Point Numbers - precision without complexity
  • Division in Verilog - divided we stand
  • More maths to follow

Representing Numbers

We’re so familiar with different representations of numbers we hardly give them a second thought.

Some representations of forty-two:

  • 101010 (binary)
  • 42 (decimal)
  • 0x2A (hexadecimal)
  • 4.2x10 1 (scientific notation)
  • XLII (Roman numerals)
  • 四二 (Japanese numerals)
  • zweiundvierzig (German)

Different representations express (almost) the same thing but work better (or worse) in different circumstances: hexadecimal is suitable for a memory address, while scientific notation compactly expresses vast and tiny numbers alike.

As a hardware designer, you need to consider how you represent numbers. How many bits do I need? Do I need signed numbers? Will BCD make my design simpler? Is fixed-point accurate enough? What happens when I mix different widths in one expression?

Cistercian Numerals For something a bit less ordinary, try Cistercian numerals (Wikipedia).

Computers famously “think” in binary, and the same is true for most electronics. Off and on, high and low. Simple, right?

For positive integers, things are pretty straightforward.

Let’s take a look at 42 in binary: 101010 2

Each binary digit is twice the previous one: 1, 2, 4, 8, 16, 32…

32 + 8 + 2 = 42

Forty-two requires at least six binary digits to represent in this way.

Binary Coded Decimal

But there are other possible representations: some systems use binary coded decimal (BCD). Packed 1 BCD uses a nibble (4-bit) value to represent each decimal digit.

To get the packed BCD representation, convert each decimal digit into a 4-bit binary value:

The BCD representation requires eight bits to represent 42, two more than the plain old binary version. However, there are advantages, including the ease of display (each nibble is one character) and the ability to accurately represent decimal numbers (such as 10.1).

On typical binary computers, BCD adds overhead for arithmetic operations. However, when designing your own hardware, you can support BCD directly in logic, making it an attractive option for straightforward numerical designs.

Wikipedia’s binary-coded decimal article covers different BCD variants and sign encoding.

Interesting as BCD is, we’ll be sticking with plain old binary for the rest of this document. Many of the concepts we’ll cover also apply to BCD.

Binary in Verilog

By default, a Verilog reg or wire is 1 bit wide. This is a scalar :

A scalar can only hold 0 or 1 (but see Four State Data Types below).

We need a vector to hold values other than 0 and 1.

A vector is declared like this: type [upper:lower] name;

a , b , and c are vectors:

  • Wire a handles 0-63 inclusive (2 6 is 64).
  • Reg b handles 0-255 inclusive (2 8 is 256).
  • Logic c handles 0-4095 inclusive (2 12 is 4096).

You need to ensure your vector is large enough to handle the full range of values your design requires. Synthesis tools are good at discarding unused bits, so it’s better to err on the side of too large rather than too small.

Deciding on the appropriate vector width for an algorithm requires an understanding of that algorithm. For example, I’ve worked with an ellipse drawing algorithm that required 48-bit internal vectors when using 16-bit coordinates.

It’s easy to miss the width from a signal declaration and create a scalar by mistake:

Alas, many tools provide no warning on width mismatches. To catch issues with bit widths, I strongly recommend you lint your designs with Verilator .

Four State Data Types The logic, reg, and wire data types can take one of four values: 0, 1, X, Z , where X is unknown, and Z is high impedance. For this introduction to numbers, we’re going to ignore X and Z .

We cover vectors in detail in the next part of the series: Vectors and Arrays .

Verilog has several options for interpreting literal numbers.

A lone unadorned number, such as 42 is interpreted as a signed 32-bit decimal :

The default interpretation seems reasonable, but sometimes it’s easier to work in another base, and Verilog gives you the option of binary, octal and hexadecimal as well as decimal.

You specify the base (radix) using a single quote followed by a letter:

  • d - decimal
  • h - hexadecimal

With hexadecimal, you can use the letters a-f and A-F as well as the usual digits:

Unlike the default decimal interpretation, these literals are unsigned , even with a decimal base.

If you want a signed literal with a base, you need to add an s before the base:

You can use a negative sign to create a negative literal. Stick with decimal for literals with minus signs: it quickly gets confusing in other bases.

We’ll be covering signed numbers in detail in the next section.

You specify a literal width in bits by putting it before the single quote:

ProTip: You can include underscores in your literals to improve readability.

What happens if you only specify some of the bits in a literal?

Verilog filled the remaining bits of 8'b1111 with zero, which is what you might expect and is how numbers usually work day-to-day.

It doesn’t matter if the literal is signed or another base; the value is always filled with zeros:

In case you’re wondering about sign extension, fear not we come to that later in this post.

Literal Tricks

In SystemVerilog, you can set all the bits of a vector to ‘1’:

You can also use the concat operator {} to set specific patterns in SystemVerilog and Verilog:

You can nest concat operators, as in the final example above.

Concat allows us to set an appropriate value regardless of the vector width.

Signed Numbers

If your design requires negative values, you need to handle signed numbers. The standard approach is two’s complement , as with almost all CPUs and software.

The Two’s Complement

With two’s complement, addition, subtraction, and multiplication all work as they do with positive binary numbers. But what is the two’s complement? The positive and negative two’s complement representations of an N-bit number add up to 2 N .

For example, with four-bit values: 7 is 0111 and -7 is 1001 because 0111 + 1001 = 10000 (2 4 ).

Discarding the extra bit, the result of adding a number and its two’s complement is always zero.

You can switch the sign of a two’s complement number by inverting the bits and adding one:

You rarely need to determine the two’s complement; Verilog can handle it for you.

Let’s look at a few additions to confirm things work as expected:

To learn more, check out the Wikipedia article on two’s complement .

The range of a two’s complement vector of width n is:

-2 (n-1) to +2 (n-1) -1

For example, an 8-bit signed vector has the range:

-2 7 to +2 7 -1 = -128 to +127

You can’t represent +128 with an 8-bit signed vector.

Try to take the two’s complement of -128:

You get the original -128 back.

Signing Your Signals

Declaring a vector as signed is easy:

For signed literals, as we discussed above, you add the s prefix to the base:

Test Negative

It’s straightforward to check the sign of a number with two’s complement:

  • For positive numbers (and zero), the most significant bit is 0
  • For negative numbers, the most significant bit is 1

You can check the most significant bit directly:

However, comparison operators are usually clearer:

Signed Expressions

Now we know how to handle signed vectors and literals, it’s time to wrestle with expressions. I’ve done my best to accurately distil a rather dry and complex subject into something palatable, so I hope I don’t offend the language lawyers and bore everyone else.

An expression consists of operands , such as variables and literals, and operators , such as addition and assignment.

Verilog uses the width of the widest operand when evaluating an expression.

It doesn’t matter what the operators are; all Verilog cares about is the width of the operands.

Narrower operands are widened until they’re the same width as the widest. For unsigned operands, Verilog simply fills the new bits with zero, but with signed operands, it uses sign extension .

Sign extension copies the most significant bit (MSB) to fill the width. Remember, for a signed number, the MSB is 1 for negative numbers and 0 otherwise.

This doesn’t sound too bad until you learn that in Verilog:

If all the operands are signed, the result is signed. Otherwise, it’s unsigned.

Verilog doesn’t consider it an error to mix signed and unsigned operands; it treats them all as unsigned. This leads to painful surprises that can be hard to debug.

Take a look at this example:

Running the wider test bench:

Looking at the binary, we can understand where 19 comes from.

When we set m = -4 it has binary value 1100 .

When Verilog evaluates the expression y = u + m :

  • m is 4 bits wide, but u and y are 8 bits
  • m must be widened to 8 bits to match the widest operands
  • u is unsigned, so m is also considered unsigned
  • Treated as unsigned, m is widened to 8 bits with zeros: 00001100 (12 in decimal)
  • 00001100 + 00000111 = 00010011 (12+7=19 in decimal)

If you take one thing away from this post:

Never mix signed and unsigned variables in one expression!

If the left-hand side of an assignment is smaller than the right-hand side, then the value is truncated. The following examples use literals, but this also applies to expressions and variables.

Because 15 has no base, Verilog treats it as a signed 32-bit decimal:

For a , the truncated bits are all zero, so don’t change the value.

If the right-hand side is signed, truncating it may change its value and sign :

We assigned -13 to e , but after truncation, we get +3.

Your synthesis tool or simulator should warn you about truncated values.

Reckoning with Arithmetic

We’ve talked a lot about the representation of numbers, but we’ve yet to do much maths. Let’s finish this post by having a quick look at the elementary arithmetic operations.

Modern FPGAs include dedicated logic, such as carry chains , to handle addition efficiently: there’s no need to roll your own design. However, there are plenty of university slide decks online if you want to create an adder from scratch.

Before we skip over addition entirely, it’s worth having a quick look at overflow.

Consider the following:

The signals x y z are unsigned and 4 bits wide.

If we set x = 2 and y = 9 then (x + y) = 11 or 1011 2 in binary.

z has the value 1011 2 : z = 11 as expected.

If we set x = 11 and y = 7 , you might expect the result to be (x + y) = 18 or 10010 2 in binary, which is then truncated to fit in z .

However, Verilog uses the width of the widest operand to evaluate expressions. The operands in our case are the three variables x , y , and z , all of which are 4 bits wide. Thus the result of the expression is 0010 2 , and it’s assigned to z .

Modular arithmatic , where the result wraps around, is the norm in hardware and software.

Catching the Overflow

However, there are times when you want to know if overflow has occurred:

The widest operand in our new expression is {c, z} , which is 5 bits wide, so when we evaluate x + y , we get 10010 2 . The value of 1 gets assigned to c while 0010 2 is assigned to z as before.

You could use the carry bit to set an overflow flag when designing a CPU.

Another potential use is with saturation arithmetic : we keep z at its maximum value when overflow occurs:

With this saturation design, if x = 11 and y = 7 , then z = 15 .

Learn more from saturation arithmetic on Wikipedia.

Subtraction

Subtraction is almost the same as addition: subtracting y is equivalent to adding -y . It’s easy to find -y , as we saw earlier when discussing two’s complement .

If we set x = 11 and y = 7 :

Your synthesis tool will handle this, but remember that subtraction is a little more complex than addition. Prefer incrementing over decrementing and register the results of subtractions before using them in subsequent calculations, especially on low-power FPGAs such as the iCE40UP.

Multiplication

Multiplication is more complex than addition or subtraction, but modern FPGAs can handle it with dedicated DSP blocks. Small vectors don’t require too much thought, but the resulting output has potentially twice as many bits as the inputs:

I’ve written a dedicated post on Multiplication with FPGA DSPs that looks at the use of reg and pipelining to improve multiplication performance and minimise logic use.

What about division? I’ve bad news: Verilog won’t synthesise this for you. The good news is that it’s not hard to implement yourself: I have a dedicated post on Division in Verilog that covers integers, fixed-point, and signed numbers.

What’s Next?

In the second part, we look at Vectors and Arrays or jump ahead to Fixed-Point Numbers .

Unpacked BCD uses a whole byte (8-bits) for each decimal digit.  ↩︎

Combinational Logic with assign

The verilog assign statement is typically used to continuously drive a signal of wire datatype and gets synthesized as combinational logic. Here are some more design examples using the assign statement.

Example #1 : Simple combinational logic

The code shown below implements a simple digital combinational logic which has an output wire z that is driven continuously with an assign statement to realize the digital equation.

The module combo gets elaborated into the following hardware schematic using synthesis tools and can be seen that the combinational logic is implemented with digital gates.

simple combinational logic with assign

The testbench is a platform for simulating the design to ensure that the design does behave as expected. All combinations of inputs are driven to the design module using a for loop with a delay statement of 10 time units so that the new value is applied to the inputs after some time.

verilog assign binary

Example #2: Half Adder

The half adder module accepts two scalar inputs a and b and uses combinational logic to assign the outputs sum and carry bit cout . The sum is driven by an XOR between a and b while the carry bit is obtained by an AND between the two inputs.

half adder circuit with assign

Example #3: Full Adder

A full adder can be built using the half adder module shown above or the entire combinational logic can be applied as is with assign statements to drive the outputs sum and cout .

full adder circuit with assign

Example #4: 2x1 Multiplexer

The simple 2x1 multiplexer uses a ternary operator to decide which input should be assigned to the output c . If sel is 1, output is driven by a and if sel is 0 output is driven by b .

2x1 multiplexer

Example #5: 1x4 Demultiplexer

The demultiplexer uses a combination of sel and f inputs to drive the different output signals. Each output signal is driven by a separate assign statement. Note that the same signal is generally not recommended to be driven by different assign statements.

1x4 demultiplexer

Example #6: 4x16 Decoder

4x16 decoder

404 Not found

Operators in Verilog

Verilog deals with the design of digital electronic circuits .  Describing a complex circuit in terms of gates ( gate-level modeling ) is a tedious task. Thus, we use a higher level of abstraction. This modeling, one above the gate-level, is known as dataflow modeling . In this level, we describe the flow of data from input to output.

To do this, dataflow modeling occasionally uses expressions, operands, and operators to describe an electronic circuit. For example, if we consider the dataflow model of an AND gate:

From the above code, we can see that it consists of an expression a & b with two operands a and b and an operator & .

In this article, we are we will be looking at all the operators in Verilog . We will be using almost all of these Verilog operators extensively throughout this Verilog course . The application of these operators will allow you to internalize their use-case scenarios and syntax.

An operator, in many ways, is similar to a simple mathematical operator. They receive one or two inputs and generate a single output. Operators enable synthesis tools to choose the desired hardware elements.

We can categorize operators based on:

  • Number of Operands

Operators in Verilog based on the number of operands

Depending on how many operands are used in an expression, operators are classified as:

Unary operators

Unary operators need only one operand. For example, arithmetic operators for representing sign (+,-), negation operator (!,~), reduction operator (&, |, ~, ^).

Binary operators

A binary operator requires two operands to perform operations. The majority of the operators available in Verilog requires two operands. Addition, logic operations, multiplication, etc.

Ternary operators

Ternary operators require three operands. For example, conditional operator(? :).

We will look at examples for each of the above in detail as we proceed.

Operators in Verilog based on Operation

We can also classify operators based on what operation they perform on given data.

Arithmetic operators

This operator is gonna take us to good old school days.

Arithmetic operators are used to perform basic math calculations. They perform a specific operation on two numeric values and return a single numeric value.

Arithmetic operators play a significant part in describing hardware units such as Arithmetic logic Unit (ALU). Therefore, Verilog has provided us with the following arithmetic operators.

What about signed numbers?

Verilog also has the provision for representing signed numbers. We use ‘+’ and ‘-‘ to represent positive and negative numbers. In short, ‘+’ and ‘-‘ can be used in both unary and binary form. Unary is for representing signed number and binary for calculations.

Now that we have an idea of what arithmetic operators are, we can see how they are used in Verilog using a sample program as an example.

Simulation log

Logical Operators

Logical operators perform a logical operation on the logical value of the operands and tell you whether it is true or false, i.e., it returns a boolean value. For example:

Let’s say we have to perform logical and operation between 3 (non-zero) and 0 (zero). Hence, Logical value of 3 is true(1) and for 0, it is false(0). Therefore, AND of true and false will give you false (0).

These are the logical operators available in Verilog.

Here’s how Verilog performs logical operations.

Bit-wise Operators

Verilog supports the use of a bit-wise operator. This operator is a bit of an odd cross between a logical operator and an arithmetic operator. They take each bit in one operand and perform the operation with the corresponding bit in the other operand. If one of the operands is shorter than the other, the length will be made the same by adding zeros on the shorter operand. It’s a bit confusing. Check out the example below.

For example: bitwise AND of a = 3(11) and b=2 (00). Bitwise AND is similar to concatenating a[1] & b[1] and a[0] & b[0]  which gives a result 00.

We will get a better understanding when we go through the simulated output of this code.

Reduction Operators

Unlike logical and bitwise logical operators, the Reduction operator is a unary operator. This operand is useful for converting a multi-bit vector into a single bit scalar value. It performs bit by bit logical operation on the vector operand and returns a boolean value.

For example,

Verilog has provided us with the following types of reduction operators.

Here’s the code for understanding how reduction operator is described in Verilog

Difference between logical, bitwise logical, and reduction operators?

In short, even though the functionalities look similar, there is a difference in how the above operators perform on the operands.

Relational operators

If we want to check the relation between the given operands, then we use relational operators. Relational operators test the relation between operands and return a 1 or 0.

An example code will help us to understand how relational operators work in Verilog.

Equality Operator

Like Relational operators, Equality operators are also used for relation checking. These operators test whether the operands are the same or not. They return 1 if both the operands are the same and 0 if they are not.

A list of equality operators in Verilog is given below.

As per the table, we can see that there are two types of equality operators:

  • Logical Equality (==,!==): In this case, if one of the operand bits has an x(unknown) or z(high impedance), the resultant will be x.
  • Case Equality (===,!===): Here, the x(unknown) and z(high impedance) in operand bits are included during the comparison. So the result will be either true (1) or false (0).

Let’s see how Verilog performs equality operation with the help of the code below:

Shift Operators

Shift operators are used to shift data in a variable. This operator is essential for modeling hardware elements like shift registers , shift and add multipliers, etc.

There are two types of shift operations:

  • Logical shift: they shift the input and pad with zeros. For example, shift 1000 right twice will result in 0010.
  • Arithmetic shift: they preserve the sign of MSB of our variable we need to shift. For example, arithmetic shift 1000 to the right twice will be 1110.

The shift operators provided in Verilog are:

We will be able to gain a clear understanding of how a shift operator works in Verilog from the below code:

Concatenation Operators

Concatenation operators are used to join different bits of data into one. Concatenations are expressed using the brace characters { }, with commas separating the expressions within.

We will get a better understanding of the working of the concatenation operator from the simulated output of the code below.

Replication operator

The replication operator is used to replicate a group of bits n times. It takes the format {n{m}}, where n indicates replication multiplier i.e., how many times m should be repeated.

For example, in {3{2’b01}} 3 is the repetition multiplier and 2’b01 is what will be replicated 3 times.

Look at the Verilog code and simulated output below to see how the replication operator works.

Conditional Operator

The conditional operator selects an expression for evaluation depending on the value of the condition.

If the condition is evaluated as false (or zero value), then false_expression is evaluated and used as a result of an entire expression.

For example

The above statement means that out will be assigned data1 if enable is true(1) or zero if enable is false(0).

Verilog makes use of the conditional operator in order to build tri-state buffers and multiplexers .

Let’s see how the conditional operator can be used practically.

Operator Precedence in Verilog

We have discussed the different operators that we can use in Verilog. Is it possible to use multiple operators in a single expression?  Undoubtedly, yes. Then how do we choose which operation to perform first?

That is when the operator precedence table comes to play.

This table describes the order in which the operators are executed.

For example:

But, it is better to use brackets rather than depending entirely on the precedence of operators. This will ensure the readability of the expression and correctness of the result.

Tabular summary

So, we have gone through all the operators that Verilog has provided. Let’s summarize the operators that we have learned.

Keep the summary table of the Verilog operators above handy. Operators are like tools that help you implement your logic. If you ever feel stuck, take a glance at your tools and they might offer you a way out of your logical quandary.

About the author

Related courses to Operators in Verilog

CMOS - IC Design Course

A free course as part of our VLSI track that teaches everything CMOS. Right from the physics of CMOS to designing of logic circuits using the CMOS inverter.

VHDL Course

A free and complete VHDL course for students. Learn everything from scratch including syntax, different modeling styles with examples of basic circuits.

Digital Electronics Course

A free course on digital electronics and digital logic design for engineers. Everything is taught from the basics in an easy to understand manner.

Very good inputs/work Thank you very much

Glad you enjoyed it Shashidhara! Check out our free Verilog course here .

Leave a Reply Cancel reply

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

  • Verilog-AMS Tutorials
  • Modeling Digital Buses in Verilog-A

Modeling Digital Buses in Verilog-A 

It is generally preferred to use Verilog-AMS when simulating mixed-signal systems. However, sometimes that is not possible. This tutorial demonstrates a reasonably efficient and robust way to convert the value of an electrical bus into an integer. Before starting this tutorial, you should read Functional Modeling .

Input Buses 

There are a two challenges that must be overcome to implement electrical digital input buses in Verilog-A:

The threshold crossings must be resolved to avoid undesirable delays in recognizing the value of the bus.

The individual bit values must be combined into a single integer.

Consider the following model of an 8-bit DAC:

This model starts by applying a threshold to the individual lines in an electrical array and combining the resulting Booleans into an integer. The electrical array is in and the integer is code . The if statement computes the sign of the integer. It is only needed if in represents a signed number. Specifically, it should be deleted in in is an unsigned number.

The next block of code implements the enable feature and scales the output to the desired range.

The final block of code drives the differential outputs with the computed result. Notice that transition functions are used to smooth the otherwise discontinuous output signal.

The conversion from electrical signal to Booleans includes use of cross functions to assure that the threshold crossings are properly resolved.

The for loop in the first block is genvar loop. Such loops are fully expanded before the simulation begins. That loop expands as follows:

This DAC updates immediately after the input changes. It is possible to modify this model so it updates on a clock edge:

In this model the input in is sampled on the rising edge of the clock and it is the sampled value that drives the output.

Output Buses 

The challenges that must be overcome to implement electrical digital output buses in Verilog-A are:

An integer must be decomposed into its individual bits.

The abrupt and discontinuous transitions in the value of the output bits must be smoothed with well controlled.

This process is demonstrated with the following model of an 6-bit ADC:

The first part of this model is an event block that samples the voltage of the input pin at rising edges of the clock. The actual analog-to-digital conversion is performed by offsetting and scaling the input voltage and then casting the value to an integer. This line converts input values from the range −1 V ⋯ 1 V to integers in the range 0 ⋯ 64. Finally, the integer is then clipped so that its value always falls within the range 0 to 63, the normal operating range of the ADC. You can eliminate the +1 to get a signed output, but you must also change the clipping range.

The second part of the model is a genvar loop that splits result into its individual bits and drives them onto the output bus. This is performed with the help of the left-shift operator, the bitwise and operator, and the inline conditional operator. To see how this works, expand the genvar loop:

Now, evaluate the left shift operator:

Assume that the value of result is 58, which in binary is ‘b111010. Consider bit 0. ‘b111010 & ‘b000001 is ‘b000000, which is a logical false and so the inline conditional operator evaluates to its third argument, 0. Now consider bit 1. ‘b111010 & ‘b000010 is ‘b000010, which is a logical true and so the inline conditional operator evaluates to its second argument, vh . You can follow this logic to its conclusion to see that the values of the output bus will be { vh , vh , vh , 0, vh , 0}.

Finally, the transition function smooths and controls each output transition.

IMAGES

  1. verilog code for binary to gray converter with testbench and viceversa

    verilog assign binary

  2. Cadence Decimal to Binary Decoder using Verilog

    verilog assign binary

  3. Solved Verilog code with comments for the 2:4 binary

    verilog assign binary

  4. binary counter design by verilog in xilinx project navigator

    verilog assign binary

  5. Solved Write a test bench for the following verilog code (

    verilog assign binary

  6. Simulate verilog binary to BCD converter in Vivado!

    verilog assign binary

COMMENTS

  1. binary

    "assign" is used for net type declarations (Wire,Tri etc).Since wires change values according to the value driving them, whenever the operands on the RHS changes,the value is evaluated and assigned to LHS (simulating a wire) always - is used for registers + combinational logic.

  2. PDF Verilog

    "....... And here there be monsters!" (Capt. Barbossa) Numbers are represented as: <size>'<signed><radix>value ("<>" indicates optional part) size The number of binary bits the number is comprised of. Not the number of hex or decimal digits. Default is 32 bits. ' A separator, single quote, not a backtick signed Indicates if the value is signed.

  3. An Introduction to Verilog Data Types and Arrays

    <bits>'<representation><value> We use the <bits> field to indicate the number of bits in the data that we are representing. We use the <representation> field to indicate how our data is represented. This field can be set to b (for binary), h (for hex), o (for octal) or d (for decimal).

  4. Verilog assign statement

    In Verilog, this concept is realized by the assign statement where any wire or other similar wire like data-types can be driven continuously with a value. The value can either be a constant or an expression comprising of a group of signals. Assign Syntax

  5. How to specify a value for each bit of the reg in Verilog?

    I want to declare a reg of 8 bits and set the value for each one of the bits separately (based on another "counter" reg) inside an always block using Verilog. Here is what I thought: module readv...

  6. PDF Verilog

    There are two types of operators: binary and unary Binary operators: add(+), subtract(-), multiply(*), divide(/), power(**), modulus(%) //suppose that: a = 4'b0011; // // b = 4'b0100; d = 6; e = 4; f = 2; //then, + b //add a and b; evaluates to 4'b0111 - a //subtract a from b; evaluates to 4'b0001 * b //multiply a and b; evaluates to 4'b1100

  7. An introduction to SystemVerilog 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.

  8. Numbers in Verilog

    Published 30 Sep 2021 · Updated 01 Mar 2023 Welcome to my ongoing series covering mathematics and algorithms with FPGAs. This series begins with the basics of Verilog numbers, then considers fixed-point, division, square roots and CORDIC before covering more complex algorithms, such as data compression.

  9. PDF Intro to Verilog

    Microsoft PowerPoint - L03_Verilog v2.pptx. Intro to Verilog. • Wires - theory vs reality (Lab1) • Hardware Description Languages. • Verilog -- structural: modules, instances -- dataflow: continuous assignment -- sequential behavior: always blocks -- pitfalls -- other useful features. Reminder: Lab #1 due by 9pm tonight.

  10. verilog

    2 Answers Sorted by: 2 Nothing says that you always need to specify all the zeros in binary base. In fact you can write it like this: reg [31:0] COUNT = 32'd0; ....using a decimal base. The Verilog will extend that decimal zero out to the specified 32 bit width. Share Cite Follow answered Feb 11, 2017 at 10:24 Michael Karas 57.1k 3 71 138 1

  11. Verilog Assignments

    force release Placing values onto nets and variables are called assignments. There are three basic forms: Procedural Continuous Procedural continuous Legal LHS values An assignment has two parts - right-hand side (RHS) and left-hand side (LHS) with an equal symbol (=) or a less than-equal symbol (<=) in between.

  12. Combinational Logic with assign

    The verilog assign statement is typically used to continuously drive a signal of wire datatype and gets synthesized as combinational logic. Here are some more design examples using the assign statement. Example #1 : Simple combinational logic

  13. How do we initialise unpacked arrays in Verilog?

    For Verilog, you have to initialise each element in the array one by one: b [0] = 1'b0; b [1] = 1'b0; b [2] = ... You could also use a for -loop and localparam to initialise it, by storing the packed initialisation value in the localparam, then using the for -loop to copy it in to your unpacked array. As a bonus, the loop can be parameterised ...

  14. An Begin to Verilog Data Types and Arrays

    <bits>'<representation><value> We use the <bits> field to indicate the number the bits in the data that we are representing. We used the <representation> province to indicate how our data is represents. Get field can be set to b (for binary), h (for hex), o (for octal) or d (for decimal).

  15. PDF Verilog 1

    Verilog 1 - Fundamentals UCSD CSE 141L - Taylor Heavily modified; descended from MIT's 6.375. ... Binary literals ... Base format (d,b,o,h)‏ Decimal number representing size in bits We'll learn how to actually assign literals to nets a little later . Verilog Fundamentals !

  16. Operators in Verilog

    Binary operators Ternary operators Operators in Verilog based on Operation Arithmetic operators Logical Operators Bit-wise Operators Reduction Operators Difference between logical, bitwise logical, and reduction operators? Relational operators Equality Operator Shift Operators Concatenation Operators Replication operator Conditional Operator

  17. Modeling Digital Buses in Verilog-A

    The challenges that must be overcome to implement electrical digital output buses in Verilog-A are: An integer must be decomposed into its individual bits. ... Assume that the value of result is 58, which in binary is 'b111010. Consider bit 0. 'b111010 & 'b000001 is 'b000000, which is a logical false and so the inline conditional ...

  18. quartus

    1 Answer Sorted by: 0 reg [255:0] myname = 01001110011010010110101101101111011011000110111101111010; In this case you have a huge decimal number 1,001,110,011,010,010,110,101,101,101,111,011,011,000,110,111,101,111,010 which you try to assign to a relatively small reg (256 bit wide).

  19. Easy way to assign values to an array in Verilog?

    Easy way to assign values to an array in Verilog? Ask Question Asked 8 years, 9 months ago Modified 9 months ago Viewed 34k times -1 So I'm creating a large FIR filter in Verilog, it has 256 taps. So I need 256 coefficients.