Discussion 1 Verilog

Dr Ryan Robucci

Objective

  1. Introduction to Verilog
  2. Verilog basics
  3. Testbenches
  4. Coding Style
  5. Extended Introduction

Introduction to Verilog

What is Verilog

Verilog (IEEE 1364) is a hardware description language (HDL) that can be used to model electric systems for simulation and hardware synthesis and verification.

Verilog basics

Verilog coding styles

  • Structural models

    • comprises basic syntax supporting building a module from instatiations of Verilog primatives and other modules, along with the interconnections among the instatiations as well as the module inputs and outputs
    • conceptually like a textual version of a heirarcal schematic
  • Dataflow models

    • comprises C-like expressions to describe combinational logic with continous updates triggered automatically by input updates
  • Behavioral models :

    • functionality described using algorithms, most commonly with sequential code. The intent is for the code to describe the mapping of input values to output values, but not nessisarily in the same fashion as the hardware it represetents. Once the function is understood by a synthesizer too, it can decide how to implement the function in hardware.
  • Register-Transfer Level (RTL)

    • Describes identifiable registers and the movement of data amoung them through functions at specific specified timing events like clock edges logic. The term often refers to code that can be synthesized to hardware.

Types of primitives

Built-in primatives provide a starting set of building blocks for describing digital logic.

  • Gate-level primitive :
    • In-built primatives digital gate primatives NOT, AND, OR can be instatiated along with connections, using keywords like not and, or: e.g. and myand1 (out,a,b);
  • Switch-level primitive :
    • Switch Level modeling allows you to construct transistor-level schematic
      model of a design from transistor and supply primitives
    • nmos, pmos, supply1, supply0, etc...
  • User Defined Primatives : a tabular description of hardware (e.g. a truth table, state transistion table) but will not be used in this course

Structural Verilog with Module and Testbench

module mux_structural(d0,d1,sel,y);

  input d0,d1;
  input sel;
  output y; //defaults to type wire, which is suitable for 
           //  making connections between parts in netlists
  wire sel_n,w0,w1;
  not i0 (sel_n,sel);
  and i1 (w0,d0,sel_n);
  and i2 (w1,d1,sel);
  or  i3 (y,w0,w1);
endmodule

Line 1

  • keyword module begins a module followed by
  • an identifier to instatiate the module
  • port list a parentisis comma-separated list of port name
  • semicolon

Line 3

  • The ports are then declared to be input, output, or inout
  • endmodule keyword to conclude the module definition

Line 7

  • internal nets are declated using the keyword wire

Line 8

  • Instatiations of Verilog primatives and/or other modules

Understand the concurrent description: at the base Verilog constructs are concurrent by default. In the example, the order of the gates provided in lines 8-11 do not matter

A testbench:

module tb();              //a testbench is typically self-containtd and thus has no ports
                          //Internal Signals
  reg d0,d1,sel;          //reg:  signals that will be assigned within the same
                          //       hierarchy level using procedural code
  wire y;                 //wire: signals for connections
                          //In SystemVerilog, use: logic d0,d1,sel,y;
                          //Instantiation of module, referred to as Device Under Test
  mux_structural dut(.*); //SystemVerilog default connection syntax
                          // for standare Verilog use explicit port mapping
                          //   mux_structural dut(.d0(d0),.d1(d1),.sel(sel),.y(y));

  integer count;          //working integer variable
  
  initial begin           //produral code is typically used to generate input stimulous
    count = 0;            //   unless a test-jig module 
    forever #1 count++;   //   is instatiated to interact with the DUT
  end

  assign {d0,d1,sel}=count;

  initial #0 $display("d0 d1 sel  y"); // zero-delay #0 ensures that printing
                                       // after circuit initialization is completed 
  initial    $monitor("%2b ",d0,"%2b ",d1,"%2b ",sel," ","%2b ",y);

  initial #7 $finish;   //terminates simulation
  
endmodule

The highlighted code lines with the DUT instantiation, the assign statement, the run-once initial lines, and the block of code in lines 14-17 may be provded in any order since the base level of code is concurrent.

The lines within begin end statements, lines 15-16, are sequntial code and only there does order matter.

The $monitor is a printing task supporting multiple inputs, as well as formatting strings followed by the arguments satisfying inputs to the formatting string. The monitor task automatically retriggers and prints when any mutable input changes.

Results: (iverilog '-Wall' '-g2012' design.sv testbench.sv && unbuffer vvp a.out)

d0 d1 sel  y
 0  0  0   0 
 0  0  1   0 
 0  1  0   0 
 0  1  1   1 
 1  0  0   1 
 1  0  1   0 
 1  1  0   1 
 1  1  1   1 

Another Example Using Multi-Bit Vectors:

module mux_structural_2bit(d0,d1,sel,y);
  input [1:0] d0,d1; //multi-bit input
  input sel;
  output [1:0] y; //multi-bit output
                  //  making connections between parts in netlists
  wire sel_n;
  wire [1:0] w0,w1; //multi-bit internal buses
  not i0 (sel_n,sel);

  and i1 (w0[0],d0[0],sel_n);
  and i2 (w1[0],d1[0],sel);
  or  i3 ( y[0],w0[0],w1[0]);

  and i4 (w0[1],d0[1],sel_n);
  and i5 (w1[1],d1[1],sel);
  or  i6 ( y[1],w0[1],w1[1]);
endmodule

module tb();
  
  reg [1:0] d0,d1;
  reg sel;
  wire [1:0] y; 
  mux_structural_2bit dut(.*);

  integer count;
  
  initial begin
    count = 0;
    forever #1 count++;
  end
   
  initial $display("%3s ","sel","%3s ","d1","%3s ","d0"," ","%3s ","y");
  initial $monitor("%3b ", sel ,"%3b ", d1 ,"%3b ", d0 ," ","%3b ", y );

  assign {sel,d1,d0}=count;

  initial #31 $finish;
  
endmodule
sel  d1  d0    y 
  0  00  00   00 
  0  00  01   01 
  0  00  10   10 
  0  00  11   11 
  0  01  00   00 
  0  01  01   01 
  0  01  10   10 
  0  01  11   11 
  0  10  00   00 
  0  10  01   01 
  0  10  10   10 
  0  10  11   11 
  0  11  00   00 
  0  11  01   01 
  0  11  10   10 
  0  11  11   11 
  1  00  00   00 
  1  00  01   00 
  1  00  10   00 
  1  00  11   00 
  1  01  00   01 
  1  01  01   01 
  1  01  10   01 
  1  01  11   01 
  1  10  00   10 
  1  10  01   10 
  1  10  10   10 
  1  10  11   10 
  1  11  00   11 
  1  11  01   11 
  1  11  10   11 
  1  11  11   11 

Dataflow modelling

  • Dataflow models : assignment expressions

    • Assignment: = with either wire delcaration or later with assign
    • Arithmetic: +, -, * , /, %, **
    • Relational: <, >,<=, >=
    • Equality: ==, !=, also === and !== for metalogic support
      • Also Wildcard equality operators: ==? , !=?
    • Logical: &&, ||, !
      • Also logical implication ->, and logical equivalence <-> (see Verilog Manual)
    • Bit-wise binary: &, |, ^, ~^ (xnor), ^~ (xnor)
    • Bit-wise unary negation: ~
    • Reduction: &, ~&, |, ~|, ^, ~^
    • Logic Shift: >>, <<
    • Arithemtic Shift: >>, <<
    • Conditional Operator ? :
    • Concatenation: { comma-separated list }
    • Replication: {count{comma-separated list } }
  • One might consider Structural Verilog as inclusive of dataflow, since many of the dataflow operations that map directly to built-in logic primitives and specifications of net connections.

    • The following are the same in many contexts

      assign x = a & b & c;
      
      and n0(x,a,b,c);
      
  • The exact implementation and structure implied in the following is less certain unless we explicitly know the exact module that addition would map to with our synthesizer and library

    assign x = a+b;
    

