Skip to practice questions

Part 3 · Special types and conversion

Enums, strings, casts, and streaming

Preserve meaning across enums, strings, tagged values, casts, byte streams, and representation-changing boundaries.

Reading path
3 of 3
Concept chapters
2
Practice links
5

Question-first preparation

Practice the mechanisms on this page.

Each question maps directly to one of the chapters below, so you can test the contract before reading the explanation.

05 · Semantic scalar types

Encode legal state spaces instead of memorizing raw numbers.

Enums, strings, time, real-valued types, and forward declarations improve intent when their conversion and tool-boundary rules are made explicit.

Rule to say firstKeep states typed, cast raw values deliberately, validate membership, and convert special simulation types at explicit model boundaries.

Typed state transition

Raw bits enter through a validation gate

  1. 01Raw encodinglogic [1:0]

    Protocol or register representation.

  2. 02Explicit caststate_t'(raw)

    Makes the representation change visible.

  3. 03Membership checkinside {IDLE, BUSY, DONE}

    Rejects unnamed encodings.

  4. 04Named behaviorstate.name()

    Readable control and diagnostics.

Raw encodinglogic [1:0]

Protocol or register representation.

Enum default
Base-type default
It is not automatically the first declared label.
byte
Signed by default
8'hFF interpreted as byte is -1 unless declared unsigned.
real
Not packed
It cannot be a member of a packed union or packed struct.

A type name improves readability only when illegal raw encodings remain observable and checked.

01

Observe

Identify values that form a closed named set, text that is testbench-only, time units that can be rounded, and types that require forward references.

02

Decide

Use enums for legal states, strings for dynamic text, time/realtime for temporal models, and typedef-forward declarations to break declaration-order dependencies.

03

Failure

An unchecked cast can create an unnamed enum value, byte signedness can turn 255 into -1, and time precision can silently round a delay.

04

Prove

Use inside or name-based checks, specify timeunit/timeprecision, test boundary characters, and keep real/string use outside synthesizable bit-level interfaces.

Enums are constraints with names

An enum defines a base integral type and a set of labels. Automatic progression, explicit encodings, .first, .last, .next, .prev, .num, and .name improve models, but a cast can still create a base value without a matching label.

Validate after crossing a raw boundarysystemverilog
typedef enum logic [1:0] {
  IDLE = 2'b00,
  BUSY = 2'b01,
  DONE = 2'b10
} state_t;

logic [1:0] raw_state;
state_t state;

always_comb begin
  state = state_t'(raw_state);
  assert (state inside {IDLE, BUSY, DONE})
    else $error("illegal state %b", raw_state);
end

Strings and special types belong at deliberate boundaries

A string is a dynamic sequence of bytes with methods for length, comparison, conversion, case, and formatting. time is a 64-bit unsigned four-state integral type; realtime is real-valued. shortreal and real model floating values but are not packed hardware fields.

  • Specify timeunit and timeprecision in each reusable scope so literal delays have a stable meaning.
  • Use $sformatf for constructed diagnostics and explicit ato*/itoa-style conversions when text crosses into numbers.
  • Use chandle for opaque foreign handles and void for intentionally discarded expression results.
  • Forward typedefs allow mutually referential class declarations; parameterized classes still need compatible specialization.
Special-type boundary checks
TypeStrengthBoundary risk
enumNamed legal statesCast can create unnamed value
stringDynamic text and methodsEncoding and synthesis support
timeIntegral simulation timeUnit and precision rounding
realtime / realFractional modelsNot a packed bit representation
chandleOpaque DPI handleLifetime and ownership live outside SV
voidDiscarded result / no return valueA task itself has no return type

Events and forward declarations are handle-level promises

An event variable is a handle to a synchronization object. Assigning one event variable to another aliases that object, so a trigger through either handle wakes waiters on the shared event. A forward class typedef similarly introduces only a class type name: it permits handle declarations before the full class, but not construction, member access, or assumptions about an incomplete definition.

  • Event assignment does not copy a pulse or create history; both handles refer to the same event object from that point forward.
  • A transient -> trigger can still be missed by a waiter that starts later; use a persistent protocol when delivery must survive timing.
  • A forward declaration must be resolved by a compatible full declaration in the same scope, including matching parameterization where applicable.
  • Before the full class definition, use the incomplete type only where a class handle type is sufficient.
Alias one event; forward-declare one classsystemverilog
typedef class packet_node;

event data_ready;
event ready_alias = data_ready;
packet_node pending; // a handle is legal before the full definition

initial fork
  begin @data_ready;   $display("shared event observed"); end
  begin #1 -> ready_alias; end
join

class packet_node;
  packet_node next;
  int payload;
endclass

Explain it out loud

Interview reasoning checkpoints

Strong answer

A raw integral value generally requires an explicit cast, and the cast does not guarantee the value matches a named label. Validate membership after untrusted inputs.

Reason it through
  1. The enum base type defines representation.
  2. The label set defines intended legal values.
  3. A cast bridges representation but does not prove legality.
Interviewer lens

Mention both type safety and the possibility of unnamed encodings.

Common trap

Assuming the enum variable always contains one of its declared labels.

