Verilog

Contents

  1. 1 Verilog Backgrounder
    1. 1.1 What is Verilog?
    2. 1.2 A Brief History of Verilog
    3. 1.3 Design Flow using Verilog
      1. 1.3.1 System-level Verification
      2. 1.3.2 RTL design and testbench creation
      3. 1.3.3 RTL verification
      4. 1.3.4 Look-ahead Synthesis
    4. 1.4 Levels of Abstraction
    5. 1.5 Scope of Verilog
      1. 1.5.1 Design process
      2. 1.5.2 System level
      3. 1.5.3 Digital
      4. 1.5.4 Analog
    6. 1.6 Synthesizing Verilog
  2. 2 Designing Hardware using Verilog
    1. 2.1 A Simple Design
      1. 2.1.1 Verilog: an AOI gate module
      2. 2.1.2 Comments
      3. 2.1.3 Module and Port declarations
      4. 2.1.4 Endmodule
      5. 2.1.5 Functionality
      6. 2.1.6 Verilog 1995
    2. 2.2 Wires
      1. 2.2.1 Verilog: Internal signals of an AOI gate module
      2. 2.2.2 Wire Declarations
      3. 2.2.3 Continuous Assignments
    3. 2.3 Wire Assignments
      1. 2.3.1 Verilog: Using wire assignments to describe an AOI gate module
    4. 2.4 A Design Hierarchy
      1. 2.4.1 Module Instances
      2. 2.4.2 Verilog: 2-input multiplexer module
      3. 2.4.3 Implicit Wires
      4. 2.4.4 Module Instances
    5. 2.5 Test Benches
      1. 2.5.1 Initial Statement
    6. 2.6 Response Capture
    7. 2.7 RTL Verilog
      1. 2.7.1 Always blocks
    8. 2.8 If statement
      1. 2.8.1 Variable declaration
      2. 2.8.2 Combinational logic
      3. 2.8.3 If statement
      4. 2.8.4 Synthesis considerations
    9. 2.9 Synthesizing Latches
      1. 2.9.1 If Statements
      2. 2.9.2 Incomplete Assignment
      3. 2.9.3 Simplifying code analysis
      4. 2.9.4 Default Assignment
  3. 3 Useful Verilog Tips
    1. 3.1 Top-down Design and Synthesis Issues for Sequential Always Blocks
    2. 3.2 Think Before You Code
  4. 4 Other Verilog Resources
    1. 4.1 Verilog Example Models
      1. 4.1.1 Analog-to-Digital Converter
      2. 4.1.2 Shift Register
      3. 4.1.3 Simple RAM Model
      4. 4.1.4 Universal Asynchronous Receiver (UAR)
    2. 4.2 8-bit x 8-bit Pipelined Multiplier
    3. 4.3 Verilog FAQ
      1. 4.3.1 What is the difference between Verilog and VHDL?
      2. 4.3.2 What versions of Verilog are there?
      3. 4.3.3 Can I use Verilog for the analog part of a design?
      4. 4.3.4 How must I write Verilog to make it synthesisable?
      5. 4.3.5 A Verilog design can be moved to any tool or technology. Right?
      6. 4.3.6 I can see how to write abstract behavioural descriptions in Verilog, but how do you describe and simulate the actual hardware?
      7. 4.3.7 What is Synthesis?
      8. 4.3.8 How about on-line information resources?
  5. 5 Copyright

Verilog Backgrounder

What is Verilog?

Verilog is a Hardware Description Language; a textual format for describing electronic circuits and systems. Applied to electronic design, Verilog is intended to be used for verification through simulation, for timing analysis, for test analysis (testability analysis and fault grading) and for logic synthesis.

The Verilog HDL is an IEEE standard - number 1364. The first version of the IEEE standard for Verilog was published in 1995. A revised version was published in 2001; this is the current version. The IEEE Verilog standard document is known as the Language Reference Manual, or LRM. This is the complete authoritative definition of the Verilog HDL.

A further revision of the Verilog standard is expected to be published in 2005. Accellera - the organisation that oversees developments in the Verilog language - has also published the SystemVerilog extensions to Verilog, which is also expected to become an IEEE standard in 2005. See the appropriate Knowhow section for more details about SystemVerilog.

IEEE Std 1364 also defines the Programming Language Interface, or PLI. This is a collection of software routines which permit a bidirectional interface between Verilog and other languages (usually C).

Note that VHDL is not an abbreviation for Verilog HDL - Verilog and VHDL are two different HDLs. They have more similarities than differences, however.

A Brief History of Verilog

The history of the Verilog HDL goes back to the 1980s, when a company called Gateway Design Automation developed a logic simulator, Verilog-XL, and with it a hardware description language.

Cadence Design Systems acquired Gateway in 1989, and with it the rights to the language and the simulator. In 1990, Cadence put the language (but not the simulator) into the public domain, with the intention that it should become a standard, non-proprietary language.

The Verilog HDL is now maintained by a non profit making organisation, Accellera, which was formed from the merger of Open Verilog International (OVI) and VHDL International. OVI had the task of taking the language through the IEEE standardisation procedure.

In December 1995 Verilog HDL became IEEE Std. 1364-1995. A revised version was published in 2001: IEEE Std. 1364-2001. This is the current version, although a futher revision is expected in 2005.

Accellera have also been developing a new standard, SystemVerilog, which extends Verilog. SystemVerilog is also expected to become an IEEE standard in 2005. For more details, see the Systemverilog section of KnowHow

There is also a draft standard for analog and mixed-signal extensions to Verilog, Verilog-AMS.

Design Flow using Verilog

The diagram below summarises the high level design flow for an ASIC (ie. gate array, standard cell) or FPGA. In a practical design situation, each step described in the following sections may be split into several smaller steps, and parts of the design flow will be iterated as errors are uncovered.

System-level Verification

As a first step, Verilog may be used to model and simulate aspects of the complete system containing one or more ASICs or FPGAs. This may be a fully functional description of the system allowing the specification to be validated prior to commencing detailed design. Alternatively, this may be a partial description that abstracts certain properties of the system, such as a performance model to detect system performance bottle-necks.

Verilog is not ideally suited to system-level modelling. This is one motivation for SystemVerilog, which enhances Verilog in this area.

RTL design and testbench creation

Once the overall system architecture and partitioning is stable, the detailed design of each ASIC or FPGA can commence. This starts by capturing the design in Verilog at the register transfer level, and capturing a set of test cases in Verilog. These two tasks are complementary, and are sometimes performed by different design teams in isolation to ensure that the specification is correctly interpreted. The RTL Verilog should be synthesizable if automatic logic synthesis is to be used. Test case generation is a major task that requires a disciplined approach and much engineering ingenuity: the quality of the final ASIC or FPGA depends on the coverage of these test cases.

For today's large, complex designs, verification can be a real bottleneck. This provides another motivation for SystemVerilog - it has features for expediting testbench development. See the SystemVerilog section of Knowhow for more details.

RTL verification

The RTL Verilog is then simulated to validate the functionality against the specification. RTL simulation is usually one or two orders of magnitude faster than gate level simulation, and experience has shown that this speed-up is best exploited by doing more simulation, not spending less time on simulation.

In practice it is common to spend 70-80% of the design cycle writing and simulating Verilog at and above the register transfer level, and 20-30% of the time synthesizing and verifying the gates.

Look-ahead Synthesis

Although some exploratory synthesis will be done early on in the design process, to provide accurate speed and area data to aid in the evaluation of architectural decisions and to check the engineer's understanding of how the Verilog will be synthesized, the main synthesis production run is deferred until functional simulation is complete. It is pointless to invest a lot of time and effort in synthesis until the functionality of the design is validated.

Levels of Abstraction

Verilog descriptions can span multiple levels of abstraction i.e. levels of detail, and can be used for different purposes at various stages in the design process.

At the highest level, Verilog contains stochastical functions (queues and random probability distributions) to support performance modelling.

Verilog supports abstract behavioural modeling, so can be used to model the functionality of a system at a high level of abstraction. This is useful at the system analysis and partitioning stage.

Verilog supports Register Transfer Level descriptions, which are used for the detailed design of digital circuits. Synthesis tools transform RTL descriptions to gate level.

Verilog supports gate and switch level descriptions, used for the verification of digital designs, including gate and switch level logic simulation, static and dynamic timing analysis, testability analysis and fault grading.

Verilog can also be used to describe simulation environments; test vectors, expected results, results comparison and analysis.