Dataflow modeling example

module mux_dataflow_2bit_enables(d0,d1,sel,enCh0,enCh1,enGlobal,y);
  input [1:0] d0,d1; 
  input sel,enCh0,enCh1,enGlobal;
  output [1:0] y;
                  
  wire sel_n;
  wire w0,w1; 
  wire y0,y1;

  assign w0  = (d0[0] & ~sel) | (d1[0] & sel); //NOT, ANDs, OR
  assign w1  = sel ? d1[1] : d0[1];   // C-like conditional ternary operator
  assign y0 = w0 & enCh0 & enGlobal;  // string of binary (2-inputs) AND gates forms
                                      //    a three-input AND gate 
  assign y1 = & {w1,enCh1,enGlobal};  // reduction and compresses 3 bits to 
                                      //   one resulting bit result using the AND operator 
  assign y = {y1,y0};                 // concatentation operator
endmodule
module tb();
  
  reg [1:0] d0,d1;
  reg enCh0,enCh1,enGlobal;
  reg sel;
  wire [1:0] y; 
  mux_dataflow_2bit_enables dut(.*);

  integer count;
  
  initial begin
      count = 0;
      forever #1 count++;
  end
   
  initial $display("%8s ","enGlobal","%6s ","enCh1","%6s ","enCh0", "%3s " ,"sel","%3s ","d1","%3s ","d0"," ","| %3s","y");
  initial $monitor("%8b ", enGlobal ,"%6b ", enCh1 ,"%6b ", enCh0 , "%3b " , sel ,"%3b ", d1 ,"%3b ", d0 ," ","| %3b", y );

  assign {enGlobal,enCh1,enCh0,sel,d1,d0}=count;

  initial #255 $finish;
  
endmodule
enGlobal  enCh1  enCh0 sel  d1  d0  |   y
       0      0      0   0  00  00  |  00
       0      0      0   0  00  01  |  00
       0      0      0   0  00  10  |  00
       0      0      0   0  00  11  |  00
       0      0      0   0  01  00  |  00
...
       0      0      1   1  00  11  |  00
       0      0      1   1  01  00  |  00
...
       0      1      0   0  10  00  |  00
       0      1      0   0  10  01  |  00
       0      1      0   0  10  10  |  00
       0      1      0   0  10  11  |  00
       0      1      0   0  11  00  |  00
...
       1      0      1   1  01  11  |  01
       1      0      1   1  10  00  |  00
       1      0      1   1  10  01  |  00
...
       1      1      0   0  01  11  |  10
       1      1      0   0  10  00  |  00
       1      1      0   0  10  01  |  00
       1      1      0   0  10  10  |  10
       1      1      0   0  10  11  |  10
....
       1      1      1   1  10  01  |  10
       1      1      1   1  10  10  |  10
       1      1      1   1  10  11  |  10
       1      1      1   1  11  00  |  11
       1      1      1   1  11  01  |  11
       1      1      1   1  11  10  |  11
       1      1      1   1  11  11  |  11

Behavioral and RTL model

  • Behavioral code is implemented in procedural blocks that include one or several statements that describe an algorithm to define the behavior of a block of logic in a simulation or in hardware

  • A procedural block may include sequential statements from which the algorithm may be understood by beginning interpretation of statements one at a time (similar to traditional software coding languages) or
    parallel statements intended to be interpreted in parallel.

    • begin...end block of code with sequential statements
    • fork...join block of code with parallel statements
  • The creation of behavioral code is sometimes characterized by a lack of regard for hardware realization

  • Synthesizable Behavioral Code is code that a given synthesizer can map to a hardware implementation

    • The definition of synthesizable is synthesizer dependant- some simple prodecural code constructs are universally
      synthesizable by every synthesizer, while more complex code blocks and certain operators are not considered
      synthesizable by many

    • Example :

        x = myUINT8 >> 2; 
            // This is a shift by a constant implemented by a simple routing of bits.     
            // It is generally regarded as synthesizable
        x = myUINT8 >> varShift;
            // This is a variable shift with many possible implementations.      
            // It will simulate just fine, but at the synthesis step many      
            // synthesizers will throw an error saying that this is not synthesizable 
      
  • Behavioral code may indeed describe behavior in such a way that is not directly synthesizable by almost any synthesizer (such as reading waveforms from a .txt file) – though what is “synthesizable” is always defined by the synthesizer tool being used

    • Procedural code implemented with regard for hardware implementation, from which registers, the combinatorial logic between, and control signals like clocks may be inferred is called Register Transfer Level (RTL) code
    • Sometimes the terms “behavioral code” and “RTL code” are used to refer to synthesizable and nonsynthesizable code, though even this separation is dependent on the synthesizer tool being used

Behavioral and RTL model (Initial and Always Blocks)

  • Initial and Always blocks will be the first two types of blocks we will discuss (tasks, functions)

  • Initial blocks are triggered once at the start of a simulation or in the case of some synthesis tools may be used to describe the power-up state of registers or may be used to describe the initial default value of an intermediate variable

  • Always blocks continually repeats its execution during a simulation, but optionally can be gated (paused) so as to describe evaluation triggered with change in one or more signals as provided in a sensitivity list.

    • When describing combinatorial logic the sensitivity list should include every input to the logic. Changes to either high or low can invoke re-evaluation.
    • For coding sequential logic the sensitivity list should include only the control signals that trigger updates to sequential logic outputs
    • Example Control signals to include in a sensitivity list for seq. logic blocks:
      • enable for latches
      • clock for more traditional registers or flip-flops
      • any additional asynchronous controls like an asynchronous set and asynchronous reset
  • Assuming no delay statements are included in the procedural code: the keywords begin and end may be used to encapsulate a description of an algorithm using a block of sequential code.The code is just a description of a desired behavior and does not necessarily mimic the implementation itself – the entire description is evaluated in one instant in time (takes 0 time to complete)

    • syntax-wise, use begin and end like { and } in C

Edge-Sensitive vs Value-Sensitive Functions

  • When describing clocked, register hardware that should updates its observable outputs only when the clock and/or aync control signals changes, provide only the change-invoking control signals with the appropriate edge selection in the sensitivity list

    always @ (posedge clk, posedge reset)
    
  • When describing only combinational hardware, which should update its observable output when data inputs change regardless of the direction of change, list all of the function dependencies in the sensitivity list.

    always @ (a, b, c, d)
    
  • When providing a mix, the combinational results may not be used outside the block, and edge-selection must be used

RTL Verilog example:

module mux_rtl_2bit_enables(d0,d1,sel,enCh0,enCh1,enGlobal,y);
  input [1:0] d0,d1; 
  input sel,enCh0,enCh1,enGlobal;
  output reg [1:0] y; 
                      
  wire sel_n;
  wire w0, w1; 

  always @ (d0,d1,sel,enCh0,enCh1,enGlobal) begin: blk0
    reg y1,y0;
    y1 = 1'bx;
    y0 = 1'bx;
    if (enGlobal) begin 
      case (sel)
        1'b0 : begin 
          {y1,y0} = { d0[1]&enCh1 , d0[0]&enCh0 };
            y = {y1,y0};
          end
        1'b1 : y = d1 & {enCh1,enCh0}; //bit-wise AND, mult-bit assignment
      endcase
    end else begin
        y = 2'b0;
    end
  end
endmodule

Testbench:

