Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here5
  • SystemVerilog Language Foundations28
    • SystemVerilog Data Types
    • Arrays: Packed, Dynamic, Queues & Associative
    • Procedural Code & Scheduling
      • always_comb, always_ff, always_latch
      • Blocking vs Nonblocking Assignments
      • Tasks vs Functions
      • The Event Scheduler & Stratified Regions
      • Loops & Flow Control
    • Operators, Expressions & Width Rules
    • Packages, Typedefs & Compilation
    • Interfaces, Modports & Clocking Blocks
  • Object-Oriented SystemVerilog for Verification30
  • Constraint Randomization — Interview-Critical Deep Dive39
  • SystemVerilog Assertions — Interview-Critical Deep Dive41
  • Functional Coverage in SystemVerilog25
  • Testbench Architecture in SystemVerilog24
  • Advanced Language & Integration26
  • Senior & Interview Preparation23
  • Principal-Level Verification34
Loading lesson

←Procedural Code & Scheduling
  1. Topics
  2. SystemVerilog
  3. SystemVerilog Language Foundations
  4. Procedural Code & Scheduling
  5. Tasks vs Functions
On this page

    Part 1 of 9, lesson 12 of 28Part 1 of 9/Language Foundations/Lesson 12 of 28Intermediate

    Tasks vs Functions

    Tasks that consume time versus zero-time functions, automatic storage and argument passing.

    Two parallel calls to one task returned each other's results. The task was static, so both calls shared one set of arguments and locals.

    After this lesson you can: choose a task or a function, make routines automatic where calls can overlap, and pick input, output, inout or ref arguments.

    Time divides tasks from functions

    A function must execute in zero simulation time. No # delays, no @ event controls, no wait, and no task calls, which is what lets functions appear inside expressions.

    A task may consume time and block on events, so bus drivers and monitors are tasks. A void function returns nothing but keeps the zero-time guarantee, which suits pure side effects such as printing or counting.

    Aspect

    Function

    Task

    May consume time

    Never

    Yes: delays, events, wait

    Callable from

    Expressions and other functions

    Procedural code, never from a function

    Typical use

    CRC, address math, void reporters

    Bus drivers, monitors, checkers

    Automatic versus static storage

    Routines in modules and interfaces default to static lifetime: one shared copy of every argument and local. Two concurrent calls overwrite each other's state with no warning of any kind.

    Declaring the routine automatic gives every call a private stack frame. Class methods are always automatic; module and interface routines are the danger zone.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    // snippet: two parallel calls share one frame, so the second call corrupts the first
    task send_pkt(input int id, input int len);
      int beats;
      beats = len;
      repeat (beats) begin
        @(posedge clk);
      end
    endtask

    The fix is one keyword. The body stays identical; only the storage class changes.

    How arguments pass

    Predict first A task counts beats into an output int got while it runs for 6 clocks. The caller reads its variable after 3 clocks. What does it see?

    Its old value. output arguments copy out only when the task returns, so the caller sees 6 at the end and nothing before.

    Inputs copy in at call time, while output and inout copy out at return. The caller sees nothing until the routine returns, so an output never streams live updates.

    A ref argument is a true alias: reads and writes touch the caller variable directly, and large arrays avoid an expensive copy. It requires an automatic routine. const ref keeps the no-copy efficiency with a read-only contract.

    Direction

    Caller sees the value

    Cost for large arrays

    input

    N/A: copies in at call

    One full copy

    output, inout

    Only at return

    One full copy each way for inout

    ref

    Immediately, it is an alias

    No copy

    const ref

    N/A: read-only

    No copy

    Expecting an output argument to update the caller while the task still runs. Copy-out happens at return; live updates need ref.

    “Two forked branches call one module task and both get wrong data.”

    The task is static, so the calls share arguments and locals. Declare it automatic for a private frame per call.

    Check yourself A function needs to wait for a clock edge before returning its result. What is the structural fix?

    It cannot wait: functions run in zero time. Split the work so a task handles the waiting and the function handles the pure computation.

    Check yourself Why does the language allow ref arguments only in automatic routines?

    A ref names the caller's variable for one call. A static routine keeps one shared frame that outlives every call, so it has nowhere safe to hold a per-call alias.

    Testbench

    Concurrent automatic calls proving independent frames, plus a const ref checksum that avoids copying the array. The first bench uses fork ... join, which starts the two calls at the same time and waits for both; the OOP part teaches it properly.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    module tb_auto_task;
      timeunit 1ns; timeprecision 1ps;
      logic clk = 0;
      always #5 clk = ~clk;
      int got0 = -1, got1 = -1;
      int errors = 0;
      task automatic send_pkt(input int id, input int len, output int got);
        int beats;
        beats = len;
        got = 0;
        repeat (beats) begin
          @(posedge clk);
          got = got + 1;
        end
      endtask
      initial begin
        fork
          send_pkt(0, 6, got0);
          send_pkt(1, 2, got1);
        join
        if (got0 !== 6) errors++;
        if (got1 !== 2) errors++;
        if (errors != 0) $fatal(1, "FAIL: automatic task calls show unexpected beat counts");
        $display("PASS: concurrent automatic calls return 6 and 2 beats with no crosstalk");
        $finish;
      end
    endmodule
    Terminal
    PASS: concurrent automatic calls return 6 and 2 beats with no crosstalk
    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    function automatic int unsigned checksum(const ref byte data[]);
      int unsigned total = 0;
      foreach (data[i]) total += int'(data[i]);
      return total;
    endfunction
    
    module tb_checksum;
      byte frame[];
      int unsigned got = 0;
      int errors = 0;
      initial begin
        frame = new[4];
        frame[0] = 8'h01; frame[1] = 8'h02;
        frame[2] = 8'h03; frame[3] = 8'h04;
        got = checksum(frame);
        if (got !== 10) errors++;
        if (errors != 0) $fatal(1, "FAIL: checksum gave an unexpected total");
        $display("PASS: const ref checksum of 01 02 03 04 is 10 with no array copy");
        $finish;
      end
    endmodule
    Terminal
    PASS: const ref checksum of 01 02 03 04 is 10 with no array copy
    • Functions run in zero time and can sit inside expressions; tasks may wait.

    • Routines in modules are static by default; automatic gives each call its own copy.

    • output copies back at return; ref is a live alias and needs an automatic routine.

    Rule of thumb: anything called from parallel code is automatic.

    How this was checked

    Run with Verilator 5.048 (--binary --timing) on 2026-10-18; every bench printed the PASS line shown, with no warnings. Icarus 13 cannot run the const ref checksum bench; the parallel-task bench runs on both.

    Related topics

    • Next: The Event Scheduler

    Previous

    17 / 275

    Next: The Event Scheduler & Stratified Regions

    Up next

    Up nextThe Event Scheduler & Stratified Regions

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • always_comb, always_ff, always_latch
    • Loops & Flow Control