With some tools, Verilog can be used to control simulation e.g. setting breakpoints, taking checkpoints, restarting from time 0, tracing waveforms. However, most of these functions are not included in the 1364 standard, but are proprietary to particular simulators. Most simulators have their own command languages; with many tools this is based on Tcl, which is an industry-standard tool language. See the Knowhow section on Tcl/Tk for more details.

Scope of Verilog

Verilog can be used at different levels of abstraction as we have already seen. But how useful are these different levels of abstraction when it comes to using Verilog?

Design process

The diagram below shows a very simplified view of the electronic system design process incorporating Verilog. The central portion of the diagram shows the parts of the design process which will be impacted by Verilog.

System level

Verilog is not ideally suited for abstract system-level simulation, prior to the hardware-software split. This is to some extent addressed by SystemVerilog. Unlike VHDL, which has support for user-defined types and overloaded operators which allow the designer to abstract his work into the domain of the problem, Verilog restricts the designer to working with pre-defined system functions and tasks for stochastic simulation and can be used for modelling performance, throughput and queueing but only in so far as those built-in langauge features allow. Designers occasionally use the stochastic level of abstraction for this phase of the design process.

Digital

Verilog is suitable for use today in the digital hardware design process, from functional simulation, manual design and logic synthesis down to gate-level simulation. Verilog tools provide an integrated design environment in this area.

Verilog is also suited for specialized implementation-level design verification tools such as fault simulation, switch level simulation and worst case timing simulation. Verilog can be used to simulate gate level fanout loading effects and routing delays through the import of SDF files.

The RTL level of abstraction is used for functional simulation prior to synthesis. The gate level of abstraction exists post-synthesis but this level of abstraction is not often created by the designer, it is a level of abstraction adopted by the EDA tools (synthesis and timing analysis, for example).

Analog

Because of Verilog's flexibility as a programming language, it has been stretched to handle analog simulation in limited cases. There is a draft standard – Verilog-AMS – that addresses analog and mixed signal simulation.

Synthesizing Verilog

How do you write good synthesisable Verilog code to give you the hardware you want?

Synthesis is a broad term often used to describe very different tools. Synthesis can include silicon compilers and function generators used by ASIC vendors to produce regular RAM and ROM type structures. Synthesis in the context of this tutorial refers to generating random logic structures from Verilog descriptions. This is best suited to gate arrays and programmable devices such FPGAs.

Synthesis is not a panacea! It is vital to tackle High Level Design using Verilog with realistic expectations of synthesis.

The definition of Verilog for simulation is cast in stone and enshrined in the Language Reference Manual. Other tools which use Verilog, such as synthesis, will make their own interpretation of the Verilog language. There is an IEEE standard for Verilog synthesis (IEEE Std. 1364.1-2002) but no vendor adheres strictly to it.

It is not sufficient that the Verilog is functionally correct; it must be written in such a way that it directs the synthesis tool to generate good hardware, and moreover, the Verilog must be matched to the idiosyncrasies of the particular synthesis tool being used. We shall tackle some of these idiosyncracies in this Verilog tutorial.

There are currently three kinds of synthesis:

  • behavioural synthesis
  • high-level synthesis
  • RTL synthesis

There is some overlap between these three synthesis domains. We will concentrate on RTL synthesis, which is by far the most common. The essence of RTL code is that operations described in Verilog are tied to particular clock cycles. The synthesised netlist exhibits the same clock-by-clock cycle behaviour, allowing the RTL testbench to be easily re-used for gate-level simulation.

Designing Hardware using Verilog

A Simple Design

A design is described in Verilog using the concept of a module. A module can be conceptualised as consisting of two parts, the port declarations and the module body. The port declarations represent the external interface to the module. The module body represents the internal description of the module - its behaviour, its structure, or a mixture of both. Let's imagine we want to describe an and-or-invert (AOI) gate in Verilog.

Verilog: an AOI gate module

// Verilog code for AND-OR-INVERT gate
module AOI (input A, B, C, D, output F);
assign F = ~((A & B) | (C & D));
endmodule
// end of Verilog code

OK, that's the code. Let's dissect it line by line...

Comments

// Verilog code for AND-OR-INVERT gate

Like all programming languages, Verilog supports comments. There are two types of comment in Verilog, line comments and block comments; we will look at line comments for now. Comments are not part of the Verilog design, but allow the user to make notes referring to the Verilog code, usually as an aid to understanding it. Here the comment is a “header” that tells us that the Verilog describes an AOI gate. It is no more than an aide de memoire in this case. A Verilog compiler will ignore this line of Verilog. Two forward slashes mark the start of a line comment, which is ignored by the Verilog compiler. A line comment can be on a separate line or at the end of a line of Verilog code, but in any case stops at the end of the line.

Module and Port declarations

module AOI (input A, B, C, D, output F);

The name of the module is just an arbitrary label invented by the user. It does not correspond to a name pre-defined in a Verilog component library. module is a Verilog keyword. This line defines the start of a new Verilog module definition. All of the input and output ports of the module must appear in parentheses after the module name. The ordering of ports is not important for the module definition per se, although it is conventional to specify input ports first.

A port may correspond to a pin on an IC, an edge connector on a board, or any logical channel of communication with a block of hardware. The port declarations include the names of the ports ( e.g., A, B ), and the direction that information is allowed to flow through the ports (input, output or inout).

Endmodule

endmodule

The module definition is terminated by the Verilog keyword endmodule.

Functionality

Well, that's the interface to the module taken care of, but what about it's functionality?

assign F = ~((A & B) | (C & D));

In this module body, there is but one statement, and all the names referenced in this statement are in fact the ports of the design. Because all of the names used in the module body are declared in the module header and port declarations, there are no further declarations for internal elements required in the module body. assign is a Verilog keyword. It denotes a concurrent continuous assignment, which describes the functionality of the module. The concurrent assignment executes whenever one of the four ports A, B, C or D change value. The ~, & and | symbols represent the bit-wise not, and and or operators respectively, which are built in to the Verilog language. That's it! That's all there is to describing the functionality of an AOI gate in Verilog.

// end of Verilog code

Another Verilog comment, and that's the end of a Verilog description for an AOI gate.

Verilog 1995

The above example is written using Verilog-2001 syntax. Many people continue to use the 1995 syntax, which is still allowed in Verilog-2001. In Verilog-1995 the module header would look like this:

module AOI (A, B, C, D, F);
input A, B, C, D;
output F;

Note that the port names are listed after the module name, and declared as inputs and outputs in separate statements. The port declarations must repeat the names of the ports in the module header.

Wires

The module shown on the “Modules” page, was simple enough to describe using a continuous assignment where the output was a function of the inputs. Usually, modules are more complex than this, and internal connections are required. To make a continuous assignment to an internal signal, the signal must first be declared as a wire.

A Verilog wire represents an electrical connection.

Verilog: Internal signals of an AOI gate module

// Verilog code for AND-OR-INVERT gate
module AOI (input A, B, C, D, output F);
wire F; // the default
wire AB, CD, O; // necessary

assign AB = A & B;
assign CD = C & D;
assign O = AB | CD;
assign F = ~O;
endmodule
// end of Verilog code

OK, that's the code. Let's examine it a little more closely...

Wire Declarations

wire AB, CD, O;

This is the syntax for a wire declaration. A wire declaration looks like a Verilog-1995 style port declaration, with a type (wire), an optional vector width and a name or list of names. You can create separate wire declarations if you wish, for example:

wire AB, CD;
wire O;

is an alternative way of creating wire declarations.

Note that ports default to being wires, so the definition of wire F in the Verilog code is optional.

Continuous Assignments

assign AB = A & B;
assign CD = C & D;
assign O = AB | CD;
assign F = ~O;

In this module body, there are four continuous assignment statements. These statements are independent and executed concurrently. They are not necessarily executed in the order in which they are written. This does not affect the functionality of the design. Suppose assign AB = A & B; changes value. This causes B to be evaluated. If AB changes as a result then assign O = AB | CD; is evaluated. If O changes value then assign F = ~O; will be evaluated; possibly the output of the module will change due to a change on B.

Wire Assignments

A wire can be declared and continuously assigned in a single statement - a wire assignment. This is a shortcut which saves declaring and assigning a wire separately. There are no advantages or disadvantages between the two methods other than the obvious difference that wire assignments reduce the size the the text.

Later on we will discuss delays on assignments and wires. A delay in a wire assignment is equivalent to a delay in the corresponding continuous assignment, not a delay on the wire. Thus it could be necessary to separate the wire declaration from the continuous assignment to put the delay onto the wire rather than the assignment. Note that this is a subtle point that you are unlikely to encounter in practice!

