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

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here6
  • SystemVerilog Language Foundations28
    • SystemVerilog Data Types
    • Arrays: Packed, Dynamic, Queues & Associative
      • Packed vs Unpacked Arrays
      • Dynamic Arrays and Queues
      • Associative Arrays
      • Array Methods and Selection
    • Procedural Code & Scheduling
    • Operators, Expressions & Width Rules
    • Packages, Typedefs & Compilation
    • Interfaces, Modports & Clocking Blocks
  • Object-Oriented SystemVerilog for Verification30
  • Constraint Randomization — Interview-Critical Deep Dive36
  • SystemVerilog Assertions — Interview-Critical Deep Dive40
  • Functional Coverage in SystemVerilog22
  • Testbench Architecture in SystemVerilog23
  • Advanced Language & Integration25
  • Senior & Interview Preparation21
  • Principal-Level Verification28
Loading lesson

‹Arrays: Packed, Dynamic, Queues & Associative
  1. Topics
  2. SystemVerilog
  3. SystemVerilog Language Foundations
  4. Arrays: Packed, Dynamic, Queues & Associative
  5. Array Methods and Selection

Part 1 · Language Foundations · Intermediate

Array Methods and Selection

Reductions, finders, sorting, plus how to pick the right container.

On this page

    Why this matters

    A payload checksum came out as 0x80 on a packet whose bytes add to 640. The sum method works at the element width, and the result had wrapped long before it reached the 32-bit variable.

    After this lesson you can: use the reduction, locator and ordering methods correctly, guard their results, and choose the right container for a job.

    Reductions and the width trap

    Reduction methods fold an array to one value: sum(), product(), and(), or() and xor(). The result width is the element width.

    Summing a byte array wraps at 8 bits. Widen each element first with a with clause, writing q.sum() with (int'(item)) for a byte unsigned array, where item names the current element. A plain byte is signed, so int'(item) sign-extends it: write int'(unsigned'(item)) there or F0 counts as -16.

    Expression over {8'hF0, 8'hA0, 8'hF0}

    Result

    payload.sum()

    8'h80: accumulation wraps at 8 bits

    payload.sum() with (int'(item))

    640: full 32-bit total

    payload.sum() with (int'(item > 8'h80))

    3: loop-free count of matching elements

    min(), max() and unique() differ from reductions: they return queues. Capture the queue, check size(), then use element [0]. The order of unique() results is unspecified, so sort them before checking positions.

    Finders that return queues

    Locator methods scan against a with expression and always return a queue. An empty queue represents no match, so no sentinel value is ever needed.

    find returns matching elements and find_index returns their positions. find_first and find_last stop at one match but still return a queue of zero or one.

    Every locator answers with a queue, and an empty queue is the miss.

    Ordering, then picking the container

    sort(), rsort() and reverse() mutate the array in place and return nothing. Arrays of objects (the OOP part) need a with key such as sort(t) with (t.addr), since objects have no natural order.

    Three questions pick the container: is the size known at compile time, does the collection grow and shrink during the run, and is the index space dense or sparse.

    Fixed

    Dynamic

    Queue

    Associative

    Size

    Compile-time constant

    new[n] at runtime

    Grows per push

    Grows per written key

    Access

    O(1)

    O(1)

    O(1) at ends, O(n) middle

    Lookup per key, slower than indexed

    Memory

    Full footprint always

    Exactly n elements

    Small per-element overhead

    Per-entry overhead, only touched keys

    Use

    RTL memories, tables

    Payloads sized from config

    FIFOs, scoreboards, logs

    Sparse memories, ID lookup

    • Dynamic grown per iteration with `new[]`: quadratic in practice, so that workload is a queue.

    • Dense 0..N-1 keys in an associative array: pure hash overhead, so that workload is dynamic.

    • Per-response `find_first` by ID over a queue: linear each time, so that lookup is associative.

    • A giant fixed array of which 1% is touched: megabytes wasted, so that model is associative.

    Common mistakes

    Common pitfall

    Reading byte_q.sum() into an int does not recover wrapped bits. Accumulation already wrapped at 8 bits before the assignment. Widen inside the with clause instead.

    Interview

    Likely question: “why do locators return queues instead of elements?”

    • Zero matches must be representable without a sentinel: an empty queue.

    • Idiom: capture the result, check size(), then use [0].

    • Same rule covers min, max and unique.

    Check yourself Why does the bench call sort() after unique() before checking positions?

    The order of unique() results is unspecified, so a check on positions could pass on one simulator and fail on another. Sorting fixes the order.

    Check yourself find_first matched nothing and the code reads [0] directly. What happens?

    It indexes an empty queue, which is out of bounds. Check size() before touching [0].

    Check yourself Responses arrive out of order and each is found with find_first by ID over a queue. What container should replace it?

    An associative array keyed by ID. Per-response linear search becomes a direct lookup plus delete(id).

    Testbench

    Predict first payload is a byte unsigned queue holding F0, A0, F0. What does payload.sum() return?

    8'h80, not 640. The sum is computed at the element width, 8 bits, so the true total 0x280 wraps to 0x80.

    Wrapped versus widened sums, conditional counts, finders with miss guards, unique, and in-place sorting.

    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    module tb_array_methods;
      byte unsigned payload[$];
      int vals[$];
      int idxs[$];
      int one[$];
      int none[$];
      byte unsigned mymin[$];
      int errors = 0;
    
      initial begin
        payload = {8'hF0, 8'hA0, 8'hF0};
        if (payload.sum() !== 8'h80) errors++;              // 8-bit wrap: F0+A0+F0 = 280 -> 80
        if (payload.sum() with (int'(item)) !== 640) errors++;
        if (payload.sum() with (int'(item > 8'h80)) !== 3) errors++;
    
        mymin = payload.min();                              // min returns a queue
        if (mymin.size() != 1 || mymin[0] !== 8'hA0) errors++;
    
        vals = {4, 1, 4, 9, 1};
        vals = vals.unique();                               // one of each value, order unspecified
        vals.sort();                                        // fix the order -> {1, 4, 9}
        if (vals.size() != 3) errors++;
        if (vals[0] !== 1 || vals[1] !== 4 || vals[2] !== 9) errors++;
    
        idxs = vals.find_index(x) with (x > 2);             // positions {1, 2}
        if (idxs.size() != 2 || idxs[0] !== 1 || idxs[1] !== 2) errors++;
    
        one = vals.find_first(x) with (x == 9);             // queue of 1
        if (one.size() != 1 || one[0] !== 9) errors++;
        none = vals.find_first(x) with (x > 100);           // queue of 0 = miss
        if (none.size() != 0) errors++;
    
        vals.rsort();                                       // in place -> {9, 4, 1}
        if (vals.size() != 3 || vals[0] !== 9 || vals[2] !== 1) errors++;
    
        if (errors != 0) $fatal(1, "FAIL: array methods self-check");
        $display("PASS: widths widened, finders guarded, sorting done in place");
        $finish;
      end
    endmodule
    Terminal
    PASS: widths widened, finders guarded, sorting done in place

    Remember

    • Reductions keep the element width; widen inside with (int'(item)).

    • Locators, min, max and unique return queues; an empty queue is a miss.

    • sort, rsort and reverse change the array in place and return nothing.

    Rule of thumb: widen before you sum, check size() before you index a result.

    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 this bench (it does not support the with clause on array methods).

    Related topics

    • Next topic: Procedural Code & Scheduling

    Previous

    15 / 259

    Next: Procedural Code & Scheduling

    Up next

    Up nextProcedural Code & Scheduling

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Packed vs Unpacked Arrays
    • Dynamic Arrays and Queues