← Design Verification Interviews

Interview Question · Code Reading

A Packet Passed as a String

L1 JuniorL2 Mid-Leveldpistrings

The Question

The interviewer asks

A colleague passes packet bytes to C as a string. What does this print?

systemverilog
import "DPI-C" function int c_length(input string s);   // C: return strlen(s);
string payload = "";
byte unsigned bytes[3] = '{8'h41, 8'h00, 8'h42};
initial begin
  foreach (bytes[i]) payload = {payload, string'(bytes[i])};
  $display("%0d", c_length(payload));
end
Reveal the Model Answer

It prints 2, not 3. A SystemVerilog string cannot hold a zero byte, so the 0x00 is dropped as the string is built. Even if it got through, C's strlen stops at the first zero. A string crosses as a borrowed const char* and is for text, such as names and file paths. Send binary data as a byte array with a count.

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Junior)

Predicts 2 and says strings are for text, with binary data in a byte array.

Cross-Questions

Where the interviewer goes next. Open a node to practise that follow-up.

WhyWhat IfBreak ItCompare

  1. A Packet Passed as a String
  2. WhyWhy a String Has No Zero ByteL1 Junior

    Why can a SystemVerilog string not hold a zero byte?

    Reveal the Model Answer

    The standard says so: a string never contains the 0 character, and writing 0 into one character is ignored. That keeps it compatible with C strings, which end at the first zero.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "s[1] = 8'h00; cuts the string short at index 1."

    The write is ignored instead. Our probe kept "AB" with length 2.

    Source ASIC Point engine notes (Verilator 5.052 DPI probe, 2026-11-08: s = "AB"; s[1] = 8'h00 left s as AB, length 2)

  3. Break ItA C Model That Kept a String PointerL2 Mid-Level

    A C model stores the const char* it receives for a test name and prints it at the end of the test. Sometimes it prints garbage. Why?

    Reveal the Model Answer

    A string argument reaches C as a pointer to memory the simulator owns, valid only while the call runs. After the call returns, the simulator may reuse or free that memory, so the stored pointer can point at anything. Copy the text into storage the C side owns during the call.

    The Life of a String Across the CallArrows show where the pointer points and what C does with it. The text lives in the simulator's buffer; the copy in kept is the only thing that survives the call.
    The life of a string across the callArrows show where the pointer points and what C does with it. The text lives in the simulator's buffer; the copy in `kept` is the only thing that survives the call. SystemVerilog string cfg "AXI" in thesimulator's ownbuffer pointer const char* name points at that bufferwhile the call lasts C protocol_code strcmp during thecall, returns 2 model storage kept[32] remember_name copiesthe bytes; the modelkeeps this SystemVerilog cfg = "changed…" after the call thesimulator reuses itsbuffer passesread nowcopied nowstale pointer
    1. Pass

      The testbench calls protocol_code(cfg). C gets a pointer to the bytes A X I and a terminating zero, nothing more.

      Follow the calls, values and waits
      1. data AXI: string cfg → const char* name
    2. Read during the call

      strcmp reads the bytes and the function returns 2. The pointer has done its job.

      Follow the calls, values and waits
      1. data AXI: const char* name → protocol_code
      2. return 2: protocol_code → const char* name
    3. Copy what you must keep

      remember_name copies the bytes into kept, storage the model owns. After this the model never touches name again.

      Follow the calls, values and waits
      1. data AXI: const char* name → kept[32]
    4. The call returns

      The testbench assigns a new value to cfg. A kept name pointer would now read whatever the simulator put there; kept still says AXI.

    Read the diagram as text
    • string cfg: "AXI" in the simulator's own buffer
    • const char* name: points at that buffer while the call lasts
    • protocol_code: strcmp during the call, returns 2
    • kept[32]: remember_name copies the bytes; the model keeps this
    • cfg = "changed…": after the call the simulator reuses its buffer
    • string cfg → const char* name: passes
    • const char* name → protocol_code: read now
    • const char* name → kept[32]: copied now
    • cfg = "changed…" → const char* name: stale pointer

    Borrow during the call, copy to keep. A pointer that outlives the call points at memory the simulator may have changed, moved or freed.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "The string lives as long as the SystemVerilog variable does."

    The pointer refers to the simulator's copy for that call, not to the variable, so a later change or reuse invalidates it.

    Source IEEE 1800-2017 Annex H.7.2 (argument passing: ownership of string storage)

  4. What IfCopying an Object That Holds a ChandleL2 Mid-Level

    Asked if the C object sits in a transaction class

    What if a model class holds a chandle, and someone copies the object with new this or a copy() method?

    Reveal the Model Answer

    The copy gets the same address, so both objects drive one C model. An update through either is seen by both. If the copy needs its own state, call a C clone function that allocates a new struct. Then decide which object deletes which model.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "The copied object now has its own C model."

    A shallow copy copies field values, and a chandle's value is an address. The twin adds 100 through the copy and the original shows 105.

    Run It

    A model object with a C accumulator, copied after adding 5.

    RUNVerilator + C/C++ over DPI · simulator service
    Edit the code, then run it
    Expected: a=105 b=105 FAIL: the copy shares one C model, because copying a chandle copies only the address
    See the Fix
    module tb;
      import "DPI-C" function chandle acc_new();
      import "DPI-C" function chandle acc_clone(input chandle h);
      import "DPI-C" function int acc_add(input chandle h, input int v);
      class model;
        chandle h;
        function new(); h = acc_new(); endfunction
        function model copy();
          model c = new this;              // a shallow copy of every field
          c.h = acc_clone(h);              // the fix: the copy gets its own C state
          return c;
        endfunction
      endclass
      model a = new, b;
      initial begin
        void'(acc_add(a.h, 5));
        b = a.copy();
        void'(acc_add(b.h, 100));
        $display("a=%0d b=%0d", acc_add(a.h, 0), acc_add(b.h, 0));
        if (acc_add(a.h, 0) != 5) $display("FAIL: the copy shares one C model, because copying a chandle copies only the address");
        else $display("PASS: each object owns its own C state");
        $finish;
      end
    endmodule
    

    Prints: a=5 b=105 PASS: each object owns its own C state

  5. CompareWhy a Chandle and Not an IntegerL2 Mid-Level

    Why store the C pointer in a chandle rather than in a longint?

    Reveal the Model Answer

    A chandle always has the size of a pointer on the machine that runs the simulation. The language only lets you assign it, compare it with null and pass it to C. That stops SystemVerilog code from doing arithmetic on it or inventing values. A longint would work by accident on a 64-bit host and offer none of those guarantees.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "You can read the struct fields through the chandle in SystemVerilog."

    It is opaque: only C code can dereference it, through accessor functions you import.

    Source IEEE 1800-2017 §6.14

What People Get Wrong

Common wrong answers and why they fail. Run It shows the wrong belief failing on a real simulator.

Misconception

"A string carries any bytes, and C finds the length with strlen."

The zero byte is lost before the call. The twin shows SystemVerilog length 2 for three appended bytes.

Run It

Three bytes, one of them zero, appended to a string and measured in C.

RUNVerilator + C/C++ over DPI · simulator service
Edit the code, then run it
Expected: SystemVerilog length=2, C length=2 FAIL: the zero byte never reached C; a string cannot carry binary data
See the Fix
module tb;
  import "DPI-C" function int c_count(input byte unsigned d[], input int n);
  byte unsigned bytes[3] = '{8'h41, 8'h00, 8'h42};
  string payload = "";
  int got;
  initial begin
    // The fix: binary data travels as a byte array with a count.
    got = c_count(bytes, 3);
    $display("C received %0d bytes", got);
    if (got != 3) $display("FAIL: the zero byte never reached C; a string cannot carry binary data");
    else $display("PASS: all three bytes reached C, zero included");
    $finish;
  end
endmodule

Prints: C received 3 bytes PASS: all three bytes reached C, zero included

Misconception

"C should free the const char* when it is done with it."

The simulator owns that memory. Freeing it corrupts the simulator's heap, often with a crash much later.

Source IEEE 1800-2017 §35.5.1.4 (memory management)

Half-Truth

"A string and a chandle are both pointers, so either can hold a C object."

A string is text that C only borrows during the call. A chandle is the only type that carries a C pointer SystemVerilog may keep.

Learn It, Then Prove It