module tb();
  
  reg [1:0] d0,d1;
  reg enCh0,enCh1,enGlobal;
  reg sel;
  wire [1:0] y_rtl; 
  wire [1:0] y_df;
  mux_dataflow_2bit_enables dut_df (.y(y_df) ,.*);
  mux_rtl_2bit_enables      dut_rtl(.y(y_rtl),.*);

  integer count;
  
  initial begin
    count = 0;
    forever #1 count++;
  end
   
  wire [1:0] match = y_df~^y_rtl;

  initial $display("%8s ","enGlobal","%6s ","enCh1","%6s ","enCh0", "%3s " ,"sel","%3s ","d1","%3s ","d0"," ","| %6s","y_df"," %6s","y_rtl", "%6s" ,"match");
  initial $monitor("%8b ", enGlobal ,"%6b ", enCh1 ,"%6b ", enCh0 , "%3b " , sel ,"%3b ", d1 ,"%3b ", d0 ," ","| %6b", y_df ," %6b", y_rtl , "%6b" , match );

  assign {enGlobal,enCh1,enCh0,sel,d1,d0}=count;

  initial #255 $finish;
  
endmodule

Output:

enGlobal  enCh1  enCh0 sel  d1  d0  |   y_df  y_rtl match
       0      0      0   0  00  00  |     00     00    11
       0      0      0   0  00  01  |     00     00    11
       0      0      0   0  00  10  |     00     00    11
...
       1      1      1   1  11  10  |     11     11    11
       1      1      1   1  11  11  |     11     11    11
...

Sensitivity List

Prev versions of Verilog used or keyword in sensitivity list, which referred to a sensitivity to a change in any of a or b or c.

always @ (a or b or c) 

This is not the same as being sensitive to a change in the result (a|b|c).

always @ (a|b|c); //not the same

With Verilog 2001:
Use a comma-separated sensitivity list

always @ (a, b, c, d)
always @ (posedge clk, posedge reset)

Shortcut for including all dependencies (inputs) in a combinatorial block:

always @ (*)

Behavioral Design with blocking and non-blocking assignment statements

There are 2 kinds of assignment statements:

  • blocking using the = operator, and
  • non-blocking using the <= operator.
  • Blocking assignments act like sequential code statements and make an assignment when they are encountered
  • Non-blocking schedule assignments to happen at some time in the future execution (not necessarily future time, but perhaps after subsequent code). They are called non-blocking because statements the follow can be evaluated before the actual assignment happens.

reg vs. Register

reg≢registerreg \not\equiv register

When discussing a variable y declared using reg y; refer to y as "reg" "y", not as "register" "y". You can also refer to it as "variable" "y".

  • reg keyword does not mean Register : Here is something to be cleared up right away when learning Verilog reg is just a variable. In fact the type of the signal that is declared by using ref is called a variable in later versions of Verilog because the the name is so confusing. So, don't let yourself be confused by the name, reg does not necessarily register manifest as a register in hardware.

Origin of the confusion: "Although the Verilog HDL is used for more than just simulation, the semantics of the language are defined for simulation, and everything else is abstracted from this base definition." -- page 64 of Verilog Standard

From a simulation perspective, a reg is assigned through procedural code and in the execution of that code the simulator might require a memory in order to compute the resulting value of a reg.

The code below describes a signal y that is updated with the value a|b|c any time a,b,or c changes. Clearly this is a 3-input OR gate, which is combinatorial. However, the algorithm/code used to describe the function requires a memory of y to compute the resulting value of y. To a simulator, a type reg is distinctly differnt then a wire.

reg y;
always @(a,b,c) begin
y = a;
y = y|b;
y = y|c;
end

This code computes the result of the expression assigning a|b|c and after computing the reult, independant of y, and then schedules an assignment of that result to y.

wire y;
assign y = a|b|c;

A reg is used in procedural-code as variables that may end up being implemented using with if sequential logic is generated and are just represent the output net of combinatorial logic otherwise. Wires on the other hand are for structural connections (nets/wires) between modules or outputs of combinatorial expressions. There is a simple rule to decide if you should use a reg or a wire: if the signal is assigned from a procedural block of code or not. It is so straight forward that in SystemVerilog you can just use the datatype logic and the type of reg or wire is effectively inferred from its use.

Metalogic Values

Verilog Supports more than the two logic values 0/Low and 1/High. The following Metalogic Values are worth being aware of an may be encountered unintentionally.

z: high-impedance (not driven), supports modeling of bus drivers where multiple potential drivers are connected to one output

x: undetermined, can represent the following

  • result when two drivers don’t agree
  • output that cannot be definitely determined to be 1 or 0
  • unintialized values in simulation

./images/metalogic_bus2.svg

Details for Reading

Structural Data Types: wire and reg and the others

  • Verilog data types called nets which model hardware connections between circuit components. The two most common structural data types are wire and reg.
  • A wire is like a real wire in a circuit . Its purpose is to make circuit network connections. Its value at every instant in time is decided by the driver connected to it. The driver may be assigned through a structural connection to a primitive or module or a continuous assignment statement.
  • Module ports of type input and inout are always of type wire. This type decision is ignorant of the external connection driving the signal.
  • Module ports of type output may be wire (network connection) or reg (a variable), depending on the coded driver. If driver is described using procedural code then use type reg.
  • In procedural code, the reg type hold their values until another value is put on them.
  • The declarations for wire and reg signals are inside a module but outside any initial or always procedural block. Verilog also supports local reg if a block is named.
    always @ (posedge clk) begin: BLOCKNAME … … … end
  • The default state of a reg is 'x' (unknown), and the for a wire is 'z'.
  • If you need a special strength type operation use special net keyword wand, wor, tri, triand, trior, trireg.

Verilog 2000: signed reg type, reg init., new operators

Register data type is now called a variable, as the previous name of register created a lot of confusion for beginners. Also it is possible to specify an initial value for the register/variable data type.

reg a = 0; // v2k allows to init variables
reg b, c, d = 0; //just init d

New signed reg:

// reg data type can be signed in v2k
// We can assign with signed constants
reg signed [7:0] data = 8'shF0;

Undeclared Nets

Setting Default Net Type

using `default net_type none at the top of your Verlog file is a recommended beginning practice, otherwise typing errors can generate additional nets rather than make connections. These errors are difficult to add to the list of debugging challenges for a beginner

In Verilog 1995, default data type is net and its width is always 1 bit.
This can be dangerous for two
reasons…
a simple typing mistake can declare
a new variable instead of an intended connection to an existing net causing a confusing error message or lead to a coding mistake
forgetting a declaration can lead to 1-bit wires which loose information

     wire [7:0] a; wire [7:0] b; wire [7:0] d;
     wire [7:0] e;
     c=a+b; //one bit!!!!
     e=c+d;

In Verilog 2001 the width is adjusted automatically
In Verilog 2001, we can disable default data type by using a
special directive at the top of the code:
`default net_type none

wire a,b,c,d,y;
mylib_and2(w1,a,b);
mylib_and2(w2,c,d);
mylib_and2(y,w1,w2);

Rules of Thumb for Beginners

  • Take care in specifying the sensitivity list : the contents of sensitivity list needs careful attention. A mistake here is a common cause for diferences between Verilog simulation and synthesized hardware.

    • Some common mistakes : Find out the bug in these code snippets.
        always @ (a,b,c) begin
            x = (c & a) | (~c & b);
        end
      
        always @ (a,c) begin
            x = (c & a) | (~c & x);
        end
      
        always @ (a,b,c) begin
            if (c) x = a;
            else x = b;
        end
      
        always @ (c) begin
            if (c) x = a;
            else x = b;
        end
      
      always @ (a,b,c) begin	     
       if c x = a;
      end
      
  • Code interpretation for combinational logic

    • Can you ignore the sensitivity list and reevaluate the procedural block at any and every instant of time or could that change the functional behavior?
    • If it is former then the interpretation can be mapped to a set of output input relationships described by a combinatorial truth table and no memory of the past is required
    • If reevaluation could change the resulting functionality, then you may have described what can only be implemented with some sequential hardware
  • If results from any execution of the block directly rely on signals/results generated from a previous execution of the block then sequential logic is describe. This does not include the case when results are saved using external sequential logic.

  • Draw the truth table for each block with and without considering the sensitivity list, it should include a row for every possible input combination and the output variables should should never occur in the output columns

