1

I have a Verilog testbench for a FIFO and I think using tasks to perform push and pop would be more effective than what I am doing now. Currently, I use a sequence of repeat (ticks) @ (negedge clk) statements to push, pop or push and pop in a long initial begin/end block.

This makes the code ugly and difficult to read. I would rather have a task, push_task(ticks), that I can call. Something like this -

initial begin
    task_push(5);
    task_pop(2);
    task_push(10);
    ...
end

The problem is, if I write my push_task like this -

task push_task;
    input [31:0] ticks;
    repeat (ticks) begin
            push <= 1'b1;
            push_data <= $random % (2**DATA_WIDTH);
            @ (negedge clk);
            push <= 1'b0;
        end
    end
endtask

and I have a similar pop_task, then I can either push or pop but not both at the same time. How do I get both to either execute at the same time or sequentially depending on what I want to do?

One obvious way is to create a third task that does both. Another way is to have a singe task with three inputs, ticks, push and pop and have push and pop be asserted based on whether the inputs to the task are asserted or not.

Does anyone have another (better?) way of doing this? I can find plenty of information online about what the syntax of tasks is and how to write them. Not much about what the correct way of using them is.

1 Answer 1

1

Use fork-join for parallel operations, begin-end for sequential. Nesting is allowed.

Ex:

initial begin
  task_push(10);
  fork // parallel operations
    task_push(4);
    task_pop(7);
  join
  // back to sequential
  fork // another parallel operation
    task_push(6);
    begin // sequential within parallel
      task_pop(2);
      @(negedge clk); // no pop for one cycle
      task_pop(3);
    end
  join
end

FYI I will suggest changing your tasks to be more like the following. The header is more conventional; a IEEE1364-2001 & above and IEEE1800 style instead of the legacy IEEE1364-1995 style. Moving the assignments to push outside the loop will save some CPU cycles in simulation. Your CPU time is probably nothing, but something to consider when scaling up to significantly larger project.

task push_task(input [31:0] ticks);
begin
  push <= 1'b1;
  repeat (ticks) begin
    push_data <= $random % (2**DATA_WIDTH);
    @(negedge clk);
  end
  push <= 1'b0;
end
endtask

Sign up to request clarification or add additional context in comments.

5 Comments

This worked. Thank you. If I may, with regards to your "pro tip" about saving CPU cycles, is it more correct to have a begin before the push that was moved out of the repeat loop? That works for me as expected. Without it, I get errors (using iverilog as the simulator).
Having the begin before any statement was required in IEEE 1363-1995 (non-ANSI style). It became optional with ANSI style (input/output defined in parentheses). In IEEE 1800-2012 it the leading begin is fully operational. I'm not sure when it became fully optional; I'd have to look it up. iverilog should accept no begin before push in v0.9; in v0.8 you may need to compile with -g2 according to iverilog.wikia.com(
I reviewed all the LRMs and played with the code on EDAplayground. Apparently all version of IEEE1364 (Verilog) require begin-end for multiple statements in tasks or functions. It is inferred for all versions of IEEE1800 (SystemVerilog), where I do most of my development. The latest iverilog has partial SystemVerilog support with the -g2009 or -g2012 compile flag.
That makes sense. Thanks a lot for the example. May I ask what the "Nothing stem" comment on line 42 refers to?
It was either an abandoned thought I forgot to delete or a typo for "nothing step" as in no pop for one cycle. I updated to make it more clear.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.