Synthesizable Behavioral Extensions

Introduction

Some of the additional features found in the new VER:

  • Nestable `ifdef, `else, `endif preprocessor directives.
  • `define preprocessor directive.
  • Combinational ALWAYS blocks
  • Synchronous ALWAYS blocks (both posedge/negedge clock allowed [CSIM only])
  • Procedural statements: if-else, begin-end, non-blocking assignment
  • Operators +, -, &&, ||, ==, and != (also allowed outside ALWAYS blocks)
  • Implicit operator != 0 if a vector is used in an if-else condition
  • Case statements
  • Blocking and nonblocking behavioral assignments

    Examples

    Better support for parameters
    module paramtest (y, a);
    parameter       SZ = 3;
    output [SZ-1:0] y;
    input           a;
    
    parameter  Y_0 = SZ'b0,
               Y_1 = SZ'b1;
    
    assign y = a ? Y_0 : Y_1;
    
    endmodule // paramtest
    

    Always blocks
    module bar (q, d, reset, en, scan_in, scan_test, ck);
    output       q;
    input        d, reset, en, scan_in, scan_test, ck;
    
    reg          q, din;
    
    always @ (posedge ck)           // clocked process: q is a flipflop
       if (reset)
          q = 1'b0;
       else if (en)
          q = din;
    
    always @ (d
              or scan_test
              or scan_in)           // combinational process: a mux
       if (scan_test)
          din = scan_in;
       else
          din = d;
    
    endmodule // bar
    
    
    module foo (y, a, b, p, q, r);
    output [3:0] y;
    input [3:0]  a, b;
    input        p, q, r;
    
    reg [3:0]    y;
    
    always @ (a or b or p or q or r)
       begin
          y[3:0] = 4'h0;
          if (a == b)     // this synthesizes a XOR/NOR comparator
             begin
                if (p & q)
                   y[2:1] = 2'b11;
             end
          else if (a)     // interpreted as 'a != 0'
             y[0] = r;
       end
    
    endmodule // foo
    

    Case-endcase. Note the use of PRAGMA parallel_case to indicate that the case switch should be implemented as a AND-OR switch instead of a series of prioritized MUXes. Instead of the PRAGMA keyword you can also use SYNOPSYS, as in // synopsys parallel_case...
    module bar(y, z, a, ck);
    output [3:0] y;
    reg [3:0]    y;
    output [3:0] z;
    input [3:0]  a;
    input        ck;
    
    always @ (posedge ck)
       begin
          y[2] = a[3];
          casex (a[2:1])                    // pragma parallel_case
            2'bx0: y[1] = 1'b1;
            2'b11:
               begin
                  y[2:1] = 2'b00;
                  case (a[3:2])
                    2'b01: y[2] = 1'b0;
                    default y[2] = a[0];
                  endcase
                  if (a[0] != 1'b1)
                     y[1] = 1'b1;
               end
            default y[1] = a[3];
          endcase
       end
    
    assign z = a[3:2] ? 4'b1010
                      : a[1] ? 4'b0000
                             : 4'b1100;
    
    endmodule // bar
    

    Blocking v. Nonblocking Behavioral Assignments

    Non-blocking assigns use the <= symbol instead of = and they have no effect until after the execution of the always block. Blocking assignments take effect immediately. Example:

       always @ (ck)
       begin
          a <= b;
          b <= a;
       end
    
    swaps the values in register a and b. But ...
       always @ (ck)
       begin
          a = b;
          b = a;
       end
    
    ....copies b to register a, but it also copies b to register b (i.e. nothing happens to register b itself).

    Until now, ver used the = symbol to implement a mix of blocking and non-blocking semantics. (`Non-blocking' because assignment did not take effect immediately, but also `blocking' because the sequential order of the assignments was guaranteed, unlike non-blocking semantics, see your favorite Verilog book, Thomas & Moorby I believe?).

    In Thomas & Moorby verilog the following yields unpredictable results:

       begin
         a <= 1'b1;
         a <= 1'b0;
       end
    
    The verilog simulator cannot guarantee that the second assignment to a overrules the first assignment to a. However, in the synopsys semantics they actually do guarantee that a will be 1'b0. I have taken the synopsys road for ver, because this is also compatible with T & M. (And because it is easier to program in ver/behave.c because now I don't have to randomize the results for non-blocking assignments).

    I have tested the new code quite extensively for a change. One thing I discovered was that some big circuit of mine would not run anymore, but that was a problem with the verilog because the semantics of `=' had slightly changed. Replacing it with <= (or changing the order of the blocking assignments inside my always block) fixed the problem.

    I believe this is serious step to getting closer to real verilog semantics with ver. Oh yeah, I also added delay control to procedural assignments, i.e. `y <= #1 a'. Its semantics are ignored but it comes in very handy when compiling code that was written for Synopsys or Cadence.

    For some funny reason the delay control bumps up the number of shift/reduce conflicts from 2 to 14, but this does not seem to affect the parser in a bad way (it still parses the way it used to do as far as I can tell).

    The other thing that I like to add one day is named blocks, as in:

       module foo;
       reg bar;
    
       always @ (...)
       begin: a_new_scope
         reg foo_in_a_new_scope;
         ...
       end
       endmodule
    
    This is being used liberally in a bunch of verilog example code that I ftp'ed from the Xilinx web-site. I think it is nice if ver is capable of compiling all those examples. I believe the named blocks are the only thing missing from ver to handle the examples.

    Enjoy!

    For more involved examples, click here or here.
    Paul Stravers


    11dec99 bybell@linux-workshop.com / bybell@nc.rr.com
    Return to the Ver Homepage...