Beginner Tips for Procedural Code for Hardware Synthesis

  1. For the purpose of coding, a reg, wire, or logic should be thought of as label for the the physical wire that is the output of a logic block, not the gate or register itself.
  2. Don't try to describe hardware that you can't first draw a representative circuit for...better yet, draw the circuit before coding.
  3. When modeling sequential logic, use non-blocking assignments q <= a+b; to assign the output of a register (here the input to the register is a+b)
  4. When modeling latches, use non-blocking assignments. (actually don't code any latches for now. If you see any synthesis message for latches, eliminate them.)
  5. When modeling combinatorial logic with procedural code always block, use blocking assignments to assign the output. y=a+b;
  6. Separate combinatorial and sequential logic into separate always blocks (as much as reasonably possible) to avoid accidental registers and latches. Masters of the art do not need follow this rule
  7. When modeling both sequential and combinatorial logic within the same always block, use non-blocking assignments for registers and minimally use blocking statements for intermediate combinatorial logic. y=a+b;q<=y;q_prev<q;
  8. Do not mix blocking and non-blocking assignments to the same variable.
  9. Do not make assignments to the same variable from more than one always block.

Realizing Equivalent Functional Behavior in Pre-Synthesis Simulation, Pre-Synthesis Simulation, and Synthesized Hardware

  • In some ways VHDL was designed for specification (description of hardware) and Verilog was designed for efficient simulation of hardware.
    VHDL has a more explicit handling and update model of signals that are internal to a module versus signals passed between models.
  • With Verilog, synthesizers and simulators don’t always agree on interpretation of code and this can lead to problems for students that think like the simulator only. We have to learn to understand how a simulator “interacts” with the code versus how a synthesizer approaches interpreting the code and mapping it to hardware. Keep both in mind as you learn Verilog.
  • Veilog has many potential use models and flexibility in style, so the community of Verilog RTL coders has learned to adopt, by convention only, safe practices like “Suggested Procedural RTL Coding Practices” to achieve deterministic matching between pre-synthesis functional simulation and sythesized hardware behavior.

Suggested Procedural RTL Coding Practices

Coding Requirements

A,B,C,D, and F are also enforced requirements for this course

A) Use non-blocking statements to assign outputs of sequential gates

B) Use blocking statements to assign outputs of combinatorial gates

C) Avoid unnecessary latches when coding for FPGAs, but when necessary use non-blocking statements

  • Latches fall outside the scope of basic synchronous timing analysis assumptions and complicate timing and functional testing.

D) Do not make assignments to the same variable from more than one always block

  • it confuses synthesizers and can lead to non-deterministic simulation behavior if multiple assignments are made at the same point in time

E) Consider what each output bit is assigned for any possible evaluation under any input and sequential state condition

  • When mixing sequential and combinatorial code, it is easy to get into the mindset of coding for sequential logic outputs (which commonly defaults to retaining previous values in many cases) and overlook what happens with the intermediate combinatorial logic in all cases
  • When coding sequential code missed cases can lead to unintended enables, storing and use of old data

F) Do not attempt to use any delay operator for the purpose of affecting hardware synthesis

Related guidelines:
http://www.sunburst-design.com/papers/CummingsSNUG2000SJ_NBA.pdf page 22

Course Guideline/Requirement: Use non-blocking assignment for EVERY output of a register

Poor, uses blocking Assignment

module dffb (q, d, clk, rst);
  output q;
  input d, clk, rst;
  reg q;
  always @(posedge clk)
    if (rst) q = 1'b0;
    else q = d;
endmodule

Good, uses Non-blocking Assignment

module dffx (q, d, clk, rst);
  output q;
  input d, clk, rst;
  reg q;
  always @(posedge clk)
    if (rst) q <= 1'b0; //coding all sequential always blocks, even simple single-block modules, using nonblocking assignments.
    else q <= d;
endmodule

Combinatorial and Registered-Output Logic

Course Guideline/Requirement: Use blocking assignment for any output of combinational logic, regardless of the trigger type

Combinatorial:

reg y;
always @(a,b)
   y = a & b;

In this isolated block you might have have alternatively tried and used
y <= a & b;
but we will follow a convention explained later whereby we use blocking for all combinatorial logic

Sequential (registered-output combinatorial logic):

reg q;
always @(posedge clk)
   q <= a & b;

Examples

// Combinational Logic
reg y;
always @(a,b)
    y = a & b;

// Sequential Logic mixed with Implied Combinational Logic
reg q;
always @(posedge clk)
    q <= a & b;

// Sequential mixed with Explicit Combinational Logic 
reg q;
always @(posedge clk): someBlockName
    reg _q;
    _q = a & b;
    q <= _q;

// Poor, uses blocking Assignment
module dffb (q, d, clk, rst);
    output q;
    input d, clk, rst;
    reg q;
    always @(posedge clk)
      if (rst) q = 1'b0;
      else q = d;
endmodule
// Coding all sequential always blocks,even simple single-block modules,using non-blocking assignments.
// Good, uses Non-blocking Assignment
module dffx (q, d, clk, rst);
  output q;
  input d, clk, rst;
  reg q;
  always @(posedge clk) begin
    if (rst) q <= 1'b0;
    else q <= d;
  end
endmodule

Cycle-Accurate Descriptions

Determinitisic, Cycle-Accurate Descriptions are used obtain matching of synthesized hardware function to pre-synthesis simulated function

In VHDL, Variables update immediately upon assignment and do not facilitate communication between processes. Signals are used to communicate between processes and are updated at the end of a simulation cycle.

In Verilog, we can adopt a coding conventions that mimics this by using named blocks

Sequential Circuit with Embedded Combinatorial:

always @(posedge clk) begin
   {a,b,c} <= {l|m,m|n,(n|l)&e};
end

Separated Combinatorial and Sequential with explicit naming and modeling of combinatorial outputs:

reg a_prereg,b_prereg,c_prereg,c_or;
always @(l,m,n) begin   
   c_or = n|l;
   {a_prereg,b_prereg,c_prereg} = {(l|m),(m|n),c_or&e};
end
always @(posedge clk) begin
   {a,b,c} <= {a_prereg,b_prereg,c_prereg};
end

Merged Style with local internal variables supporting "cycle-acurate" hardware reprentation and simuation
local variables require the use of a named block, but the advantage is that this prevents accidential use elsewhere

always @(posedge clk) begin: someBlockName
   reg _a,_b,_c,_c_or; //variables indended for use in this block only
   					   // though this is not enforced
   _c_or = n|l;
   {_a,_b,_c} = {l|m,m|n,_c_or&e};
   {a,b,c} <= {a_prereg,b_prereg,c_prereg};
end

What is the value of a signal when nothing is observing it?

  • A cycle-accurate simulation only needs to have the correct result at the end of every cycle.
  • A cycle-accuracte model for simulation or synthesis only needs to produce the correct output result.
  • If "peaking" at _a,_b,_c in simulation, they will only change on the positive clock edge, though in sythesized hardware they would update with changes of l,m,n normally as combinatorial circuits do.