Verilog: Using wire assignments to describe an AOI gate module

// Verilog code for AND-OR-INVERT gate
module AOI (input A, B, C, D, output F);
/* start of a block comment
wire F;
wire AB, CD, O;
assign AB = A & B;
assign CD = C & D;
assign O = AB | CD;
assign F = ~O;
end of a block comment */

// Equivalent...
wire AB = A & B;
wire CD = C & D;
wire O = AB | CD;
wire F = ~O;
endmodule
// end of Verilog code

So in this sample code, each of the wire declarations and its corresponding assign statement are effectively merged into one wire assignment.

Note the use of a block comment in the Verilog code, rather than the line comments we have seen so far. A block comment may span several lines of code. Block comments may not be nested.

A Design Hierarchy

Modules can reference other modules to form a hierarchy. Here we see a 2:1 multiplexer with an inverting data path consisting of an AOI gate and a pair of inverters.

Module Instances

The MUX_2 module contains references to each of the lower level modules, and describes the interconnections between them. In Verilog jargon, a reference to a lower level module is called a module instance.

Each instance is an independent, concurrently active copy of a module. Each module instance consists of the name of the module being instanced (e.g. AOI or INV), an instance name (unique to that instance within the current module) and a port connection list.

The module port connections can be given in order (positional mapping), or the ports can be explicitly named as they are connected (named mapping). Named mapping is usually preferred for long connection lists as it makes errors less likely.

Verilog: 2-input multiplexer module

// Verilog code for 2-input multiplexer
module INV (input A, output F); // An inverter
assign F = ~A;
endmodule

module AOI (input A, B, C, D, output F);
assign F = ~((A & B) | (C & D));
endmodule

module MUX2 (input SEL, A, B, output F); // 2:1 multiplexer
// wires SELB and FB are implicit
// Module instances...
INV G1 (SEL, SELB);
AOI G2 (SELB, A, SEL, B, FB);
INV G3 (.A(FB), .F(F)); // Named mapping
endmodule
// end of Verilog code

Yes, it's time to dissect the code line by line again, but we'll concentrate on the new lines as the module interface has been covered before (see A Simple Design).

Implicit Wires

// wires SELB and FB are implicit

The wires used in continuous assignments MUST be declared. However, one-bit wires connecting component instances together do not need to be declared. Such wires are regarded as implicit wires. Note that implicit wires are only one bit wide, if a connection between two components is a bus, you must declare the bus as a wire.

Module Instances

AOI G2 (SELB, A, SEL, B, FB);

In a module instance, the ports defined in the module interface are connected to wires in the instantiating module through the use of port mapping. For the instance of AOI, the first wire in the port list is SELB. In the module header for the AOI gate, A is the first port in the port list, so SELB is connected to A. The second port in the module header is B, the second wire in the port list is A, thus the wire A in MUX2 is connecyted to the port B of the AOI gate instance.

INV G3 (.A(FB), .F(F));

The second INV instance, G3, uses named mapping rather than positional mapping. In the port list for the G£ instance, the wire FB is connected to the input port, A, of the INV instance. The period character is followed by the name of the module header port; in brackets following the formal port, the name of the wire is entered.

Test Benches

Test benches help you to verify that a design is correct. How do you create a simple testbench in Verilog?

Let's take the exisiting MUX_2 example module and create a testbench for it. We can create a template for the testbench code simply by refering to the diagram above.

module MUX2TEST;  // No ports!
...
initial
// Stimulus
...

MUX2 M (SEL, A, B, F);

initial
// Analysis
...

endmodule

Initial Statement

In this code fragment, the stimulus and response capture are going to be coded using a pair of initial blocks. An initial block can contain sequential statements that can be used to describe the behaviour of signals in a test bench.

In the Stimulus initial block, we need to generate waveform on the A, B and SEL inputs. Thus:

initial  // Stimulus
begin
SEL = 0; A = 0; B = 0;
#10 A = 1;
#10 SEL = 1;
#10 B = 1;
end

Once again, let's look at each line in turn.

SEL = 0; A = 0; B = 0;

This line contains three sequential statements. First of all, SEL is set to 0, then A, then B. All three are set to 0 at simulation time 0.

#10 A = 1;

In terms of simulation, the simulator now advances by 10 time units and then assigns 1 to A. Note that we are at simulation time = 10 time units, not 10 ns or 10 ps! Unless we direct the Verilog simulator otherwise, a Verilog simulation works in dimensionless time units.

#10 SEL = 1;
#10 B = 1;

These two lines are similar to the one above. 10 time units after A is set to 1, SEL is set to 1. Another 10 time units later (so we are now at simulation time = 30 time units), B is set to 1. The diagram below shows how the initial block has created a waveform sequence for the three signals.

We shall look at the use of the initial block to capture the MUX_2's response in the next section of the tutorial.

Response Capture

In the previous section of the tutorial, we looked at describing stimuli in Verilog to test our 2-input multiplexer. So next, we’ll look at how to capture the response of our device under test.

Remember from the module template that we are using initial blocks to code up the Stimulus and Response blocks.

module MUX2TEST;  // No ports!
...
initial
// Stimulus
...

MUX2 M (SEL, A, B, F);

initial
// Analysis
...


endmodule

The Response initial block can be described very easily in Verilog as we can benefit from a built-in Verilog system task. Thus:

initial  // Response
$monitor($time, , SEL, A, B, F);

Once again, let's look at each item in turn.

$monitor();

$monitor is a system task that is part of the Verilog language. Its mission in life is to print values to the screen. The values it prints are those corresponding to the arguments that you pass to the task when it is executed. The $monitor task is executed whenever any one of its arguments changes, with one or two notable exceptions.

$time

$time is a system function (as opposed to a system task). It returns the current simulation time. In the above example, $time is an argument to $monitor. However, $time changing does not cause $monitor to execute - $monitor is clever enough to know that you wouldn't really want to print to the screen the values of all of the arguments every time the simulation time changed.

, , ,

The space at position 2 in the argument list ensures that a space is printed to the screen after the value of $time each time $monitor is executed. This is a simple method of formatting the screen output.

SEL, A, B, F

Finally we come to the signal arguments themselves. Each time one of these signals changes value, $monitor will execute. When $monitor executes it will print all of the argument values to the screen, including $time. This is the output created by $monitor in our MUX2 testbench:

 0 0000
10 0101
20 1100
30 1111

This is simply a tabular listing of the waveforms that would be generated during simulation (if we had a waveform viewer, that is!).

It's amazing what you can learn from two lines of code, isn't it? We'll look at more elaborate output formatting soon.

RTL Verilog

Remember this?

Now we are going to look at the principles of RTL coding for synthesis tools.

Most commercially available synthesis tools expect to be given a design description in RTL form. RTL is an acronym for register transfer level. This implies that your Verilog code describes how data is transformed as it is passed from register to register. The transforming of the data is performed by the combinational logic that exists between the registers. Don't worry! RTL code also applies to pure combinational logic - you don't have to use registers. To show you what we mean by RTL code, let's consider a simple example.

module AOI (input A, B, C, D, output F);
assign F = ~((A & B) | (C & D));
endmodule

Yes! The AOI gate that we have used as an example so far has actually been written in RTL form. This means that continuous assignments are a valid way of describing designs for input to RTL synthesis tools. What other code techniques can we use? How about:

module MUX2 (input SEL, A, B, output F);
input SEL, A, B;
output F;
INV G1 (SEL, SELB);
AOI G2 (SELB, A, SEL, B, FB);
INV G3 (.A(FB), .F(F));
endmodule

