Clocking blocks causes multiple driving for signal

Hello everyone!

I’ve statred using clocking blocks in my designs, and they cause lots of gotcha’s =)
in particular, this one confuses me a lot:


interface some_if (input clk);
  logic signal;

  clocking cb (@posedge clk)
    output signal;
  endclocking
  
  modport driver (cb);

endinterface

Even if i have no driver in my agent for this interface, this clocking block causes an error (variable is driven via procedural & continious assignments, or smthng like this), because module, that i test has a driver for this signal. I actually understand, WHY it happens (implicit procedural assignment happens on that signal by the output clockvar associated with that signal), but i have no good idea, how to overcome this issue.
i’ve seen papers like this one

in internet, but it looks very monstrously, and also, it makes impossible (as i can see it), using interface inside DUT itself (in article above DUT uses ADDR and READY not via interface, but as raw signals).
Also there’s this one paper
https://verificationacademy.com/resources/technical-papers/the-missing-link-the-testbench-to-dut-connection
and it looks pretty good, except for the last paragraph: clocking block is defined there just as in my example, and it seems to me, that it will end up with the same error (please, correct me, if I’m wrong).
So what’s the solution? Is there any? Should I use CB’s at all, if they cause so many problems?
Thank you for reading!

In reply to trogers:
Declare signal as a
wire
.

so simple=)
Thank you, Dave!

ok, so it’s not really that simple) declaring signal as a “wire” symply forbids any procedural assignments, so it’s not legal to drive signal in any “always” blocks. So, as i see it, if I use clocking block in my testbench, i have to edit my DUT, if it drives signals to the interface with “always” statement, to “assign” driving, right?
Sorry for being so hair-splitting, but it just doesn’t seem to be a right thing, that i might need to change DUT in order to write testbench.

In reply to trogers:

It looks like you want to be able to use the interface in both a master mode, where ‘signal’ is an output from ‘some_if’, and in a slave/monitor mode where the DUT is driving ‘signal’ into ‘some_if’.

In this case, you need some additional control logic to configure ‘some_if’:


interface some_if (input clk);
  wire signal;
 
  bit signal_b;
  bit mode=1'b0;  // 1 = Master; 0 = Slave

  assign signal = (mode==1'b1) ? signal_b : 1'bz;

  clocking cb (@posedge clk)
    output signal;
  endclocking
 
  modport driver (cb);
 
endinterface

From your driver, change ‘mode’ as appropriate, and set ‘signal_b’ as the output value.

In reply to cgales:

you mean, that i should write from DUT using “signal_b” and “mode = 1”, and from TB using “signal” and “mode = 0”? that actually might work, i guess, although two signals will be required to interact with this signal from DUT-master(signal_b) and DUT-slave(signal). Doesn’t look perfect too.

It seems to me, no matter what trick or technique i use, using interfaces in testbench ends up with PITA. I’ve read about using abstract classes to access signals from TB, can you tell me, if they have clocking blocks positive sides, such as determined time of sampling and driving signals?
I din’t find much information about it in whitepaper, mentioned above (The Missing Link).

sorry for double posting, but i’m still looking for solution of this problem=) Clocking blocks and abstract classes look like very popular techniques for driving signals, but somehow there’s almost no infomation about their behaviour and gothas at the junction with DUT.
So what is a painless way to drive signals? I’m sure, that every verification engineer found a way to avoid side-effects like imaginary multiple driving. Please, share with me=)