Xilinx tools, at the time of this writing, will generate extra edge-sensitive hardware for _a,_b,_c,_c_or, and then since they are not used elsewhere Xilinx will report that hardware is trimmed leaving only our inteded output. This makes many distracting warning that I hope are removed in newer software adopting the newer SystemVerilog constructs like always_ff.

Guideline: Avoid declaring block-local variables needlessly outside the block

Enforced Guileline on Using Block Scope

This guideline is enforced in this Course: Avoid declaring block-local variables needlessly outside the block. Name blocks as needed to allow new scope for variables.

  • The following is poor style. It creates potential confusion and potential mistakes of use elsewhere in the circuit by delaring _c_or,_a,_b,_c and making them available outside the block. Using _c_or outside the block would actually genrate a new register outputing a delayed version of _c_or. Using _a,_b,_c would result in a new register that would be recognized to be the same as a,b,c respectively

      reg _a,_b,_c;
      always @(posedge clk) begin
        {_a,_b,_c} = {l|m,m|n,n|l};
        {a,b,c} <= {a_prereg,b_prereg,c_prereg};
      end
    
  • Good: uses named block and local scope

     always @(posedge clk) begin: blockC
       reg _a,_b,_c;
       {_a,_b,_c} = {l|m,m|n,n|l};
       {a,b,c} <= {_a,_b,_c};
     end
    

Trimmable-Logic Style

  • use for mixed sequntial and combinational blocks
reg y;
always @(posedge clk, negedge clr_n)
begin: blockY                 
 /************************************************//**/  
 /* Depends on a,b, current value of y, clr_n     //**/
 /************************************************//**/  
 /**/ reg y_prereg,partial;                        /**/
 /**/  y_prereg=1'bx;partial=1'bx;                 /**/
 /**/  if (!clr_n) y <= 1'b0;                      /**/
 /**/  else begin                                  /**/
 /**/    partial = a & b;                          /**/
 /**/    y_prereg = ~a | partial;                  /**/
 /**/    y <= y_prereg;                            /**/
 /**/  end                                         /**/
 /****************************************************/
 /* Output: block updates value of y               /**/
 /* Side effect: updates to y_prereg and partial   /**/
 /************************************************//**/  
end

Danger

Do not attempt to use intermediate combinational logic generated within an edge-triggered block outside the block.
It will generate unintential logic, or reduce your grade.
If you need the combinational version of the signal elsewhere, code the generating logic in a separate combinational block

Extended Verilog Introduction

Basic Unsigned Literals

Some examples quoted from https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=8299595 IEEE Std 1800-2017

  • Unsized, unsigned homogenious literals. Assignment using any of the following sets all bits to specified value:
    '0, '1, 'X, 'x, 'Z, 'z

  • Plain unsized, unsided decimal is provided without any extra decaration:

    456
    
  • In general

    • Start with optional bit size specifier

    • Provide tick '

    • specify literal reprensentation type with b,h,d or alternatives B,H,D

    • provide the value using the syntax of the literal

      • underscores _ for reablity may be freed used except for the first character
    • left padding rules are non-trivial and beginners are recommended to avoid, but simple use of unsized literals is reasonable '0,'1,'z,'x

    • Examples:

      4'b1001      // 4-bit binary number
      8'b1001_1011 // 8-bit binary number
       'h2AF       // 12-bit hex
      5 'D 3       //  5-bit decimal number
      12'hx        // 12-bit unknown number
        'x         // all x
      
  • ? is an alternative for z in literals. ? and z have a special meaning in certain contexts, which will be explained later.

  • Signed Literals will be covered later.

Logic Primatives

  • in general, the output connection is provided as the first argument, with a variable number of arguments for port connections
  • an intance name is provided after the primative type and before the connections
    The outputs must be type wire (or alernatively logic in the case of SystemVerilog)
  • Ex Not:
    not inv1 (out,in)
    
  • The other key primatives avaible are nand,or,and,or,xor,xnor, though other primatives exist
    ...
    wire a,b,c,d,e,x,y,z;
    ...
    or orX (x,a,b);
    or orY (y,c,d);
    and i0 (z,x,y,e);
    

Continuous assignment and Implicit Assignment

Continous assignment may be decribed implicity with the wire declartion or provided explicilty later along with an assign keyword

...
wire a,b,c,d,e;
...
wire x = a | b; //implicit continous assignment
wire z;
...
assign z = x & (c&d) & e ; 

Keyword: Implicit Continous Assignment

Simulating

The stimulus (input)

  • Designs can be instantiated and driven by other HDL code, typically called a testbench, that drives test signals
  • Alternatively, some simulators support a scripting language to drive input signals
    The output
  • Use $display $monitor or $strobe statements to print result to screen or file
  • Create a value change dump file (VCD)
    • Can be read and displayed by many tools
  • May Directly use a GUI to select and display signals

Design Strategies

  • For a beginner, treat Verilog as Hardware Description Language, not a software coding language. Start off learning Verilog by describing hardware for which you can design and draw a schematic; then translate this to HDL.
  • Plan by partitioning the design into sections and modules and coding styles that should be used.
  • Identify existing modules, memory components needed, and data-path logic, as well as the control signals required to make those operate as desired.
  • Simulate each part with a testbench before putting system together. Update testbenches and resimulate them as your design evolves.
  • Large memory blocks are often provided by the manufacturer to be instantiated. Smaller memory elements may be coded or embedded into other descriptions of the design
  • Data-path logic can be embedded coded with data-flow, structural elements, or complex synthesizable behavioral descriptions.
  • Some styles explicitly separate Comb. Logic and Seq Logic, but this is up to you.
  • Best practice is to develop a consistent approach to design, as well as a consistent coding style. It makes designing, coding, and debugging easier for you with time. An inconsistent hack-it-together and hack-until-it-works approach is not conducive to becoming more efficient.
  • Typically, complex control is implemented by a synthesizable behavioral case-statement-based state-machine, while simpler control could be implemented with any combinatorial description style. Data-path logic (comb. and sequential) can be integrated into the overall state machine or separated out (better for incremental simulation).

Possible Blueprint:

Components of a modeling/description language

Wires and registers with specified precision and sign
Arithmetic and bitwise operations
Comparison Operations
Bitvector Operations (selecting multiple bits from a vector, concatenation)
Logical Operators
Selection (muxes)
Indexed Storage (arrays)
Organizational syntax elements and Precedence
Modules (Hardware) Definition and Instantiation

Details on Port Declaration Styles

Verilog 2000 has New Port Declaration Options

Verilog 95 code:

module memory( read, write, data_in, addr, data_out);
input  read;
input  write;
input  [7:0] data_in;
input  [3:0] addr;
output  [7:0] data_out;

reg [7:0] data_out;

After the port list, port direction must be declared to be input, output, or inout as well as the width if more than one bit
Type declaration: type is by default a wire unless another type is provided

Verilog 2k with direction and data type listed:

module memory(
  input wire read,
  input wire write,
  input wire [7:0] data_in,
  input wire [3:0] addr,
  output reg [7:0] data_out
);

Verilog 2k with no type in port list

module memory(
  input  read,
  input  write,
  input  [7:0] data_in,
  input  [3:0] addr,
  output reg [7:0] data_out
);

ports are declared as wire by default, override with keyword reg keyword

However, there is a stylistic disadvantage to exposing type in port declaration. In the following two examples, the function is the same but the type for y must change.

Verilog 2000 – port y as reg

module dff2y(output reg qA,
             output reg qB,
             output reg y,
             input dA,
             input dB,
             input en_n, 
             input clk)

  always@(posedge clk)
    if (~en_n) begin
      qA <= dA;
      qB <= dB;
    end

  always @(qA,aB) begin
    y = 1’b0;
    if (a&b) begin
      y = 1;
    end
  end
end

Verilog 2000 – port y as wire