Strong answer

No. Its default comes from the enum's base type: X for a four-state base or zero for a two-state base. That value may or may not correspond to the first label.

Reason it through
  1. Initialization follows the representation type.
  2. Declaration order does not override the base-type default.
  3. Reset or initialize state explicitly and assert legal state.
Interviewer lens

This tests whether the candidate separates enum naming from storage semantics.

Common trap

Relying on the first declared label as implicit reset state.

06 · Conversion and bit-stream layout

Make every representation crossing directional and testable.

Casting, assignment patterns, concatenation, part-selects, and streaming operators answer different questions. Choosing the right one prevents a visually plausible but bit-reversed adapter.

Rule to say firstUse casts for type interpretation, patterns for aggregate construction, and streaming for ordered serialization; document left alignment, slice size, signedness, and truncation.

Bit-stream adapter

Shape → order → width → interpretation

  1. 01Source shapebyte data[4]

    Unpacked elements have a declared traversal order.

  2. 02Stream order{>>8{data}}

    Right-stream in eight-bit slices.

  3. 03Target width40 bits

    A 32-bit stream occupies the left; eight zero bits fill the right.

  4. 04Typed viewpacket_t'(...)

    Cast only after layout is established.

Source shapebyte data[4]

Unpacked elements have a declared traversal order.

Indexed select
base +: width
Width is constant; index direction is explicit.
Assignment pattern
'{default: ...}
Constructs aggregate members, not a raw concatenation.
Streaming fill
Left aligned
If target is wider, remaining right-hand bits are zero.

Treat serialization as a named interface with its own assertions instead of scattering clever casts through the design.

01

Observe

Record source width, target width, packed order, element order, slice size, and whether the operation should reorder or only reinterpret.

02

Decide

Use indexed part-selects for fixed-width windows, assignment patterns for named/default fields, and streaming for general pack or unpack operations.

03

Failure

A wider streaming target is left-aligned and zero-filled on the right; assuming right alignment produces a valid-looking but wrong packet.

04

Prove

Round-trip non-palindromic data, assert $bits, compare each field, and test width mismatch, signed extension, and X/Z propagation.

Casts change type; they do not invent a protocol

A sized cast establishes width, a signed or unsigned cast changes arithmetic interpretation, and a type cast applies a destination type. None of those choices by itself specifies the byte order of an unpacked array or validates an enum encoding.

Build, stream, and round-trip explicitlysystemverilog
typedef struct packed {
  logic [7:0] kind;
  logic [7:0] len;
  logic [15:0] payload;
} packet_t;

byte unsigned bytes [0:3] = '{8'hA5, 8'h3C, 8'h1B, 8'h7E};
byte unsigned roundtrip [0:3];
logic [31:0] word;
logic [39:0] wide;
packet_t packet;

initial begin
  word   = {>>8{bytes}};
  wide   = {>>8{bytes}}; // 32-bit stream at [39:8], right-filled with 8'h00
  packet = packet_t'(word);
  {>>8{roundtrip}} = word;
  assert (roundtrip == bytes);
end

Part-selects state direction without variable width

base +: width selects upward from base; base -: width selects downward. The base may vary at run time, but width must be constant. This is safer than manually recomputing two range endpoints.

Representation tools
ToolPrimary jobQuestion to ask
type'(expr)Interpret as destination typeAre width and legality valid?
N'(expr)Resize to N bitsIs extension signed or unsigned?
'{field: value}Construct aggregateAre all members assigned?
{a, b}Concatenate packed valuesWhat is leftmost significance?
base +: W / -: WFixed-width windowWhich index direction?
{>>slice{expr}}Serialize / deserializeWhat are order and fill?

DPI representation needs an adapter contract

Two-state open arrays may map efficiently to native C element storage; four-state vectors require aval/bval-style representation so X/Z is not lost. ABI layout, element direction, ownership, and simulator lifetime must be defined rather than inferred from a C cast.

  • Use DPI helper APIs for open arrays instead of assuming contiguous native layout.
  • Do not pass pointers to temporary simulator storage beyond the call's valid lifetime.
  • Keep conversion at one boundary module and test it from both languages.
  • Include X and Z samples for four-state data mapping tests.

Explain it out loud

Interview reasoning checkpoints

Strong answer

The stream is placed at the left side of the target and unused bits on the right are zero-filled. Do not assume ordinary right-aligned numeric assignment.

Reason it through
  1. Compute the stream width independently from target width.
  2. Apply the streaming concatenation's alignment rule.
  3. Assert exact target slices with a non-symmetric sample.
Interviewer lens

A strong response states the alignment and fill direction, not only that padding occurs.

Common trap

Treating streaming as if it were an ordinary unsigned integer cast.

Strong answer

base +: width selects a constant-width range toward increasing indices; base -: width selects toward decreasing indices. The base can vary, but the width must be constant.

Reason it through
  1. Start with the declared index direction.
  2. Choose the desired base index and direction.
  3. Validate the first and last legal base values.
Interviewer lens

Look for the constant-width requirement and a correct directional example.

Common trap

Writing :+ or :-, or assuming +: always means more-significant bits.