Module instances are also examples of synthesizable RTL statements. However, one of the reasons to use synthesis technology is to be able to describe the design at a higher level of abstraction than using a collection of module instances or low-level binary operators in a continuous assignment. We would like to be able to describe what the design does and leave the consideration of how the design is implemented up to the synthesis tool. This is a first step (and a pretty big conceptual one) on the road to high-level design. We are going to use a feature of the Verilog language that allows us to specify the functionality of a design (the ‘what') that can be interpreted by a synthesis tool.

Always blocks

Always blocks are akin to the initial blocks that you have met already in Test Benches. Initial blocks are procedural blocks that contain sequential statements. Initial blocks execute just once. Always blocks on the other hand are always available for execution. This means that the statements inside an always block are executed up until the closing end keyword:

always
begin
// statements
end

But then they can be executed again! This means that a way of controlling execution through an always block is required. In describing synthesizable designs, a sensitivity list is often used to control execution (we shall see other approaches later).

always @(sensitivity-list)
begin
// statements
end

The sensitivity list consists of one or more signals. When at least one of these signals changes, the always block executes through to the end keyword as before. Except that now, the sensitivity list prevents the always block from executing again until another change occurs on a signal in the sensitivity list.

The statements inside the always block describe the functionality of the design (or a part of it). Let's reconsider the AOI gate:

always @(sensitivity-list)
begin
F = ~((a & b) | (c & d));
end

Instead of a continuous assignment, we now have a procedural assignment to describe the functionality of the AOI gate. Notice that the sensitivity list isn't valid Verilog code. We need to create a meaningful sensitivity list. How do we decide when to execute the always block? Perhaps a better question is what do we need to do in order to have F change value. Answer: F can only change when at least one of a, b, c or d changes. After all, these are the four inputs to the AOI gate. That's our sensitivity list:

always @(a or b or c or d)
begin
F = ~((a & b) | (c & d));
end

Verilog-2001 introduced additional syntax for describing sensitivity lists.

always @(a, b, c, d)
always @(*)
always @*

In the first of these, we have simply replaced the word or with a comma. The other two are equivalent and create an implicit sensitivity list that contains all the signals whose values are read in the statements of the always block. In this example @* or @(*) are equivalent to @(a,b,c,d). When describing combinational logic, it is important to make sure that sensitivity lists are complete; this syntax helps to ensure that this is holds.

Now for the MUX_2 design. In the above code snippet, we simply replaced the continuous assignment with an equivalent always block. We can do the same with the module instances in the MUX_2 design - strip away each instance and replace it with the equivalent always block.

always @(sel)
begin
selb = ~sel;
end

always @(a or sel or b or selb)
begin
fb = ~((a & sel) | (b & selb));
end

always @(fb)
begin
f = ~fb;
end

But we can do better than this. Let's merge the three always blocks together remembering that in the process (a pun for the VHDL'ers amongst you!) the sensitivity list of the resulting one always block contains only those signals that cause F to change value.

always @(sel or a or b)
begin
selb = ~sel;
fb = ~((a & sel) | (b & selb));
f = ~fb;
end

When writing RTL code, “think functionality, think inputs” is a useful aide memoire in terms of bridging the gap between concept and code. Well, we have already taken care of the inputs as the sensitivity list now consists of only the MUX_2 input ports.

For the functionality, let’s get conceptual. If sel is a logic 1, a is routed through to the f output. On the other hand if sel is a logic 0, b is routed through to the f output. Rather than think about routing one of the inputs through to the output let's think about the output getting one of the inputs, and let's write the text on separate lines depending upon whether we are making a decision or performing an action (sometimes referred to as pseudo-code):

if sel is logic 1
f gets a
otherwise
f gets b

This can be translated into Verilog code:

if (sel == 1)
f = a;
else
f = b;

Now before we go any further, we'll just take this code snippet a line at a time.

if (sel == 1)

The Verilog language allows for many different kinds of sequential statement. The procedural assignment is one you have already come across not only on this page but also in test benches (assignments to SEL, A and B in the stimulus initial block, if you remember). Here's another: the if statement. Actually this line is part of the if-else statement that is the entire code snippet. if is a Verilog keyword. After the if keyword you have a conditional expression, in this case (sel == 1) - does sel have the value logic 1? If so...

f = a;

f gets the value on the a input. Or in Verilog jargon, a procedural assignment. But what if sel is not logic 1?

else

Otherwise (assume sel is logic 0 - more on this assumption later)...

f = b;

f gets the value on the b input.

So, as it turns out, we have described the funcionality of the MUX_2 design using a single procedural statement, the if-else statement. In each branch of this if-else statement, there is an additional procedural statement, either assigning a to f, or b to f, depending upon the value of sel. But we have to remember that this procedural statement lives inside an always block, so...

always @(sel or a or b)
begin
if (sel == 1)
f = a;
else
f = b;
end

This now enables us to describe a design using a list of continuous assignments, a hierarchy of designs or an always block. Compare the 3 approaches for yourself:

// continuous assignments
assign selb = ~sel;
assign fb = ~((a & sel) | (b & selb));
assign f = ~fb
// a hierarchy of designs
INV G1 (SEL, SELB);
AOI G2 (SELB, A, SEL, B, FB);
INV G3 (.A(FB), .F(F));
// always block
always @(sel or a or b)
begin
if (sel == 1)
f = a;
else
f = b;
end