module dff2y(output reg qA,
             output reg qB,
             output wire y,
             input dA,
             input dB,
             input en_n, 
             input clk)

  always@(posedge clk)
    if (~en_n) begin
      qA <= dA;
      qB <= dB;
    end

    assign y = qA&qB;
end

y is not the output of a register though declared as reg along with qA and qB in the left module declaration It is arguable that such an internal coding implementation detail does not belong in the presentation of an “external” interface

Verilog 2000 -- hiding reg while exposing port direction using an intermediate signal

module dff2y(  output qA,
               output qB,
               output y,
               input dA,
               input dB,
               input en_n, 
               input clk)

  reg qA_int,qB_int,y_int;
  
  assign qA = qB_int;
  assign qB= qA_int;
  assign y= y_int;

always@(posedge clk)
    if (~en_n) begin
      qA_int <= dA;
      qB_int <= dB;
    end

  always @(qA,aB) begin
    y_int = 1’b0;
    if (a&b) begin
      y_int = a&b;
    end
  end
end

Hierarchy and Instantiation

Implicit port mapping uses the order of the ports in the definition to imply connections to local nets

Instantiation with Implicit Port Mapping

module dff2y_en( output qA, qB,
                 output y, // at the instiation, y is a wire regardless of the internal code implementation 
                 input dA, dB,
                 input en, 
                 input clk)

  wire en_n = ~en;   // wire en_n;
                     // assign en_n = ~en;

  dff2y dff2yInstance(
           qA,qB,  //dff out A B 
               y,  //and of qA&qB
           dA,dB,  //dff inputs
            en_n,  //input clk en
             clk); //clk
endmodule

Explicit port mapping uses the port names prefixed with . and allows reordering, no-connect, and omission of ports

Instantiation with Explicit Port Mapping

module dff2y_en(  output y,
                  input dA, dB,
                  input en, 
                  input clk)

  dff2y dff2yInstance(
           .dA(dA),
           .dB(dB), 
           .qA(), //qA not used (no-connect)
                  //qB omitted
           .y(y), 
           .en_n(~en), //**
           .clk(clk)); //clk
endmodule

