2
\$\begingroup\$

I've been designing digital logic in Verilog (and more recently SystemVerilog) since 1998 but I have never had much use for user-defined primitives (UDPs) as they're generally non-synthesizable. Well, as part of a testbench, I need to instantiate several UDPs, specifically, JK flip-flops with level sensitive asynchronous preset and clear.

Since it's been a while since I did anything with UDPs, I read some reference material and then coded the UDP, which I thought would be quite straightforward. Unfortunately, the UDP doesn't behave correctly in that its output is mostly undefined when it should be zero or one. I read more reference material and adjusted the UDP's table many times with no success. After just about pulling my hair out, I decided to just use the example code in the Verilog LRM. Unfortunately, even this doesn't work! Is there something wrong with the extremely simple testbench? Any insight would be appreciated. The code follows:

primitive jk_edge_ff (q, clock, j, k, preset, clear); //This is taken verbatim from the Verilog LRM 
output q; reg q;
input clock, j, k, preset, clear;
table
// clock jk pc state output/next state
? ?? 01 : ? : 1 ; // preset logic
? ?? *1 : 1 : 1 ;
? ?? 10 : ? : 0 ; // clear logic
? ?? 1* : 0 : 0 ;
r 00 00 : 0 : 1 ; // normal clocking cases
r 00 11 : ? : - ;
r 01 11 : ? : 0 ;
r 10 11 : ? : 1 ;
r 11 11 : 0 : 1 ;
r 11 11 : 1 : 0 ;
f ?? ?? : ? : - ;
b *? ?? : ? : - ; // j and k transition cases
b ?* ?? : ? : - ;
endtable
endprimitive

module tb;
  reg clock, j, k, preset, clear;
  wire q;
  jk_edge_ff jkff0(q, clock, j, k, preset, clear);
  
  initial
  begin
    clock = 1;
    forever #10 clock = ~clock;
  end
  
 initial
 begin
   $monitor("time = %t , q = %b , clock = %b , j = %b , k = %b , preset = %b , clear = %b", $time, q, clock, j, k, preset, clear);
   #20; preset = 0; clear = 0; j = 0; k = 1;
   #20 k = 0;
   #20 preset = 1;
   #20 preset = 0;
   #20 clear = 1;
   #20 clear = 0;
   #20 j = 1;
   #20 j = 0;
   #20 k = 1;
   #20 k = 0;
   #20 j = 1;
   #20 k = 1;
   #200 $finish;
 end
endmodule

In the second initial block, I've tested changing the phase (relative to the clock) of when the inputs to the flip-flop change by adjusting the first delay value in front of "preset = 0;" -- I've tried nothing, #5 , #10 , #15 and #20 with the same results.

Here is the output of the simulator, which in this case is Aldec Riviera Pro 2022.04:

# KERNEL: time =                    0 , q = x , clock = 1 , j = x , k = x , preset = x , clear = x
# KERNEL: time =                   10 , q = x , clock = 0 , j = x , k = x , preset = x , clear = x
# KERNEL: time =                   20 , q = x , clock = 1 , j = 0 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                   30 , q = x , clock = 0 , j = 0 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                   40 , q = x , clock = 1 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                   50 , q = x , clock = 0 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                   60 , q = 0 , clock = 1 , j = 0 , k = 0 , preset = 1 , clear = 0
# KERNEL: time =                   70 , q = 0 , clock = 0 , j = 0 , k = 0 , preset = 1 , clear = 0
# KERNEL: time =                   80 , q = x , clock = 1 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                   90 , q = x , clock = 0 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  100 , q = 1 , clock = 1 , j = 0 , k = 0 , preset = 0 , clear = 1
# KERNEL: time =                  110 , q = 1 , clock = 0 , j = 0 , k = 0 , preset = 0 , clear = 1
# KERNEL: time =                  120 , q = x , clock = 1 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  130 , q = x , clock = 0 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  140 , q = x , clock = 1 , j = 1 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  150 , q = x , clock = 0 , j = 1 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  160 , q = x , clock = 1 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  170 , q = x , clock = 0 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  180 , q = x , clock = 1 , j = 0 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  190 , q = x , clock = 0 , j = 0 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  200 , q = x , clock = 1 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  210 , q = x , clock = 0 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  220 , q = x , clock = 1 , j = 1 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  230 , q = x , clock = 0 , j = 1 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  240 , q = x , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  250 , q = x , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  260 , q = x , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  270 , q = x , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  280 , q = x , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  290 , q = x , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  300 , q = x , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  310 , q = x , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  320 , q = x , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  330 , q = x , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  340 , q = x , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  350 , q = x , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  360 , q = x , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  370 , q = x , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  380 , q = x , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  390 , q = x , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  400 , q = x , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  410 , q = x , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  420 , q = x , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  430 , q = x , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0

