October 15, 2013
The key difference between a sequential UDP and a combinatorial one (the ones that act like look-up tables) is the use of the reg keyword in the output declaration.
Last time I spent some time talking about simple User Defined Primitives (UDP). The synthesis tool I use won’t actually create FPGA gates for UDPs, but it still useful for creating test benches or other simulation use. The simple UDPs were little more than look-up tables. But it is possible to model more complex sequential logic using UDPs.
The key difference between a sequential UDP and a combinatorial one (the ones that act like look-up tables) is the use of the reg keyword in the output declaration:
primitive latch(q, clk, d) ; output q; reg q ; input clk, d;
This cues the simulator that the state table will have an extra entry that represents the current state of the output (q, in this case). Without the reg keyword, the lookup table uses the inputs as a “key” to find the output. With the reg keyword, the “key” is all the inputs plus the current state of the output.
You can force the start up state of the output using the initial keyword (just like you would for any reg variable). Here’s a simple latch:
primitive latch(q, clk, d) ; output q; reg q ; input clk, d; table // clk d q q' 0 1 :?: 1 ; 0 0 :?: 0 ; 1 ? : ? : - ; endtable endprimitive
Note the value after the first colon on each row of the state table is the current state of q. Then there is another colon and the new output value (indicated in the comments as q’). The hyphen in the last row of the state table means “no change” so when the clock is high, the output doesn’t change no matter what the input values are.
You can also elect to make one input per row (but only one input) edge sensitive by indicating a transition in parenthesis instead of a single value. For example, the following row matches a clock transition from high to low:
(10) 1 : ? : 1
To make life easier, you can use “r” instead of (01) and “f” instead of (10). If you want to include transitions to and from x and z states, you can use p and n. Keep in mind that in a Verilog simulation, “x” is a valid state (unknown, basically). Any case not matched in the state table will put the output in the “x” state. With that in mind, you may want a table entry that uses the “*” transition, which matches all transitions.
Here’s a simple SR flip flop using the shortcut notation:
primitive srff (q,s,r); output q; reg q; input s,r; reg q; initial q = 1'b1; // initial state of q table // s r q q' 1 0:?:1; f 0:1:-; 0 r:?:0; 0 f:0:-; 1 1:?:0; endtable endprimitive
Note that while any one row of the table can only have one input that is edge sensitive, it is allowed to have edge sensitivity on different inputs on different rows.
You might wonder about a practical use for these primitives. After all, you expect to find latches and flip flops already available in your environment. However, you might want to model a majority gate, for example, or take special actions when a clock signal goes to “x” in order to get a simulation going.
Have you used UDPs in your Verilog simulations (or will you)? Leave a comment and share your thoughts.