Default clocking block

I am new to SystemVerilog (coming from a VHDL background). I am likely doing something dumb, but I have not been able to solve it by looking stuff up online. When I try to compile the following code, the compiler complains that I need a “default clocking block” in order to use my “##2” statement. But as you can see, I do have a clocking block. I wonder if this error is related to the fact that I am using a task, and the task is somehow separate from the rest of the code? Otherwise I am stumped. I tried setting the “default input…output” clause to values other than zero ns (I don’t think I want to model skew in this case), but that didn’t help. Can someone point out what I am doing wrong?

Thanks.

// begin code
   initial begin
      fd = start_server();
      if (fd < 0)
	$finish(1);
      fork
	 wait_for_data();
      join_none
   end

   clocking cb @(posedge clk);
      default input #0ns output #0ns;
      input do_read, read_buf, read_count;
      output do_write, write_buf, write_count;
   endclocking

   task wait_for_data();
      forever begin
	 do_write = 0;
	 write_count = recv_data(fd, BUFLEN, write_buf);
	 if (write_count < 0)
	   $finish(1);// error message should be displayed from the C code
	 do_write = 1;
	 ##2;// wait for two clock cycles
      end
   endtask // wait_for_data
// end code

You want to write the clocking block as

default clocking cb @(posedge clk);
      input do_read, read_buf, read_count;
      output do_write, write_buf, write_count;
endclocking

There can only be one default clocking block in any scope, and the ##n syntax can only appear in the same scope as the default clocking block. It is more general to write repeat(2) @cb; and then you can have multiple clocking blocks with different clocks.

There are a lot of other problems wit the rest of the code shown.

  • Don’t use a fork/join_none unless you really need it. Why do you think you need it in this code?
  • If you want to interact with the clocking block signals, you need to prefix any signal with cb.signalname.
    If you want to write to a clocking block output, the only allowed syntax is
cb.*signalname* <= *expression*;

You can’t use a blocking assignment ‘=’, or use the output off a task or function.

  • You should have the ##2 or @cb at the beginning of the loop. Then any reads or writes of clocking block signals will be synchronized to the clocking block edge.

Also see
https://forum.verificationacademy.com/forum/verification-methodology-discussion-forum/systemverilog-and-other-languages-forum/31265-assigning-interface-net-type-signa

Ben Cohen SystemVerilog.us

In reply to dave_59:

Thanks for your response.

So can I use the “repeat(2) @cb” inside the task?

I believe I do need the fork/join_none statement because I am using the DPI interface to call some C code. That is why I am using SystemVerilog to begin with. The C code I am calling will block execution until some asynchronous external event takes place, so I think I need that code running in a separate process/task/thread (?) so that the initialization thread is not blocked.

I was trying to assert the “do_write” signal high for two clock cycles when the asynchronous event takes place so that my VHDL state machine will be guaranteed to see that event and respond appropriately. So I don’t think I really need any synchronous logic in this whole design. I think everything could be combinational, provided I can find some way to assert the “do_write” signal for the right amount of time and then deassert it. Note that this code will be run in different designs with different clock frequencies, so I would rather not make any specific assumptions about what the clock frequency is.

I wish there was some kind of “wait until” statement in SystemVerilog like there is in VHDL, but if there is, I can’t seem to find it. I wonder if I might be better off doing this design in FLI instead of using SystemVerilog at all.

I can post some more code if it would be helpful to explain the problem better.

In reply to fpga_user:

Yes, you can use the repeat (2) @cb inside the task.

You do not need the fork/join_none. An initial block can consume time, and each initial block is an independent concurrent thread.

Here are some VHDL ↔ SystemVerilog equivalents

**VHDL** **SystemVerilog**
WAIT UNTIL A = '1'; wait(A==1);
WAIT FOR 100 ns; #100ns
WAIT ON A; @A
WAIT ON A,B; @(A, B) // or
@(A or B)
WAIT UNTIL A='1' FOR 100 ns; fork
wait(A==1);
#100ns;
join_any