ENEE 449 - Fall 1998

Lecture 2 - Verilog Syntax and Behavioral Modeling


Objective

  1. Understand the Verilog language syntax.
  2. Understand the constructs used to model the behavior of digital systems.

1. Lexical Conventions

  1. Whitespace (space, tab, newline): Ignored in Verilog except when separating tokens. Not ignored in strings.
  2. Comments - Single Line (//) - Multiple line (/* */)
  3. Operators

    Operator Type
    Operator Symbol
    Operation Performed
    Number of Operands
    Arithmetic
    *
    /
    +
    -
    %
    multiply
    divide
    add
    subtract
    modulus
    2
    2
    2
    2
    2
    Logical
    !
    &&
    ||
    logical negation
    logical and
    logical or
    1
    2
    2
    Relational

    >
    <
    >=
    <=

    greater than
    less than
    greater than or equal
    less than or equal

    2
    2
    2
    2

    Equality ==
    !=
    ===
    !==
    equality
    inequality
    case equality
    case inequality
    2
    2
    2
    2
    Bitwise ~
    &
    |
    ^
    ^~ or ~^
    bitwise negation
    bitwise and
    bitwise or
    bitwise xor
    bitwise xnor
    1
    2
    2
    2
    2
    Reduction &
    ~&
    |
    ~|
    ^
    ^~ or ~^
    reduction and
    reduction nand
    reduction or
    reduction nor
    reduction xor
    reduction xnor

    1
    1
    1
    1
    1
    1

    Shift >>
    <<

    Right shift
    Left shift

    2
    2
    Concatenation
    Replication
    { }
    { \d{ } }
    Concatenation
    Replication
    any
    any
    Conditional ?: Conditional 3

  4. Number Specification
    1. Sized Numbers
      <size>'<base_format> <number>
      <size> is a decimal number and specifies the number of bits in the number
      <base_format> is one of [dD] - decimal, [hH] - hex, [bB] - binary, [oO] - octal.
      <number> is a string of characters representing legal number types for the specified base format ([0-9] for decimal, [0-9,a-f,A-F] for hex, [0-1] for binary, [0-7] for octal.
    2. Unsized numbers
      Numbers specified without the <base_format> specification are decimal numbers by default. Numbers that are written without a <size> specification have simulator-specified number of bits (that must be at least, and usually is, 32 bits - which we will assume for this course).
    3. Values - 0, 1, x (unknown), z (high impedance)
      An x or z sets 1 bit in the binary case, three bits in the octal case and four bits in the hexadecimal case. If the most significant represented bit of a number is 0, x, or z, then the number is automatically extended to fill the most significant bits with that value. If the most significant represented bit is a 1, then the number is 0 extended.
    4. Negative numbers
      Negative numbers can be specified by putting a minus sign before the size for a constant number. It is illegal to have a minus sign between <base_format> and <number>.

      123
      32 bit decimal
      'h7C4 32 bit hex
      'o765 32 bit octal
      4'b11 4 bit binary
      3'b10x 3 bit binary
      9'bx10 9 bit binary, x-extended
      5'd3 5 bit decimal
      -4'b11 4 bit binary two's complement of 0011 == 1101.
      The part may not contain a sign. Any sign must go on the front.

  5. Identifiers
    Identifiers are names given to objects so that they can be referenced in the design. Identifiers are made up of alphanumeric characters, the underscore, and the dollar sign ($). Identifiers are case sensitive and cannot start with a number or the dollar sign. The dollar sign is reserved as the first character for system tasks.
    An identifier can be "escaped" using the backslash '\' and ends with whitespace. Using escaped identifiers, any printable ASCII character can be used in the identifier. Escaped identifiers are commonly used by verilog netlist generators.

2. Data Types

  1. Nets (note that net is not a keyword) - Nets represent connections between hardware elements. As in hardware, nets have values continuously driven on them by the outputs of the devices that they are connected to. Typically, nets are declared as type wire. Undefined variables default to 1 bit wires. If a net has no driver, it will default to 'X'. Other types of nets are wand, wor, tri, triand, trior ...
  2. Registers - Registers are data storage elements that retain their value until another value is placed on them. Despite the poor choice of names, remember that registers are not equivalent to the hardware flip-flop. In Verilog, a register simply holds a value - the main difference between the Verilog register and the hardware flip-flops is that the flip-flop is synchronous. The register can be set at any time. Furthermore, registers do not need a driver. They can be set anywhere in the simulation from other registers, from the value of wires, or from constants. The default value for an unassigned register is 'X'.
    1. Vectors (this applies to wires as well)
      vectors are declared as [highbit : lowbit] or [lowbit : highbit], but the leftmost number is always the most significant bit of the vector (remember when extending).
      When referencing a vector, range indices must be constants - bit indices can be variables.

      Declaration
      Reference
      Comment
      reg bus[7:0] bus[4:0] The 5 least significant bits of bus.
      wire addends[4:2] addends[3:2] The 2 least significant bits of addends
      wire addends[2:4] addends[2:3] The two most significant bits of addends
      reg value [5:0] value[var:1] Illegal - indices must be constant
      reg value [5:0] value [n] The n'th bit of value where n is a register or net.

    2. Integer - Be warned that a reg variable is unsigned and that an integer variable is a signed 32-bit integer (more technically, at least a 32 bit integer - the bitsize is implementation specific, but must be at least 32 bits). This has important consequences when you subtract. Integers are very useful in testbenches or when representing variables that don't get synthesized (like loop counters). This insures that you have enough room for any data that you want to put into the integer.
    3. Real - Real numbers are not synthesizable They are used almost exclusively in testbenches.
    4. Time - Time variables are again, implementation specific, but are usually 64 bits in size. They are used to mark simulation time. The system function $time returns the current simulation time.
    5. Arrays/Memories - Arrays are allowed for reg, integer, and time data types. Multidimensional arrays are not permitted in verilog.
      Memories are specified as vectors of registers. For example, MyMem is 1K words each 32-bits.
      reg [31:0] MyMem [0:1023]

      The notation MyMem[0] references the zeroth word of memory. The array index for memory (register vector) may be a register. Notice that one can not reference a memory at the bit-level in Verilog HDL. If you want a specific range of bits in a word of memory, you must first transfer the data in the word to a temporary register.

    6. Strings - Technically speaking, the string is not a separate data type in verilog. Strings are stored in register vectors, and each character takes 8 bits (1 byte). Strings are typically declared as a register vector that is 8 times the size of the expected string; e.g.
    7. reg string [8*strlen : 1] mystring;

      Whenever you see the above vector notation (8*var:1) in a reg declaration, that reg is almost certainly a string.

    8. Parameters - Verilog allows a module to define parameters. These parameters can be used as constants within the module. When a module is instantiated, it can be instantiated with unique parameters, so that a generic counter module can be implemented with a parameter, and then specific instantiations can specify the parameter. Notice that a parameter is considered as a constant inside the Verilog module. It can thus be used in a vector range index

3. Continuous Assignments

A continuous assignment is the most basic statement in dataflow modeling. Using a continuous assignment, a value can be driven onto a net.
  1. The assign statement
    1. assign <drive_strength> <delay> <list_of_assignments>;
    2. The left hand side must always be of type net. (It can be a scalar, vector, or concatenation).
    3. Continuous assignments are always active and change whenever the right-hand-side operands change.
    4. The right-hand-side operands can be registers or nets or function calls (vectors or scalars or concatenations are allowed).
  2. Delay values can be specified for assignments in terms of time units.
               
    assign out = i1 & i2;
    assign addr[15:0] = addr1_bits[15:0] ^ addr2_bits[15:0];
    assign (c_out, sump3:0]} = a[3:0] + b[3:0] + c_in; 
  3. Implicit Continuous Assignment - rather than declaring a wire and then doing a continuous assignment, the two statements can be done together.
    wire out = in1 & in2;
  4. Assignment Delay
    assign #10 out = in1 & in2;
  5. Implicit continuous assignment delay
wire #10 out = in1 & in2;        
     

4. Behavioral Modeling

  1. The Process Model - The essence of the behavioral model is the process. It can be thought of as an independent thread of control.
    1. The initial block
      Initial blocks are set to execute at time 0 of the simulation. They proceed sequentially through the block, terminating on the final statement of the block.
    2. The always block
      The always block continuously executes it's block, never exiting or stopping. A behavioral model can contain one or more always statements.
    3. Block suspension
      Block processing can be suspended when an event statement (@) or a delay statement (#) is encountered, or, potentially when a wait statement is executed.
  2. If - Else
    In an if statement, the else is paired with the nearest, unfinished if statement. Begin-end pairs can be used to avoid confusion.
    The ?: conditional operator can be used when one of the two values is to be selected for assignment.
  3. Loops
    1. Repeat
      integer count;
      initial begin 
         count = 0; 
         repeat (128) begin
            $display("Count = %d", count);
            count = count + 1;
         end
      end
    2. For
      integer count;
      initial begin 
         for (count = 0; count < 128; count = count + 1) //notice the 3rd term
            $display("Count = %d", count);
         end
      end
    3. While
      integer count;
      initial begin 
         while (count < 128) begin
            $display("Count = %d", count);
            count = count + 1;
         end
      end
    4. Forever
      reg clock;
      initial begin 
         clock = 1'b0; 
         forever #10 clock = ~clock;
      end
  4. Breaking out of loops
    1. Named loops
    2. Disable statement
  5. Case Statements
    reg ready;
    
        case (ready)
            1'bz: $display ("ready is high impedance");
            1'bx: $display ("ready is unknown");
            default: $display ("ready is %b", ready);
        endcase
  6. Casez and Casex - Casez allows z values to be treated as don't-care values, Casex allows both z and x values to be treated as don't care values.
    r = 8'bx1x0x1x0;
    casex(r)
        8'b001100xx: statement1;
        8'b1100xx00: statement2;
        8'b00xx0011: statement3;
        8'bxx001100: statement4;
        endcase
  7. Tasks and Functions - Verilog functions and tasks are similar to software functions and tasks except that the source for the function/task must be included inside a module, rather than outside of it. For functions and tasks that find use in multiple modules, this is normally done by putting the function/task in a separate file and including the function/task in the module via the `include directive.
    1. Tasks
      1. Called from a statement.
      2. Parameters passed to and from the tasks - these are not like ports to a module
        module mark1Task;
            reg [15:0] m [0:8191]; // 8192 x 16 bit memory
            reg [12:0] pc; // 13 bit program counter
            reg [12:0] acc; // 13 bit accumulator
            reg ck; // a clock signal
            always
                begin: executeInstructions
                    reg [15:0] ir; // 16 bit instruction register
                    @(posedge ck)
                        ir = m [pc];
                    @(posedge ck)
                        case (ir [15:13])
                            // other case expressions as before
                            3'b111 : multiply (acc, m [ir [12:0]]);
                        endcase
                    pc = pc + 1;
                end
        
            task multiply;
            inout [12:0] a;
            input [15:0] b;
                begin: serialMult
                    reg [5:0] mcnd, mpy;//multiplicand and multiplier
                    reg [12:0] prod;//product
                    mpy = b[5:0];
                    mcnd = a[5:0];
                    prod = 0;
                    repeat (6)
                        begin
                            if (mpy[0])
                                prod = prod + {mcnd, 6'b000000};
                            prod = prod >> 1;
                            mpy = mpy >> 1;
                        end
                    a = prod;  // note that a is not an inout port (wire data type)
                end
            endtask
        endmodule
    2. Functions
      1. Called from within an expression.
      2. Returns a value to an expression.
      3. Has one output (the function name) and at least one input.
      4. May not include event (@) or delay (#) statements, or , to say it another way, a function must execute in one simulation time unit.
        module mark1Fun;
                reg [15:0] m [0:8191]; // 8192 x 16 bit memory
            reg [12:0] pc; // 13 bit program counter
            reg [12:0] acc; // 13 bit accumulator
            reg ck; // a clock signal
            always
                begin: executeInstructions
                    reg [15:0] ir; // 16 bit instruction register
                    @(posedge ck)
                        ir = m [pc];
                    @(posedge ck)
                        case (ir [15:13])
                            //case expressions, as before
                            3'b111: acc = multiply(acc, m [ir [12:0]]);
                        endcase
                    pc = pc + 1;
                end
            function [12:0] multiply;
            input [12:0] a;
            input [15:0] b;
                begin: serialMult
                    reg [5:0] mcnd, mpy;
                    mpy = b[5:0];
                    mcnd = a[5:0];
                    multiply = 0;
                    repeat (6)
                        begin
                            if (mpy[0])
                                multiply = multiply + {mcnd, 6'b000000};
                            multiply = multiply >> 1;
                            mpy = mpy >> 1;
                        end
                end
            endfunction
        endmodule
  8. Scope and Hierarchical Names - The '.' is the hierarchical delimeter.Verilog allows forward referencing (referring to the item before it has been defined) on modules, tasks, functions, and named blocks. It does not allow forward referencing for register and net accesses.
module top;
    reg r;		//hierarchical name is top.r
    wire w;		// hierarchical name is top.w
    b instance1();

    always
        begin: y
        reg q;	//hierarchical name is top.y.q
    end

    task t;
        begin: c	//hierarchical name is top.t.c
            reg q;	//hierarchical name is top.t.c.q
            disable y; // OK
        end
    endtask
endmodule

module bl
    reg s;		//hierarchical name is top.instance1.s

    always
    begin
        t;		//OK
        disable y;    //OK
        disable c;    //Nope, c is not known
        disable t.c;	//OK
        s = 1;	//OK
        r = 1;	//Nope, r is not known
        top.r = 1;	//OK
        t.c.q = 1;	//OK
        y.q = 1;	//OK, a different q than t.c.q
    end
endmodule
     

Last updated Mon, 21 Sep 1998 12:53:09 GMT