Strangely, in the above I notice that preset and clear seem to have their behavior reversed.

The expected output would be:

# KERNEL: time =                    0 , q = x , clock = 1 , j = x , k = x , preset = x , clear = x
# KERNEL: time =                   10 , q = x , clock = 0 , j = x , k = x , preset = x , clear = x
# KERNEL: time =                   20 , q = 0 , clock = 1 , j = 0 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                   30 , q = 0 , clock = 0 , j = 0 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                   40 , q = 0 , clock = 1 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                   50 , q = 0 , clock = 0 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                   60 , q = 1 , clock = 1 , j = 0 , k = 0 , preset = 1 , clear = 0
# KERNEL: time =                   70 , q = 1 , clock = 0 , j = 0 , k = 0 , preset = 1 , clear = 0
# KERNEL: time =                   80 , q = 1 , clock = 1 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                   90 , q = 1 , clock = 0 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  100 , q = 0 , clock = 1 , j = 0 , k = 0 , preset = 0 , clear = 1
# KERNEL: time =                  110 , q = 0 , clock = 0 , j = 0 , k = 0 , preset = 0 , clear = 1
# KERNEL: time =                  120 , q = 0 , clock = 1 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  130 , q = 0 , clock = 0 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  140 , q = 1 , clock = 1 , j = 1 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  150 , q = 1 , clock = 0 , j = 1 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  160 , q = 1 , clock = 1 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  170 , q = 1 , clock = 0 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  180 , q = 0 , clock = 1 , j = 0 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  190 , q = 0 , clock = 0 , j = 0 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  200 , q = 0 , clock = 1 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  210 , q = 0 , clock = 0 , j = 0 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  220 , q = 1 , clock = 1 , j = 1 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  230 , q = 1 , clock = 0 , j = 1 , k = 0 , preset = 0 , clear = 0
# KERNEL: time =                  240 , q = 0 , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  250 , q = 0 , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  260 , q = 1 , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  270 , q = 1 , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  280 , q = 0 , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  290 , q = 0 , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  300 , q = 1 , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  310 , q = 1 , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  320 , q = 0 , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  330 , q = 0 , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  340 , q = 1 , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  350 , q = 1 , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  360 , q = 0 , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  370 , q = 0 , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  380 , q = 1 , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  390 , q = 1 , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  400 , q = 0 , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  410 , q = 0 , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  420 , q = 1 , clock = 1 , j = 1 , k = 1 , preset = 0 , clear = 0
# KERNEL: time =                  430 , q = 1 , clock = 0 , j = 1 , k = 1 , preset = 0 , clear = 0
\$\endgroup\$
0

2 Answers 2

1
\$\begingroup\$

The problem is in the testbench code. You need to invert the sense of preset and clear. Those 2 signals act like active-low controls. For example, when preset is 0, the output goes high. You should start the simulation with both of them high, then pulse them low. Here is the fixed testbench:

module tb;
  reg clock, j, k, preset, clear;
  wire q;
  jk_edge_ff jkff0(q, clock, j, k, preset, clear);
  
  initial
  begin
    clock = 1;
    forever #10 clock = ~clock;
  end
  
 initial
 begin
   #20; preset = 1; clear = 1; j = 0; k = 1;
   #20 k = 0;
   #20 preset = 0;
   #20 preset = 1;
   #20 clear = 0;
   #20 clear = 1;
   #20 j = 1;
   #20 j = 0;
   #20 k = 1;
   #20 k = 0;
   #20 j = 1;
   #20 k = 1;
   #200 $finish;
 end

