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

Types of primitives

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

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

Line 3

Line 7

Line 8

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 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 and RTL model (Initial and Always Blocks)

Edge-Sensitive vs Value-Sensitive Functions

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:

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

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

./images/metalogic_bus2.svg

Details for Reading

Structural Data Types: wire and reg and the others

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

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

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

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

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

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?

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.

Trimmable-Logic Style

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

Logic Primatives

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)

Design Strategies

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
  1. Sequential and combinatorial logic in same block with combinatorial logic embedded in sequential assignments
  1. Sequential and combinatorial logic in same block with both combinatorial and sequential assignments

Learning the Verilog Styles

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

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] = 10;

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

Signedness:

Real:

Conversions:

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

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

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