Synchronous design with Verilog

Synchronous design with Verilog

Purely synchronous design is based upon the principle that all state elements (dtypes) can only be changed on a single event trigger (for instance, a positive clock edge).

One of the key benefits of this is that glitches on a control line do not lead to unexpected changes since when they take effect (on the trigger event) the glitch will have gone away.

Verilog coding practice

In some styles of Verilog, an output is continuously updated by enclosing it in an always block and putting all of its inputs on the event list. Thus a s-1 multiplexor could be:

        module contAss_A(out, cnt, a, b);
           output out;
           input  cnt, a, b;
           reg    out;

           always @(cnt or a or b)
              out = cnt ? a: b;

        endmodule // contAss

Notice that out must be declared as a register to allow it to be the target of an assignment within an always block.

In some cases this permits clarity as in:

        module contAss_B(out, cnt, a, b);
           output out;
           input  cnt, a, b;
           reg    out;

           always @(cnt or a or b)
              if (cnt == 1) out = a;
              else if (cnt == 0) out = b;
              else $display("unexpected value for cnt (%b)", cnt);

        endmodule

However, in other cases a simple continuous assignment statement will suffice:
        module contAss_C(out, cnt, a, b);
           output out;
           input  cnt, a, b;

           assign out = cnt ? a: b;
        endmodule

Notice: no reg declaration.

In the first two examples, the reg declaration was simply made to allow the assignment within an always block: there was NO intended correspondence with a "register" which would hold the value of out (since the code states that this changes whenever the input changes). However, when we DO want a software equivalent of a state element there should be ONLY ONE ENTRY ON THE EVENT list.

Thus the basic, bog-standard Dtype is:

        module dtype(out, clk, d);
           output out;
           input  clk, d;
           reg    out;

           always @(posedge clk)
              out = d;

        endmodule

and, of course, here the reg actually does hold its value in between changes positive clock edges.

It is simple to enhance this module with additional functionality. Thus, for instance, we can add a synchronous reset:

        module dtype_r(out, clk, reset, d);
           output out;
           input  clk, reset, d;
           reg      out;

           always @(posedge clk)
              if (reset == 1)
              out = 0;
              else
              out = d;

        endmodule

or perhaps a set and an enable signal (which updates the output only if the enable control signal is high):
        module Etype_s(out, clk, enable, set, d);
           output out;
           input  clk, enable, set, d;
           reg      out;

           always @(posedge clk)
              if (set == 1)
              out = 1;
              else if (enable == 1)
              out = d;

        endmodule

To repeat: in synchronous design, there should only be one (edge-triggered) event guarding the always statements which implement state elements.

Thus the following is NOT recommended:

        module dtype_A_r(out, clk, reset, d);
           output out;
           input  clk, reset, d;
           reg      out;

           always @(posedge clk or posedge reset)
              if (reset == 1)
              out = 0;
              else
              out = d;

        endmodule

since any positive transition (even a brief "hazard") on the reset signal will cause the dtype to be reset.

Of course, the state elements do not need to be as simple as these examples. For instance. they could be:

Test vector strategy

Synchronous design assumes that all signals settled down when the next active clock edge arrives. Thus any inputs which we provide as test vectors should also be "settled down" on the active clock edge.

In general, we provide a clock signal: clk with code such as:

        reg clk;

        initial begin
           clk = 0;
           forever #50 clk = ~clk;
           end

which means that there will be a rising clock edge at 50, 150, 250. Thus we can change our input signals on multiples of 100:
        initial begin
           a = 0;
           #100 a = 1;
           #800 a = 0;
           #100 a = 1;

           #200 $finish;
           end

Note: the delays are accumulative rather than absolute - this means that in the above example the changes occur at time:
time incr a =
0 #0 0
100 #100 1
900 #800 0
1000 #100 1
1200 #200 STOP

By the same token, we can set up a $display command to catch the outputs of our circuit at some time after the active clock edge (giving enough time for the signals to all settle down. Thus:

        initial #40 forever #100 $display("output = %d", out);

NOTE: because there is a delay (possibly of zero time, but a delay never the less) between a trigger event and an output change, you cannot sample the output of a gate at the same time as its change occurs and expect to see the new value. Thus
        always @(posedge clk)
             out = d;

        always @(posedge clk)
             $display("output = %d", out);

will (may?) not give you the new value of out. Instead you should delay the event by some time. In general, (when we have non-zero gate delays for our gates) this sampling will need to be after all the signals and gates have settled down - thus in the example above we delay the sampling by #40 units. In cases where we are dealing with only #0 delays, then a pause of #1 (or indeed of an explicit #0 ) is sufficient. An alternative to the forever loop above might thus be:
        always @(posedge clk)
             #1 $display("output = %d", out);

Putting these ideas together gives us a test module such as:

        module testsync;
           reg [3:0] a,b;
           reg         clk;

           initial begin
              clk = 0;
              forever #50 clk = ~clk;
           end

           initial begin
              a = 0;
              #100 a = 1;
              #800 a = 2;
              #100 a = 3;
              #200 $finish;
           end

           initial
              #40 forever
              #100 $display($time, " input = %d output = %d", a, b);

           always @(posedge clk) b = a;

        endmodule // testsync