always @(negedge clock) $display("time = %t , q = %b , j = %b , k = %b , preset = %b , clear = %b", 
    $time, q, j, k, preset, clear);

endmodule

They really should have given those signals different names, like n_preset.

Note: I removed the $monitor statement and replaced it with a $display on every negedge of clock (away from the active clock edge). I find this to be more meaningful output:

time =                   10 , q = x , j = x , k = x , preset = x , clear = x
time =                   30 , q = x , j = 0 , k = 1 , preset = 1 , clear = 1
time =                   50 , q = 0 , j = 0 , k = 0 , preset = 1 , clear = 1
time =                   70 , q = 1 , j = 0 , k = 0 , preset = 0 , clear = 1
time =                   90 , q = 1 , j = 0 , k = 0 , preset = 1 , clear = 1
time =                  110 , q = 0 , j = 0 , k = 0 , preset = 1 , clear = 0
time =                  130 , q = 0 , j = 0 , k = 0 , preset = 1 , clear = 1
time =                  150 , q = 0 , j = 1 , k = 0 , preset = 1 , clear = 1
time =                  170 , q = 1 , j = 0 , k = 0 , preset = 1 , clear = 1
time =                  190 , q = 1 , j = 0 , k = 1 , preset = 1 , clear = 1
time =                  210 , q = 0 , j = 0 , k = 0 , preset = 1 , clear = 1
time =                  230 , q = 0 , j = 1 , k = 0 , preset = 1 , clear = 1
time =                  250 , q = 1 , j = 1 , k = 1 , preset = 1 , clear = 1
time =                  270 , q = 0 , j = 1 , k = 1 , preset = 1 , clear = 1
time =                  290 , q = 1 , j = 1 , k = 1 , preset = 1 , clear = 1
time =                  310 , q = 0 , j = 1 , k = 1 , preset = 1 , clear = 1
time =                  330 , q = 1 , j = 1 , k = 1 , preset = 1 , clear = 1
time =                  350 , q = 0 , j = 1 , k = 1 , preset = 1 , clear = 1
time =                  370 , q = 1 , j = 1 , k = 1 , preset = 1 , clear = 1
time =                  390 , q = 0 , j = 1 , k = 1 , preset = 1 , clear = 1
time =                  410 , q = 1 , j = 1 , k = 1 , preset = 1 , clear = 1
time =                  430 , q = 0 , j = 1 , k = 1 , preset = 1 , clear = 1
\$\endgroup\$
1
  • 1
    \$\begingroup\$ I can't believe I didn't see that preset and clear were active low in the UDP table -- I knew I was overlooking something basic. Thanks! \$\endgroup\$
    – jdb2
    Commented Aug 27, 2023 at 16:13
1
\$\begingroup\$

As @toolic pointed out, I mistakenly interpreted the LRM UDP as having active high level sensitive preset and clear, when they were actually active low.

Here is the corrected UDP ( which I hope doesn't have any mistakes that I overlooked again ) :

primitive jk_edge_ff (q, clock, j, k, preset, clear); //This is taken verbatim from the Verilog LRM, but the polarity of the clear and preset signals in the table have been reversed
output q; reg q;
input clock, j, k, preset, clear;
table
// clock jk pc state output/next state
? ?? 10 : ? : 1 ; // preset logic
? ?? *0 : 1 : 1 ;
? ?? 01 : ? : 0 ; // clear logic
? ?? 0* : 0 : 0 ;
r 00 11 : 0 : 1 ; // normal clocking cases
r 00 00 : ? : - ;
r 01 00 : ? : 0 ;
r 10 00 : ? : 1 ;
r 11 00 : 0 : 1 ;
r 11 00 : 1 : 0 ;
f ?? ?? : ? : - ;
b *? ?? : ? : - ; // j and k transition cases
b ?* ?? : ? : - ;
endtable
endprimitive
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.