Implicit net declaration of a net holding the result ~en. This is NOT allowed if default net declaration is disabled. ie using `default net_type none

Combinatorial and Registered-Output Logic

Combinatorial

reg y;
always @(a,b)
   y = a & b;

In this isolated block you might have have also used y<= a & b; but we will follow a convention explained later whereby we use blocking for all combinatorial logic

Sequential (registered-output combinatorial logic):

reg q;
always @(posedge clk)
   q <= a & b;

Three behavioral code organizations for sequential and combinatorial logic

  1. Separate always blocks for combinatorial and sequential logic
  • Comb. assignments use blocking statements
  • Seq. assignments use non-blocking statements
  • Encourages conscious distinct treatment and recognition of combinatorial and sequential logic. Often this style is recommended for beginners.
  1. Sequential and combinatorial logic in same block with combinatorial logic embedded in sequential assignments
  • Seq. assignments use non-blocking statements
  1. Sequential and combinatorial logic in same block with both combinatorial and sequential assignments
  • Comb. assignments use blocking statements
  • Seq. assignments use non-blocking statements

Learning the Verilog Styles

  • An outcome of this course is that you understand each of the three styles and that you can convert code from one style to the other “at will” when coding both combinartorial and sequential logic.
  • It is not sufficient to know only one style, but at first focus heavily first on mastering style 1 (separate comb. And seq.) and fall back to it when uncertain. It requires a more explicit identification of sequential and combinatorial elements in a circuit.
  • The other styles can be less cumbersome to code, but do not force students to explicitly identify the partition of conspiratorial and sequential hardware and sometimes starting with those styles can lead to misunderstanding the mapping of RTL to hardware.

AND->OR->Register Examples

Separated Combinatorial and Sequential Logic:

reg y,y_prereg,partial;
always @(a,b,c) begin
   partial = a & b;
   y_prereg = c | partial;
end

always @(posedge clk) begin
   y <= y_prereg;
end

Mentally, associate the variable name (e.g. y) with the output of the register, not the register itself.

Implicit Mix of Combinatorial and Sequential Logic:

reg y,partial;
always @(posedge clk) begin
   partial = a & b;
   y <= c | partial;
end

Explicit Mix of Combinatorial and Sequential Logic:

reg y,y_prereg,partial;
always @(posedge clk) begin
   partial = a & b;
   y_prereg = a | partial;
   y =<= y_prereg;
end

Separated Combinatorial and Sequential Logic:

reg y,y_prereg,partial;
always @(a,b,c) begin
   partial = a & b;
   y_prereg = a | partial;
end

always @(posedge clk, negedge clr_n) begin // Asynchronous control signals must appear in the sensitivity list
  if (!clr_n) y <= 1'b0;
  else y <= y_prereg;

end

Tip

Mentally learn to associate the variable name to the output of the register, not the register itself. This rule later helps distinguish the input to the register from the output. Unfortunally the generated diagram here DOES NOT follow this convention.

Language Templates

Typically Templates can be found in a synthesizer manual or through the
development GUI

You need to follow template statles that the synthesizer recognizes. Check the documentation of a synthesizer for additional coding style requirements.

Both of the following examples generate an error in Xilinx ISE:

ERROR:Xst:899 - "top.v" line 28: The logic for <partial> does not match 
a known FF or Latch template. The description style you are using to describe 
a register or latch is not supported in the current software release.

Problem Code 1:

reg y,partial;
always @(posedge clk, negedge clr_n) begin
  partial = a & b;
  if (!clr_n) y <= 1'b0;
  else y <= ~a | partial;
end

Problem Code 2:

reg partial,y_prereg;

always @(posedge clk, negedge clr_n)
begin
  if (!clr_n) begin
    partial = a & b;
    y_prereg = ~c | partial;
    y <= 1'b0;
  end
  else begin
    partial = a & b;
    y_prereg = ~c | partial;
    y <= y_prereg;
  end
end
endmodule

The following code is more compact than the initial separated version, but leads to warnings

reg y,y_prereg,partial;
always @(posedge clk, negedge clr_n)
//------
begin
  if (!clr_n) y <= 1'b0;
  else begin
    partial = a & b;
    y_prereg = ~a | partial;
    y <= y_prereg;
  end
end
//-------

implied registers and latches are trimmed since they are only used inside this procedural code block and feed into another signal

If the are used outside, additional sequential logic would be generate to provide the saved values externally

WARNING:Xst:646 - Signal <y_prereg> is assigned but never used. This unconnected signal will be trimmed during the optimization process.
WARNING:Xst:646 - Signal <partial> is assigned but never used. This unconnected signal will be trimmed during the optimization process.

The warning tells us that these are not used externally to the procedural block and so they are trimmed-- we’ll call this a trimmable implementation and I allow it even though beginners probably should avoid this style.

The variable scope used for y_prereg and partial is unessisary.

Trimmable Style

If you follow our coding guidelines, you must use blocking assignment for outputs of combinatorial logic even if within edge-triggered procedural blocks.
Any signal that is both
(1) assigned by a BLOCKING assignment AND
(2) assigned withing an edge triggered block

I) should be considered as an internal net and you
should not (AND MAY NOT FOR THIS COURSE) use
that net outside the block in which it is assigned
II) must be declared as a local variable, requiring a
named block (see prev. slide), and thus enforcing I)

Named Blocks and Local Variables

placing a colon and a name after the keyword begin creates a named block with a named variable scope

  • With this, variables (reg) may be defined within the block that do not exist outside the block
  • This can be useful for creating internal partial results that should not be used external to the block

Local variables are preferred to disallow the mistake of using the combinatorial outputs from an edge-triggered block by keeping them declaring them locally.
(This is part of a Verilog coding style convention to softly enfore what VHDL handles more explicitly. ) This along with guidelines that will be refined in the slideset “Suggested Coding and Design Practices allow use to represent both sequntial and combinatorial hardware in the same procedural block. The alternative is to always separate combinatorial and seqential hardware, which I don’t find practical and is limiting compared to standing coding styles in VHDL.

Use of named block and local variables to explicitly constrain use of internal variables:

reg y;
always @(posedge clk, negedge clr_n)
begin: blockY                 
 /*************************************************//**/  
 /* Depends on a,b, pre-existing value of y, clr_n //**/
 /*************************************************//**/  
 /**/ reg y_prereg,partial;                         /**/
 /**/  y_prereg=1'bx;partial=1'bx;                  /**/
 /**/  if (!clr_n) y <= 1'b0;                       /**/
 /**/  else begin                                   /**/
 /**/    partial = a & b;                           /**/
 /**/    y_prereg = ~a | partial;                   /**/
 /**/    y <= y_prereg;                             /**/
 /**/  end                                          /**/
 /*****************************************************/
 /* Output: block updates value of y                /**/
 /* Side effect: updates to y_prereg and partial    /**/
 /*************************************************//**/  
end

Simple Testbench with a Clock

module mydevice_tb();
  reg clk, rst; // many signals will be reg since they are driven by procedural code in the testbench
  reg x1, x2; 
  wire y1, y2; // Outputs from the module under test are simply structural connections at this level so wires are used


  
  mydevice DUT(clk,rst, y1,y2, x1,x2); //An instance of the device under test

  
  initial clk = 0; // An initial statement or block can set initial values of signals

  always begin  //A always block with delays can be used to drive cyclic signals
    #50; //delay 
    clk = ~clk;
  end

  initial begin //Stops simulation at T=1000
    #1000 
    $finish;
  end

  // Initial value  and a change at T=10
  initial begin 
    rst = 1;
    #10; //delay 
    rst = 0;
  end

  /* Intialize signals immediately if not otherwise initialized, 
     then add delays and assignments We'll see other examples later, 
     but at first avoid changing signals input to clocked blocks at 
     the same time as the clock edge it is sensitive to
  */

  initial begin 
    y1=0;
    y2=0;

    #50; //delay 
    y1=1;
    #50; //delay
    y1=0;
    y2=1;
    #50; //delay 
    y1=1;
    y2=0;
  end

// This testbench includes no print or output statements, so it is assumed 
that a results waveform viewer (GUI) is used

endmodule //end testbench module

Constants should use localparal

For more discussion, see http://www.sunburst-design.com/papers/CummingsHDLCON2002_Parameters_rev1_2.pdf

Avoid magic numbers and use local parameters
localparam is new with Verilog 2001 For older code, you might also see the keyword parameter, but that defines a value that can be overridden when a module is instantiated

localparam a=31; //int
localparam a=32,b=31; //int (multiple on one line)
localparam byte_size=8, byte_max=bytesize-1; //int
localparam a =6.22; //real
localparam delay = (min_delay + max_delay) /2;//real
localparam initial_state = 8'b1001_0110; //reg

Vectors

In C, standard variable types are constrained by the processor architecture, but for custom hardware it makes sense to allow declaring a variables of various bit lengths. We do this by appending the type with and bit address range.
“A net or reg declaration without a range specification shall be considered 1 bit wide and is known as a scalar. Multibit net and reg data types shall be declared by specifying a range, which is known as a vector.” IEEE Verilog Specification
Standard bit vectors defined by a high to low index are created with square brackets and colon preceding the identifier
<type> [<HighBit>:<LowBit>] id;
They are accessed using square brackets for single bits and additionally using a colon if accessing a slice of bits. Examples from IEEE Specification:

reg[3:0] v;  // a 4-bit vector reg made up of (from most to 
             // least significant) v[3], v[2], v[1], and v[0]
reg [-1:4] b; // a 6-bit vector reg
reg [4:0] x, y, z; // declares three 5-bit regs

Bit Slicing

Expanding and slicing vectors is supported for evaluation and assignment

reg[7:0] v;
wire msb,lsb;
wire [3:0] highNibble, 
           lowNibble;
wire [7:0] s;


…

assign lsb = v[0];
assign msb = v[7];
assign highNibble = v[7:4];
assign lowNibble = v[3:0];

s[3:0] = 4’b0000;
s[7:0] = lowNibble;

Arrays

Whereas vectors essentially define a new variable type with a specified bit range and length, array allow creating an addressable group of elements of the same type
Standard arrays defined by a high to low index are created with square brackets and colon following the identifier
id[:];
They are accessed using square brackets

reg x[11:0];  //scalar reg array with length of 12
…
//within procedural code…
x[3] = 1’0;

Memories

Standard memories vectors defined using both bit vector and object array notation
<type> [<HighBit>:<LowBit>] id [<HighAddress>:<LowAddress>];
The objects (e.g. words) can be accessed like this:
id[address]
Individual bit slices can be accessed like this:
id[address][highbit:lowbit]

reg [7:0] ram[0:4095]; // 4096 memory cells that are 8 bits wide

//code excerpt from Chapter 2 SRAM model
input [11:0] ABUS;  // 12-bit address bus to access all 4096 memory cells
inout [7:0] DATABUS;// 8-bit data bus to write into and out of a memory cell
reg  [7:0] DATABUS_driver;
wire [7:0] DATABUS = DATABUS_driver; //inout must be driven by a wire
....
for (i=0; i <= 4095; i = i + 1)  // Setting individual memory cells to 0
  ram[i] = 0;
end
....
ram[ABUS] = DATABUS;  //writing to a memory cell
....
DATABUS_driver =  ram[ABUS]; //reading from a memory cell

High to Low Convention

I STRONGLY recommend sticking with high to low indexing for all array and vectors definitions and access Create a mix leads to errors.
One reasonable exception for read access is
bit order reversing for a bus:

wire [31:0] a;
wire [31:0] b;

assign b = a[0:31]

Detail: Some additional Behavioral Data Types: integer, real, and time

Integar types

  • 4-State: reg, logic(SystemVerilog),integertype,: 4-state
  • 2-State: int,shortint,longint,byte,bit

Signedness:

  • Default Signed: byte, shortint, int, integer, and longint default to signed

  • Default Unsigned: time, bit, reg, and logic

  • Signedness can be provdided using keywords signed and unsigned

    int unsigned ui;
    int signed si;
    

Real:

  • real is same as C double, also realtime
  • shortreal is the same as C float
  • Unlike C Real to integer conversion is defined as rounding
  • Upon conversion to a real type, z and x are interpretted as zero

Conversions:

  • Recommended to use explicit conversion using casting or system tasks like $realtobits (see Verilog Manual)

Operators

What follows is a quick overview of a subset of operators in Verilog. This will familiarize you sufficiently with various operators and syntax so that you can start working with Verilog, though more advanced details will be discussed in later lectures.
Some additional examples may be found here:
http://www.asic-world.com/verilog/operators1.html
http://www.asic-world.com/verilog/operators2.html

Logical Operators

Logical Operators force the evaluation of input arguments to TRUE or FALSE and result in a single TRUE or FALSE.

Operator Name Examples
! logical negation if (!(a==b)) , if (!a)
&& logical and if ((c==a)&&(c==b))
|| logical or if ((c==a)||(c==b))

Bitwise Operators

produce a multi-bit result
The bitwise negation operator ~ takes one argument (it is a “unary” operator) and acts in parallel on the individual bits
Example:

wire [3:0] a,y;
assign a = 4’b0101;
assign y = ~a;

Other bitwise operators take two arguments and act in parallel on pairs of bits. Logical and && or ||
Example:

wire [3:0] a = 4’b0101,b= 4’b0011,y;
assign y = a&b; //produces 0001 

Unary Reduction Operators

take a single input argument of one or multiple bits
output a single bit (it reduces a multi-bit input to a single-bit output)
Example:

wire a = 4’b0001;
wire y1,y2;
assign y1 = |a;
assign y2 = |a;

(Unary and Binary) Bitwise and Unary Reduction Operators

Operator Name Examples
| & bitwise or bitwise and y=a|b;
| & reduction or reduction and y = |b;
~| ~& reduction nor reduction nand y = ~|b;
a^b bitwise xor y = a^b;
^b reduction xor y = ^b;
~^ ^~ bitwise xnor y = a~^b;
~^ ^~ reduction xnor y = ~^b;
~ bitwise negation y = ~a;

Logical and Relational Comparisons

Operator Name, Description Examples
== logical equality if (a==b)
!= logical inequality if (a!=b)
a>b relational greater than if (a>b)
>= relational greater than or equal if (a>=b)
< relational greater than if (a<b)
<= relational greater than or equal if (a<=b)

Shift and Mathematical Operations

Operator Name, Description Examples
>>,<< shift right or left by a number of positions a = shiftvalue >> 2;
<<<,>>> Signed shifts, shift right or left by a number of positions with a signed left argument. Signed variables may be declared with the keyword signed, integers are signed by default
+,-,*,/,% Arithmetic Operators. Note for division: synthesizers may only support divide or modulo by constant power of two y=a+b; y=b/4; //right shift by 2
** Power. Note: synthesizers may only support a right argument that is a constant c=a**3;

Assignment in Procedural Code

There are two types of assignments that you will see in procedural code

Operator Name, Description Examples
<= non blocking assignment statement, schedules assignment but allows next statement to execute y <= a & b;
= blocking assignment statement, blocks execution of next statement until assignment to left-hand argument (e.g. y) is completed y = a & b;

Concatenation and Replication Operators

Operator Name, Description Examples
{ , } Concatenation: concatenation of one, two, or more operands {4'b1111,4'b0000} {2'b11,2'b11,2'b00,2'b00} Both produce 8'b11110000
{n{x}} Replication: Allows fixed number of replications (n must be a constant) assume a=16'hFFFF; then
2{a} produces 32'hFFFFFFFF
2{a} produces 32'hFFFFFFFF
{16{1'b0},a} produces 32'h0000FFFF
8{2'b10} roduces 16'b1010101010101010

Case Equality

  • Logical and mathematical comparisons can produce unkown outputs when an input bit
    is x or z
  • To explicitly check for metalogic values, use case equality.
  • In this course, USE ONLY FOR TESTBENCH OR OTHER VERIFICATION CODE AND DO NOT ATTEMPT TO USE TO SYNTHESIZE HARDWARE.
Operator Name, Description Examples
== logical quality if (bus == 4’b0000)
=== case equality if (bus === 4’bzzzz)
~= logical inequality
!== case inequality if (bus !== 4’bzzzz)

Appendix

In this code, I assigned combinatorial variables x (don’t care) at the top of the code to see if Xilinx ISE not complain, but it still does. Following it are two versions along with the Xilinx ISE synthesis report.

reg y;
always @(posedge clk, negedge clr_n)
begin: blockY
  reg y_prereg,partial;
  y_prereg=1'bx;partial=1'bx;
     if (!clr_n) y <= 1'b0;
  else begin
    partial = a & b;
    y_prereg = ~a | partial;
    y <= y_prereg;
  end
end

Trimmable Without Combinatorial Assignment to X:

module trimmable(output reg y, input a, input b, input clk,input clr_n);
always @(posedge clk, negedge clr_n)
begin: blockY
  reg y_prereg,partial;
  if (!clr_n) y <= 1'b0;
  else begin
    partial = a & b;
    y_prereg = ~a | partial;
    y <= y_prereg;
  end
end
endmodule

Started : "Synthesize - XST".
Running xst...
Command Line: xst -intstyle ise -ifn "/home/robucci/Nextcloud/covail/Courses/CMPE415/XilinxProjects/test_synth/trimmable.xst" -ofn "/home/robucci/Nextcloud/covail/Courses/CMPE415/XilinxProjects/test_synth/trimmable.syr"
Reading design: trimmable.prj

=========================================================================
*                          HDL Compilation                              *
=========================================================================
Compiling verilog file "trimmable.v" in library work
Module <trimmable> compiled
No errors in compilation
Analysis of file <"trimmable.prj"> succeeded.
 

=========================================================================
*                     Design Hierarchy Analysis                         *
=========================================================================
Analyzing hierarchy for module <trimmable> in library <work>.


=========================================================================
*                            HDL Analysis                               *
=========================================================================
Analyzing top module <trimmable>.
Module <trimmable> is correct for synthesis.
 

=========================================================================
*                           HDL Synthesis                               *
=========================================================================

Performing bidirectional port resolution...

Synthesizing Unit <trimmable>.
    Related source file is "trimmable.v".
WARNING:Xst:646 - Signal <blockY/y_prereg> is assigned but never used. This unconnected signal will be trimmed during the optimization process.
WARNING:Xst:646 - Signal <blockY/partial> is assigned but never used. This unconnected signal will be trimmed during the optimization process.
    Found 1-bit register for signal <y>.
    Summary:
inferred   1 D-type flip-flop(s).
Unit <trimmable> synthesized.


=========================================================================
HDL Synthesis Report

Macro Statistics
# Registers                                            : 1
 1-bit register                                        : 1

=========================================================================

Trimmable With Combinatorial Assignment to X:

module trimmable(output reg y, input a, input b, input clk,input clr_n);
always @(posedge clk, negedge clr_n)
begin: blockY
  reg y_prereg,partial;
  y_prereg=1'bx;
  partial=1'bx;
  if (!clr_n) y <= 1'b0;
  else begin
    partial = a & b;
    y_prereg = ~a | partial;
    y <= y_prereg;
  end
end
endmodule
Started : "Synthesize - XST".
Running xst...
Command Line: xst -intstyle ise -ifn "/home/robucci/Nextcloud/covail/Courses/CMPE415/XilinxProjects/test_synth/trimmable.xst" -ofn "/home/robucci/Nextcloud/covail/Courses/CMPE415/XilinxProjects/test_synth/trimmable.syr"
Reading design: trimmable.prj

=========================================================================
*                          HDL Compilation                              *
=========================================================================
Compiling verilog file "trimmable.v" in library work
Module <trimmable> compiled
No errors in compilation
Analysis of file <"trimmable.prj"> succeeded.
 

=========================================================================
*                     Design Hierarchy Analysis                         *
=========================================================================
Analyzing hierarchy for module <trimmable> in library <work>.


=========================================================================
*                            HDL Analysis                               *
=========================================================================
Analyzing top module <trimmable>.
Module <trimmable> is correct for synthesis.
 

=========================================================================
*                           HDL Synthesis                               *
=========================================================================

Performing bidirectional port resolution...

Synthesizing Unit <trimmable>.
    Related source file is "trimmable.v".
WARNING:Xst:646 - Signal <blockY/y_prereg> is assigned but never used. This unconnected signal will be trimmed during the optimization process.
WARNING:Xst:646 - Signal <blockY/partial> is assigned but never used. This unconnected signal will be trimmed during the optimization process.
    Found 1-bit register for signal <y>.
    Summary:
	inferred   1 D-type flip-flop(s).
Unit <trimmable> synthesized.


=========================================================================
HDL Synthesis Report

Macro Statistics
# Registers                                            : 1
 1-bit register                                        : 1

=========================================================================
≡