And of course you can mix'n'match coding styles if you wish. On a simple design, such as a MUX_2 it is perhaps not apparent how succinct the use of always blocks is in general compared to module instances and continuous assignments. But you can readily appreciate that the use of just one always block in this design is enabling us to describe the design in terms of its functionality without regard to the implementation. You can describe what you want without having to worry about how you are going to implement the design (because you don't have to - that's the synthesis tool's job!).

Go on! Read the MUX_2 design into your synthesis tool and have a play.

If statement

In the last section, we looked at describing hardware conceptually using always blocks. What kind of hardware can we describe? What are the limitations? What kinds of Verilog statement can be used in always blocks to describe hardware? Well, we have already seen the use of an if statement to describe a multiplexer, so let's dwell on if statements in this section.

always @(sensitivity-list) // invalid Verilog code!
begin
// statements
end

The code snippet above outlines a way to describe combinational logic using always blocks. To model a multiplexer, an if statement was used to describe the functionality. In addition, all of the inputs to the multiplexer were specified in the sensitivity list.

reg f;
always @(sel or a or b)
begin
if (sel == 1)
f = a;
else
f = b;
end

Variable declaration

It is a fundamental rule of the Verilog HDL that any object that is assigned a value in an always statement must be declared as a variable. Hence,

reg f; // must be declared before it is used in a statement

The term variable was introduced in the verilog-2001 standard. Previously, the term used was register. This was confusing, because a Verilog variable (register) does not necessarily imply that a hardware register would be synthesised. hence the change of terminology.

Combinational logic

It transpires that in order to create Verilog code that can be input to a synthesis tool for the synthesis of combinational logic, the requirement for all inputs to the hardware to appear in the sensitivity list is a golden rule.

Golden Rule 1:

To synthesize combinational logic using an always block, all inputs to the design must appear in the sensitivity list.

Altogether there are 3 golden rules for synthesizing combinational logic, we will address each of these golden rules over the next couple of sections in this tutorial.

If statement

The if statement in Verilog is a sequential statement that conditionally executes other sequential statements, depending upon the value of some condition. An if statement may optionally contain an else part, executed if the condition is false. Although the else part is optional, for the time being, we will code up if statements with a corresponding else rather than simple if statements. In order to have more than one sequential statement executed in an if statement, multiple statements are bracketed together using the begin..end keywords,

reg f, g; // a new reg variable, g
always @(sel or a or b)
begin
if (sel == 1)
begin
f = a;
g = ~a;
end
else
begin
f = b;
g = a & b;
end
end

If statements can be nested if you have more complex behaviour to describe:

reg f, g;
always @(sel or sel_2 or a or b)
if (sel == 1)
begin
f = a;
if (sel_2 == 1)
g = ~a;
else
g = ~b;
end
else
begin
f = b;
if (sel_2 == 1)
g = a & b;
else
g = a | b;
end

Notice that the code is beginning to look a little bit confusing! In the code above, begin..end blocks have only been used where they must be used, that is, where we have multiple statements. It is probably a good idea to use begin..end blocks throughout your Verilog code - you end up typing in a bit more Verilog but it's easier to read. Also, if you have to add more functionality to an always block later on (more sequential statement), at least the begin..end block is already in place. So,

reg f, g, h; // yes, an extra reg variable, h
always @(sel or sel_2 or a or b)
begin
if (sel == 1)
begin
f = a;
if (sel_2 == 1)
begin
h = ~b;
g = ~a;
end
else
begin
g = a | b;
h = a & b;
end
end
else
begin
if (sel_2 == 1)
begin
g = a & b;
h = ~(a & b);
end
else
begin
h = ~(a | b);
g = a | b;
end
f = b; // here's f!
end
end

Note that the order of assignments to f, g and h has been played around with (just to keep you on your toes!).

Synthesis considerations

If statements are synthesized by generating a multiplexer for each variable assigned within the if statement. The select input on each mux is driven by logic determined by the if condition, and the data inputs are determined by the expressions on the right hand sides of the assignments. During subsequent optimization by a synthesis tool, the multiplexer architecture may be changed to a structure using and-or-invert gates as surrounding functionality such as the a & b and the ~a can be merged into complex and-or-invert gates to yield a more compact hardware implementation.

Synthesizing Latches

In the last section, if statements were used to describe simple combinational logic circuits. Synthesizing the Verilog code produced multiplexing circuits, although the exact implementation depends upon the synthesis tool used and the target architecture of the device.

As well as enabling the creation of multiplexers, if statements can also be used to implement tristate buffers and transparent latches. In this article we will look at how transparent latches are synthesized from if statements and how to avoid the inadvertent creation of latches when you meant to create combinational logic circuits from Verilog code containing if statements.

If Statements

In the processes that have been coded up so far, if-else statements rather than simple if statements have been used. Let's use a simple if statement rather than an if-else statement in an example you have already seen:

reg sel, a, b;
always @ (sel or a or b)
begin : if_else
if (sel == 1)
f = a;
else
f = b;
end

becomes...

reg sel, a, b;
always @ (sel or a or b)
begin : pure_if
f = b;
if (sel == 1)
f = a;
end

Note that the behaviour being described is the same. In the pure_if always block, f initially gets b. Only if sel is active HIGH does f get a. This is perhaps a slightly odd way to describe a multiplexing circuit but it is accepted by all synthesis tools. Synthesis tools expect to create circuits responding to binary values. As far as a synthesis tool is concerned if sel is 1 a is routed through to f. If sel is not 1 it must be 0 and thus sel being 0 leaves f being driven by the initial assignment from b.

Let's lose the b input to the always block so that we have:

reg sel, a;
always @ (sel, a)
begin : latching_if
if (sel == 1)
f = a;
end

Incomplete Assignment

Now analyze the behaviour of the code. If sel is 1, f gets a. But what happens when sel is 0? Well, very simply, nothing! f does not and can not change. When sel is fixed at 0, we can change a as much as we like, f will not be assigned the value of a. If we suppose that an if statement synthesises to a multiplexer, then we must be able to configure the multiplexer such that f only gets the value of a when sel is 1. This can be achieved by feeding back the multiplexer f output back to the 0 input; in hardware terms this is a transparent latch and this is exactly the hardware synthesized by a synthesis tool given this Verilog code.

If the target architecture does not contain transparent latches the synthesis tool will generate multiplexer circuits that employ combinational feedback in order to mimic the latching behaviour required.

Now, this is very well but what's really happening here? One minute if statements create multiplexers, the next they create latches. Well, it's not the if statements, but the process as a whole that counts. If it is possible to execute an always block without assigning a value to a signal in that always block, the reg variable will be implemented as a transparent latch. This is known as incomplete assignment.

Golden Rule 2:

To synthesize combinational logic using an always block, all variables must be assigned under all conditions.

Simplifying code analysis

Suppose you are creating an always block to describe combinational logic. This always block consists of nested if-else statements as follows:

reg f, g;
always @ (sel or sel_2 or sel_3 or a or b)
begin
if (sel == 1)
begin
f = a;
if (sel_2 == 1)
g = ~ a;
else
begin
g = ~ b;
if (sel_3 == 1)
g = a ^ b;
end
end
else
begin
if (sel_2 == 1)
g = a & b;
else
if (sel_3 == 1)
g = ~(a & b);
// oops! no else
// else
// g = ...
f = b;
end
end

Will you get transparent latches on the f and g outputs? Not easy is it? If you look carefully you will see that in fact, g is latched when sel is 0, sel_2 is 0 and sel_3 is 0. The ‘oops!' comment should help you to see where the complete assignment is NOT made.

Default Assignment

Fortunately, it is possible to save yourself the bother of scouring through the always block code to locate possible incomplete assignments by setting variables to default values at the start of the always block. Using this approach you may get undesired functionality if you have missed out an assignment (which should be easy to fix) as opposed to unwanted transparent latches. For our current example,

always @ (sel or sel_2 or sel_3 or a or b)
begin
// default values assigned to f, g
f = b;
g = a & b;
if (sel == 1)
begin
f = a;
if (sel_2 == 1)
g = ~ a;
else
begin
g = ~ b;
if (sel_3 == 1)
g = a ^ b;
end
end
else
if (sel_2 == 1)
g = a & b;
else
if (sel_3 == 1)
g = ~(a & b);
end

Useful Verilog Tips

Top-down Design and Synthesis Issues for Sequential Always Blocks

We are going to look at using structured design of synthesizable always blocks to implement sequential logic.

The circuit under consideration is an 8 bit synchronous counter, with an enable, a parallel load, and an asynchronous reset. The counter loads or counts only when the enable is active. When the load is active, Data is loaded into count.

The counter has two modes: binary and decade. In binary mode, it is an 8 bit binary counter. In decade mode, it counts in two 4 bit nibbles, each nibble counting from 0 to 9, and the bottom nibble carrying into the top nibble, such that it counts from 00 to 99 decimal.

The truth table of the counter is as follows (- means don't care):

Reset	Clock	Enable	Load	Mode	Count
0 - - - - 0
1 ^ 1 - - Count
1 ^ 0 0 - Data
1 ^ 0 1 0 Count + 1 (binary)
1 ^ 0 1 1 Count + 1 (decade)

and this is the Verilog module declaration:

module COUNTER (input Clock, Reset, Enable, Load, Mode,
input [7:0] Data,
output [7:0] Count);
// module functionality
endmodule

So, how do you apply top-down design principles, a knowledge of synthesizable Verilog constructs and good coding finesse to this problem? Let's have a look...

It is important to understand that conceptually, a counter is a register with the output fed back to the input via an incrementer. Hence the Verilog code will reflect this concept. For example,

// inside a procedure
Q <= Q + 1;

The design process introduces some key Verilog coding aspects that need to borne in mind for synthesis. The most fundamental is the use of the classic asynchronous-reset-plus-clock single-procedure style of Verilog code.

always @ (posedge Clock or negedge Reset)
begin
if (!Reset)
// reset register, Q <= 0
else
// increment register, Q <= Q + 1;
end

The essence of the code structure is that the clock and reset need to be in the sensitivity list; the appropriate event on either signal will cause one of the two ‘if' branches to execute. Note that the always statement must execute only on the rising edge of the clock and the falling edge of the reset, hence we must have posedge Clock and negedge Reset as the timing control of the always statement. Now that we have defined the basic structure of the procedure, we will go on to fill in the two ‘if' branches.

The reset branch is very simple:

// inside a procedure
if (!Reset)
Q <= 0;

The clock branch needs to contain the functionality of the other four truth table entries; the code reflects the priority of those inputs directly. The enable signal has the highest priority, with nothing happening when it is high; next in priority is the load signal. Hence inside the enable block we will have,

// inside a procedure
if (!Enable)
// enable counter functionality
if (!Load)
// load data
else
// implement counting based on mode signal

For the actual increment statement (remember, Q <= Q + 1;), it is desirable to combine this functionality for either mode to ensure that only one piece of incrementer hardware is synthesised. So far, the detailed structure of the process has been derived from the truth table in a top-down design manner. Now we need to code the Verilog with a view to its implementation.

// inside a procedure
if (!Load)
// load data
else
if (lower_nibble_count != max_in_either_mode)
// increment lower nibble
else
// wrap lower nibble back to zero
if (upper_nibble_count != max_in_either_mode)
// increment upper nibble
else
// wrap upper nibble back to zero

Although we are only providing a structure for the detail of the Verilog code in the above code snippet, it is notable that the word ‘increment' appears only twice and that it applies to nibbles - the code is structured to synthesise two 4-bit incrementers. This is a subtle point - you are relying on a synthesis tool's resource sharing capabilities to optimize the Verilog unless you write the code as presented above. If your synthesis tool doesn't support resource sharing, you have to write the code as shown above!

OK, let's fill out the detail of the code structure presented so far in order to generate a model solution. This is given at the end of the section.

So, filling out the detail,

// inside a clocked procedure
if (!mode) // 8-bit binary
Q <= Q + 1;
else // two decade counters
if (Q[3:0] == 9) // wrap lower decade
begin
Q[3:0] <= 0;
if (Q[7:4] != 9) // increment upper decade
Q[7:4] <= Q[7:4] + 1;
else // wrap upper decade
Q[7:4] <= 0;
end
else // increment lower decade
Q[3:0] <= Q[3:0] + 1;

In summary, we have applied top-down design principles to create a synthesizable Verilog architecture containing a single procedure. The detailed code implementation was produced with the pitfalls of synthesis clearly borne in mind.

The rest of this section gives a model solution:

// counter
// 8 bits
// synchronous, positive edge
// binary/decade
// asynchronous reset, active low
// synchronous parallel load, active low
// synchronous enable, active low
// binary counter mode 0 = 8 bits
// decade counter mode 1 = 2x4 bits
// reset has priority over enable over load
module COUNTER (input Clock, Reset, Enable, Load, Mode,
input [7:0] Data,
output [7:0] Count);

always @ (posedge Clock or negedge Reset)
begin
if (!Reset)
Q <= 0;
else
if (!Enable)
if (!Load)
Q <= Data;
else if (!Mode && (Q[3:0] != 15)) || (Mode && (Q[3:0] != 9))
Q[3:0] <= Q[3:0] + 1;
else
begin
Q[3:0] <= 0;
if ( !Mode && (Q[7:4] != 15)) || (Mode && (Q[7:4] != 9) )
Q[7:4] <= Q[7:4] + 1;
else
Q[7:4] <= 0;
end
end
endmodule

Think Before You Code

We are going to extend the sequential counter to implement a Gray code sequence rather than a simple linear increment.

The sequence we will implement is as follows:



Nth Step after Reset (in binary) Gray Code
000 000
001 001
010 011
011 010
100 110
101 111
110 101
111 100

which yields the following always block for a Gray code counter, derived directly from the above truth table:

always @(negedge reset or posedge clock)
if (~reset)
q <= 0;
else
case (q)
3'b000: q <= 3'b001;
3'b001: q <= 3'b011;
3'b011: q <= 3'b010;
3'b010: q <= 3'b110;
3'b110: q <= 3'b111;
3'b111: q <= 3'b101;
3'b101: q <= 3'b100;
3'b100: q <= 3'b000;
default: q <= 3'bx;
endcase

Obviously, the Verilog code doesn't look like a normal counter always block. Perhaps more worrying is that this approach to coding is going to take a long time for a counter > 3 or 4 bits. Yes, the code space increases as O(2N). In the same way that we try to avoid algorithms whose complexity creates execution times or whose output data sets increase at anything greater than O(N3/2), it's also wise to avoid such algorithms from a coding standpoint.

As an example of algorithm choice, consider the options for sorting a set of data. Various algorithms exist, ye olde BubbleSort, Williams' HeapSort and Hoare's QuickSort spring to mind. The HeapSort algorithm executes in time O(Nlog2N), whereas BubbleSort executes in time O(N2). QuickSort is generally faster than HeapSort by about 25% on typical (< 100 items) random data sets providing the data set is not already ordered. As an aside, if you don't know how to code the BubbleSort algorithm don't bother, go straight to the HeapSort algorithm (do not pass GO, but do collect 200 coding Brownie points!). I know, we'll do a HeapSort model as next month's Model of the Month...

So let's find a way to code the Gray code sequence in as succinct a way as possible. By examining the sequence in the table, we can see that as we increment through each step bit N+1 inverts the value of bit N when 1. This looks just like an XOR operation and so it proves. Note, that this inversion rule applies because the sequence is incrementing and thus an incrementer is required too.

Thus, the horrid case statement in our first code snippett becomes somewhat simpler:

q <= q + 1; -- the incrementer

and for the inversion,

q_gray = {q[3], q[3]^q[2], q[2]^q[1], q[1]^q[0]};

so the Verilog code now becomes:

always @(negedge reset or posedge clock)
if (~reset)
q <= 0;
else
q <= q + 1;

assign q_gray = {q[3], ^q[3:2], ^q[2:1], ^q[1:0]};

Yes, we took advantage of Verilog's unary XOR operator to make the code even shorter, but well, that's what I thought it was there for! So a summary of the key points,

  • avoid any kind of complexity greater than O(N3/2), whether it be code space, execution time or data set
  • always try to recognise patterns in your code (look at the Synchronizer Scaler model for yet another example of this cognitive approach to hardware design)
  • use neat language features where they fit, such as Verilog's unary ^ operator

One final point, if the code looks neat from a coding, performance (execution and real-time) and data set perspective, you're probably not going to do much better. Unless, of course, you use a better algorithm...

Other Verilog Resources

Verilog Example Models

Analog-to-Digital Converter

OK. We've had a few requests for a Verilog Model of the Month, so here it is. This month we'll present a model of an ADC. You might remember that we modelled an ADC in our April Model of the Month, so this allows us to contrast VHDL coding with Verilog coding.

The example we present is for a 16-bit ADC, but you can easily modify the digital output wordlength for any desired accuracy of ADC.

The 16-bit ADC model is built around a function, ADC_16b_10v_bipolar. Note there are no conversion operators in this code as there was with the VHDL code - Verilog simply doesn't need them due to its absence of data typing.

Note that we must specify a range for the returned value from the function in Verilog, in VHDL, a range constraint is not mandatory. Real-valued ports are not allowed in Verilog so we must use a 64-bit input port to maintain an accurate representation of the analogue signals.

All of the required Verilog code is here, there are no references to other external code modules (there are eight referenced packages in the VHDL code). In the VHDL model there are frequent references to conversion functions, in the Verilog code we can use the built-in $rtoi function.

The charge_ovr reg is intended to be accessed hierarchically from the testbench in order to determine whether reset_charge needs to be re-activated. A note for VHDL coders; hierarchical naming is allowed in Verilog - remember that in VHDL you always have to use ports and generics to allow hierarchy traversal.

You are welcome to use the source code we provide but you must keep the copyright notice with the code (see the Notices page for details).

// 16-bit Analogue-Digital Converter
//
// +-----------------------------+
// | Copyright 1996 DOULOS |
// | designer : Tim Pagden |
// | opened: 7 Jun 1996 |
// +-----------------------------+

`timescale 1 ns / 1 ps

module ADC_16bit (analog_in,digital_out);

parameter conversion_time = 25.0, // conversion_time in ns
// (see `timescale above)
charge_limit = 1000000; // = 1 million

input[63:0] analog_in;

// double-precision representation of a real-valued input port;
// a fix that enables
// analog wires between modules to be coped with in Verilog.
// Think of input[63:0] <variable>
// as the equivalent of MAST's electrical.

output[15:0] digital_out;

reg[15:0] delayed_digitized_signal;
reg[15:0] old_analog,current_analog;
reg[4:0] changed_bits;
reg[19:0] charge;
reg charge_ovr;
reg reset_charge;

/* SIGNALS:-
analog_in = 64-bit representation of a real-valued signal
analog_signal = real valued signal recovered from analog_in
analog_limited = analog_signal, limited to the
real-valued input range of the ADC
digital_out = digitized 16bit 2's complement quantization of analog_limited
*/

/* function to convert analog_in to digitized_2s_comp_signal.
Takes analog_in values from (+10.0 v - 1LSB) to -10.0 v and converts
them to values from +32767 to -32768 respectively */

function[15:0] ADC_16b_10v_bipolar;

parameter max_pos_digital_value = 32767,
max_in_signal = 10.0;

input[63:0] analog_in;

reg[15:0] digitized_2s_comp_signal;

real analog_signal,analog_abs,analog_limited;
integer digitized_signal;

begin
analog_signal = $bitstoreal (analog_in);
if (analog_signal < 0.0)
begin
analog_abs = -analog_signal;
if (analog_abs > max_in_signal)
analog_abs = max_in_signal;
analog_limited = -analog_abs;
end
else
begin
analog_abs = analog_signal;
if (analog_abs > max_in_signal)
analog_abs = max_in_signal;
analog_limited = analog_abs;
end
if (analog_limited == max_in_signal)
digitized_signal = max_pos_digital_value;
else
digitized_signal = $rtoi (analog_limited * 3276.8);
if (digitized_signal < 0)
digitized_2s_comp_signal = 65536 - digitized_signal;
else
digitized_2s_comp_signal = digitized_signal;
ADC_16b_10v_bipolar = digitized_2s_comp_signal;
end

endfunction

/* This function determines the number of digital bit changes from
sample to sample; can be used to determine power consumption if required.
Task power_determine not yet implemented */

function[4:0] bit_changes;

input[15:0] old_analog,current_analog;

reg[4:0] bits_different;
integer i;

begin
bits_different = 0;
for (i=0;i<=15;i=i+1)
if (current_analog[i] != old_analog[i])
bits_different = bits_different + 1;
bit_changes = bits_different;
end

endfunction

/* Block to allow power consumption to be measured (kind of). Reset_charge
is used to periodically reset the charge accumulated value (which can be
used to determine current consumption and thus power consumption) */

always @ (posedge reset_charge)
begin
charge = 0;
charge_ovr = 0;
end

/* This block only triggered when analog_in changes by an amount greater
than 1LSB, a crude sort of scheduler */

always @ (ADC_16b_10v_bipolar (analog_in))
begin
current_analog = ADC_16b_10v_bipolar (analog_in); // digitized_signal
changed_bits = bit_changes (old_analog,current_analog);
old_analog = current_analog;
charge = charge + (changed_bits * 3);
if (charge > charge_limit)
charge_ovr = 1;
end

/* Block to implement conversion_time tpd; always block use to show
difference between block and assign coding style */

always
# conversion_time delayed_digitized_signal = ADC_16b_10v_bipolar (analog_in);

assign digital_out = delayed_digitized_signal;

endmodule
To download the Verilog source code for this month's Model of the Month, click here.

Shift Register

Well, not just a shift register. This month's model is used to highlight the creation of parameterisable components and the modelling of bidirectional ports.

The bidirectional port is modelled as a separate block from the main shifter function as a conditional continuous assignment. Notice that the high impedance state is modelled as {Length{1'bz}} rather than the more obvious Length'bz. This is because a parameter cannot be used as the vector width in Verilog. Ho hum! However, 'bz is quite legal...

The shifter function is modelled as a clocked always block The >> operator is used to present more succinct code than using concatenation would allow.

The Length parameter is used to parameterise the Data bidirectional port and the Reg register. Note that Reg is legitimate as a reg object name as Verilog is case-sensitive. But Reg is not a good name for a reg object, a better name would be shift_reg, perhaps.

So, one shift register. Parameterisable. Bidirectional. And synthesisable. Your mission, should you choose to accept it, is to modify the Verilog code to create a completely parameterisable, bidirectional shift register. Yes, bidirectional shift (shift left, as well as shift right) in addition to bidirectional I/O. This Web page will NOT self-destruct in five seconds!

You are welcome to use the source code we provide but you must keep the copyright notice with the code (see the Notices page for details).

// Shifter
//
// +-----------------------------+
// | Copyright 1996 DOULOS |
// | Library: Sequential |
// | designer : John Aynsley |
// +-----------------------------+

module Shifter (Clk, EN, WR, RD, SI, SO, Data);
// synopsys template

parameter Length = 1;

input Clk, EN, WR, RD, SI;
output SO;
inout [Length-1:0] Data;

reg SO;
reg [Length-1:0] Reg;

assign Data = !RD ? Reg : {Length{1'bz}};

always @(posedge Clk)
if (!EN)
begin
SO <= Reg[0];
Reg = Reg >> 1;
Reg[Length-1] = SI;
end
else if (!WR)
Reg = Data;

always @(WR or EN)
if (!WR & !EN)
$display("Error, Wr and En both active");

endmodule

To download the Verilog source code for this month's Model of the Month, click here.

Simple RAM Model

This month, a simple RAM model, written in Verilog. Following on from last month's introduction to parameterisation, the RAM model presented here is parameterisable in terms of memory depth and wordlength. Memory depth is parameterised using the AddressSize parameter, whilst WordSize is used to parameterise the wordlength.

The declaration,

reg [WordSize-1:0] Mem [0:1<<AddressSize];

defines the size of the memory block. Note the use of the << operator to provide a convenient mechanism for implementing 2N; there is no exponentiation operator in Verilog as there is in VHDL.

Just like the shift register model from November, the parameterisable bidirectional port is modelled as a conditional assignment. This assignment effectively models the read process from the RAM.

The first always block models the write process. The second provides a simple simultaneous read-write check.

Note that it is not possible address individual bits in the memory block using two-dimensional addressing as in VHDL. In Verilog, you need to create a temporary reg object for the memory word and then access a bit or a bit-select from that temporary reg object.

Oh, yes. How about a timing diagram?

You are welcome to use the source code we provide but you must keep the copyright notice with the code (see the Notices page for details).

// RAM Model
//
// +-----------------------------+
// | Copyright 1996 DOULOS |
// | Library: Memory |
// | designer : John Aynsley |
// +-----------------------------+

module RamChip (Address, Data, CS, WE, OE);

parameter AddressSize = 1;
parameter WordSize = 1;

input [AddressSize-1:0] Address;
inout [WordSize-1:0] Data;
input CS, WE, OE;

reg [WordSize-1:0] Mem [0:1<<AddressSize];

assign Data = (!CS && !OE) ? Mem[Address] : {WordSize{1'bz}};

always @(CS or WE)
if (!CS && !WE)
Mem[Address] = Data;

always @(WE or OE)
if (!WE && !OE)
$display("Operational error in RamChip: OE and WE both active");

endmodule

To download the Verilog source code for this month's Model of the Month, click here.


Universal Asynchronous Receiver (UAR)

Many thanks to Gerard Blair at the University of Edinburgh for allowing us to use his UAR model for February's Model of the Month. Gerard's home page is http://www.ee.ed.ac.uk/~gerard/. If you have a model that you are willing to have advertised on these pages, please drop us a line by clicking here.

OK, on with this month's Model...

The above figure shows a simplified picture of an asynchronous serial interface of the type commonly used to transfer data in computer and communications systems. The data transfer is referred to as `asynchronous' because the spacing between the characters may be of any length. In contrast, the timing of the bits within the character is well defined (and is related to the baud rate of the interface). The Tx clock and Rx clock signals are nominally of the same frequency, but are generated locally at each end of the transmission link and therefore cannot be assumed to be ‘locked' together. The design of a suitable transmitter circuit is not difficult, but the receiver must be able to detect the start of an incoming character and then store the value of each data bit, despite the fact that the relative frequency and phase of the Tx and Rx clocks may vary.

As shown in the data diagram above, the beginning and end of each character is delimited by a start bit whose value is always 0, and a stop bit whose value is always 1. In between characters, the transmitter outputs a constant value of 1. In operation, the receiver continually samples the input data. Following a 1 -> 0 data input transition, the eight data bits must be stored, and this is where a problem may occur, since for maximum reliability we wish to sample the data bits in the centre of their bit times and not close to either edge, so that small differences between the Tx and Rx clocks can be accommodated. This may be accomplished by using an Rx clock frequency which is a multiple of the data bit rate. In this exercise we shall assume that the Rx clock signal is eight times the bit rate.

Following the detection of a start bit, the stop bit should be detected 76 clock cycles later. If so, the Data Available output is set high; if not, the Framing Error output is set. Both status outputs are reset low by the detection of the next start bit.

There is thought to be a danger of spikes on the communication channel falsely starting the receiver. This means that a momentary LOW on the input to the receiver would be seen as a one-to-zero transition where-as it is really just noise.

To counter this, the specification is changed as follows:

  • The start bit is a one-to zero transition where the input signal is still zero four (or three or five) samples later.

Thus you will have to update your design for detectng a valid start bit.

Note:- the flexibility of 3-4-5 clock periods is to allow you to implement whichever is simplest - BUT there is no definition of what value is on the input at the sample AFTER the one-to-zero transition: this is to avoid problems associated with signal bounce.

Well, that's the specification for a UAR model. Here is a design based on sound synchronous design principles.

The following Verilog code has five major sub-modules:

  • a start detector which deals with recognizing the beginning of a new data frame. This sends a signal to set the...
  • control module into run mode.
  • A counter generates a pulse every 8 bits to input the data (thus sampled in the middle of the valid period) serially into a...
  • serial-parallel shift register. On the last bit of the data frame, the shift register is not updated, but rather the..
  • flags are set according to the value of the stop bit.

As with all good synchronous modules, a global_reset signal is included so that the registers can be put into a known state at the start of testing.

You are welcome to use the source code we provide but you must keep the copyright notice with the code (see the Notices page for details).

// Universal Asynchronous Receiver
//
// +-----------------------------+
// | Library : Datacomms |
// | designer : Gerard Blair |
// | opened: 03 Feb 1997 |
// +-----------------------------+

// Architectures:
// 03.02.97 uar

// Start detection
module start_detect (valid, clk, reset, gl_reset, dIn);
output valid;
input clk, reset, gl_reset, dIn;
reg [3:0] shift_reg;

always @ (posedge clk) begin
if (reset | gl_reset)
shift_reg = 0;
else
shift_reg = { shift_reg[2:0], dIn };
end

assign valid = (shift_reg [0] == 0) &
(shift_reg [2] == 0) &
(shift_reg [3] == 1);

endmodule

// controller
module counter (count72, count8, clk, enable);
output count72, count8;
input clk, enable;
wire each8;
reg [8:0] count_reg;

always @ (posedge clk)
if (enable == 0)
count_reg <= 0;
else begin
count_reg <= count_reg + 1;
end

assign each8 = ((count_reg % 8) == 7);
assign count72 = (count_reg == 71);
assign count8 = each8 & ~count72;

endmodule

// serial parallel converter
module ser_par_conv (dOut, clk, enable, dIn);
output [7:0] dOut;
input clk, enable, dIn;
reg [7:0] dOut;

always @ (posedge clk)
if (enable == 1)
dOut = {dIn, dOut[7:1]};

endmodule

// flags for ready and data error
module flags (dReady, dError, clk, set, reset, dIn);
output dReady, dError;
input clk, set, reset, dIn;
reg dReady, dError;

always @(posedge clk)
if (reset == 1) begin
dReady = 0;
dError = 0;
end
else if (set == 1) begin
dReady <= dIn;
dError <= ~dIn;
end

initial $monitor("dReady %b %b", dReady, dError, $time);

endmodule

// generating the run signals
module control (running, clk, reset, gl_reset, set);
output running;
input clk, reset, gl_reset, set;
reg running;

always @ (posedge clk)
if ((reset == 1) | (gl_reset == 1))
running = 0;
else if (set == 1)
running = 1;
endmodule

// overall receiver definition
module uar (dOut, dReady, dError, clk, gl_reset, dIn);
output [7:0] dOut;
output dReady, dError;
input clk, gl_reset, dIn;

wire running, finish, count8, start;

start_detect s_d (start, clk, running, gl_reset, dIn);
counter cov (finish, count8, clk, running);
ser_par_conv s_p (dOut, clk, count8, dIn);
flags fla (dReady, dError, clk, finish, start, dIn);
control con (running, clk, finish, gl_reset, start);

endmodule

To download the Verilog source code for this month's Model of the Month, click here.


8-bit x 8-bit Pipelined Multiplier

Briefly interrupting the Built-in Self Test (BIST) theme, this month we present a synthesizable model of an 8-bit x 8-bit pipelined multiplier in Verilog.

Although the design is synthesizable as is, a synthesis tool with a re-timing capability is required in order to create a pipelined multiplier with the pipeline registers evenly distributed throughout the design. You can of course remove the pipeline_stages always block and use the un_pipelined_output assignment.

You are welcome to use the source code we provide but you must keep the copyright notice with the code (see the Notices page for details).

`timescale 1ns/1ps

module mult_piped_8x8_2sC (a, b, clk, reset, y);

input[7:0] a,b;
input clk,reset;
output[15:0] y;

//--------------------------------------
// Function: always @ posedge clk
// y(t=0) = a(t-8) * b(t-8)
//
// Simulation: PASSED
// Synthesis: Needs re-timing feature in
// synthesis tool
//
//--------------------------------------

reg[7:0] aR[8:0];
reg[7:0] bR[8:0];
reg[15:0] yR[8:0];

always @ (posedge clk) // pipeline_stages
begin
aR[7] = aR[6]; // pipeline statements
bR[7] = bR[6];
yR[7] = yR[6];
aR[6] = aR[5];
bR[6] = bR[5];
yR[6] = yR[5];
aR[5] = aR[4];
bR[5] = bR[4];
yR[5] = yR[4];
aR[4] = aR[3];
bR[4] = bR[3];
yR[4] = yR[3];
aR[3] = aR[2];
bR[3] = bR[2];
yR[3] = yR[2];
aR[2] = aR[1];
bR[2] = bR[1];
yR[2] = yR[1];
aR[1] = aR[0];
bR[1] = bR[0];
yR[1] = yR[0];
// multiply result (a*b) appears after +clk
aR[0] = a;
bR[0] = b;
yR[0] = multiply_8x8_2sC (aR[0],bR[0]);
end

function[15:0] multiply_8x8_2sC;
input[7:0] a,b;
reg[7:0] a_mag,b_mag;
reg[14:0] y_mag;
reg[14:0] y_neg;
begin
case (a[7])
0: a_mag = a[6:0];
1: a_mag = 128 - a[6:0]; // max(a_mag) = 128, thus 8 bits
endcase
case (b[7])
0: b_mag = b[6:0];
1: b_mag = 128 - b[6:0];
endcase
y_mag = a_mag * b_mag; // max(y_mag) = 16384, thus 15 bits
if ((a[7] ^ b[7]) & (y_mag != 0)) // if (a * b) is -ve AND non-zero
begin
// y_mag >=1, <= 16256, thus need only 14 bits
y_neg = 32768 - y_mag[13:0]; // max(y_neg) = 32767, thus need 15 bits
multiply_8x8_2sC = {1'b1,y_neg};
end
else
multiply_8x8_2sC = y_mag;
end
endfunction

//assign y = multiply_8x8_2sC (a,b); // un_pipelined_output
assign y = yR[7];

endmodule

Here is a skeleton testbench architecture that can be used for testing purposes.

`timescale 1ns/1ps

module mult_piped_8x8_2sC_tb;

reg clock, reset; // reset = active HIGH
reg [7:0] a_in, b_in;
wire [15:0] y_out;

// stimulus

// response checking

// DUT
mult_piped_8x8_2sC DUT (a_in, b_in, clock, reset, y_out);

endmodule
To download the Verilog source code for this month's Model of the Month, click here.

Verilog FAQ

What is the difference between Verilog and VHDL?

On the surface, not that much. Both are IEEE standards and are supported by all the major EDA vendors. Both can be used for designing ASICs and simulating systems. However, VHDL is altogether a grander language. Its support for system level modeling and simulation is far more comprehensive than Verilog, despite some Verilog built-in functions for stochastic modeling. However, VHDL requires longer to learn and is not so amenable to quick coding. As a final thought many engineers need to know both languages due to the use of IP (Intellectual Property) blocks written in "the other" language.

What versions of Verilog are there?

The original Verilog was standardised by the IEEE as Verilog 95; there was then an improved version released, Verilog 2001. This includes things like generate statements. Work is now ongoing on SystemVerilog. If you want to know more about SystemVerilog, have a look at our 1 day Introduction to SystemVerilog.

Can I use Verilog for the analog part of a design?

No. You can't design analog circuitry in Verilog, however you can model analogue circuitry in Verilog (said this digital designer!). In theory, Verilog can be used to model the behaviour of any system or component. However, Verilog does not offer the same level of modeling accuracy as say Spice, without an awful lot of work.

How must I write Verilog to make it synthesisable?

Writing Verilog for synthesis is not particularly difficult, but you need to be disciplined, not only in your use of Verilog syntax but also your approach to writing Verilog for synthesis. It is this latter aspect which many engineers overlook; thorough training is really the only way to avoid making poor strategy decisions in writing synthesisable Verilog.

A Verilog design can be moved to any tool or technology. Right?

On the face of it, this is true. Verilog was designed to be and is a technology independent design language. However, there is less of a compliance issue between different simulators than there is for synthesis tools. Generally speaking, moving Verilog code from one simulator to another involves one or two minor changes to the Verilog, assuming you don't use a lot of system tasks (which tend to be proprietary) in your code. Two different synthesis tools may support two quite different Verilog subsets. This is particularly an issue for us at Doulos in developing our training courses, because we like to present a reasonably generic approach to writing Verilog for synthesis. This means that the Verilog we teach you is guaranteed to be more transportable between synthesis tools than it otherwise would be. Our pain is your gain! In addition because we are so aware of the differences between synthesis tools, this means that we emphasise the best way of writing Verilog to get the best from your synthesis tool.

I can see how to write abstract behavioural descriptions in Verilog, but how do you describe and simulate the actual hardware?

This is probably the biggest hurdle that many hardware engineers face when moving to Verilog. After all, sometimes we need to be able to describe actual implementation as well as abstract functionality. The way to describe "physical" hardware in Verilog is to write Verilog models of those components. This is supported in Verilog through the use of instantiation. Verilog does not allow you to physically simulate your hardware. You can only simulate a model of that component in a Verilog simulation.

What is Synthesis?

Synthesis is the stage in the design flow which is concerned with translating your Verilog code into gates - and that's putting it very simply! First of all, the Verilog must be written in a particular way for the synthesis tool that you are using. Of course, a synthesis tool doesn't actually produce gates - it will output a netlist of the design that you have synthesised that represents the chip which can be fabricated through an ASIC or FPGA vendor.

How about on-line information resources?

You're already here! Try the Verilog section of our High Level Design Library for examples of Verilog models and assorted tips and tricks. On our “Where to go next...” page you'll find links to other EDA-related Web sites. In addition, check out the comp.lang.verilog newsgroup.

Copyright

Copyright 1995-2002 Doulos - www.doulos.com