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

  • RTL Foundations36
  • Combinational Design25
  • Sequential Design27
  • FSM & Control27
  • Timing & Clocking39
  • Reset & CDC34
  • Memory & Buses57
  • Advanced RTL28
    • Parameterization and Generate Hub
    • Top-Level Integration Hub
    • Synthesis Intent Hub
    • Style and Static Checks Hub
    • Assertions in RTL Hub
    • Formal Verification Basics Hub
    • Reusable SystemVerilog for RTL
      • Packages and Compile Order
      • Packed Structs and Unions
      • Parameter Types and Guards
      • Functions and Tasks
      • Interfaces, Modports, and Portability
      • Compile-Order and Package Errors, Decoded
      • Mini Project: a Reusable FIFO IP
  • Senior & Interview76
  • Glossary and Notation
Loading lesson

‹Reusable SystemVerilog for RTL
  1. Topics
  2. RTL Design
  3. Advanced RTL
  4. Reusable SystemVerilog for RTL
  5. Packed Structs and Unions

Part 8 · Advanced · Advanced

Packed Structs and Unions

Bundle related wires into an exact packed layout and convert safely at external protocol boundaries.

Why this matters

A packed struct puts its first field in the top bits, which is not the byte order an external bus transmits. Assuming they match gives a layout that works in simulation and fails at the interface.

After this lesson you can: compute a packed struct's width and field positions, decode a value by slice, and publish the layout with a conversion function at an IP boundary.

design.sv
1
2
3
4
5
6
7
8
9
10
11
typedef struct packed {
  logic        valid;
  logic [3:0]  opcode;
  logic [7:0]  tag;
  logic [31:0] data;
} request_t;

request_t req;
logic [$bits(request_t)-1:0] req_bits;
assign req_bits = req;                 // exact packed representation
assign req.data = payload_i;

A packed request_t can travel through storage, ports and debug views. $bits(request_t) gives a stable width; an explicit conversion function can own external byte-order mapping. This fragment demonstrates layout and field access, not a fully driven request producer.

The width is 1 + 4 + 8 + 32 = 45 bits. The first declared field occupies the most significant bits: valid[44], opcode[43:40], tag[39:32], data[31:0]. These positions describe a packed value; they do not select which byte an external bus transmits first.

request_t as one 45-bit vector: valid at bit 44, opcode 43 to 40, tag 39 to 32, data 31 to 0, with the packed worked value and a decoded value

Read the strip left to right, from bit 44 down to bit 0. Each hex digit of the worked values lines up with its field.

  • Valid is a single bit, so it takes the top bit alone: the leading 1 of 1A3C12345678.

  • Opcode and tag are whole hex digits (4 and 8 bits), so A and 3C read straight off the value.

  • Data fills the bottom 32 bits: the last eight hex digits.

design.sv
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// snippet: equal-width views, using request_t above
typedef union packed {
  request_t fields;
  logic [44:0] bits;
} request_view_t;

function automatic logic [44:0] worked_request();
  request_view_t view;
  view.fields.valid  = 1'b1;
  view.fields.opcode = 4'hA;
  view.fields.tag    = 8'h3C;
  view.fields.data   = 32'h12345678;
  return view.bits; // {1'b1, 4'hA, 8'h3C, 32'h12345678}
endfunction // returns 45'h1A3C12345678
Check yourself Predict the packed value for valid=1, opcode=A, tag=3C, data=12345678. Then decode 45'h05E7DEADBEEF.

The first value is 45'h1A3C12345678. The second decodes to valid=0, opcode=5, tag=E7, data=DEADBEEF. Slice [44], [43:40], [39:32] and [31:0]; no external byte-order assumption is needed.

Type

Synthesizable use

Caution

Packed struct

Named fields over one contiguous vector

Field order defines bit positions

Packed union

Alternate views of identical bits

No run-time tag; designer owns interpretation

Unpacked struct/array

Grouped storage or arrays

Port/tool support varies; layout is not a flat bus

Enum

Named encoded state/opcode

Specify base width and illegal-value behavior

At IP boundaries, publish the bit layout and use conversion functions if external standards define endianness independently from the struct declaration.

Check yourself Does a packed union remember which member was last written?

No. All members are views of the same bits; RTL must carry an explicit tag if interpretation changes dynamically.

Remember

  • The first declared field takes the most significant bits: 1 + 4 + 8 + 32 = 45 bits for request_t.

  • Packed positions describe the value; they do not set which byte goes on a bus first.

  • Union members are views of the same bits, so carry an explicit tag if the interpretation changes.

Rule of thumb: publish the bit layout, and convert explicitly where an external standard defines byte order.

Keep going

  • Practice: Packed versus unpacked structs in RTL datapaths

  • Code lab: Packed struct beat

  • Interview: run a mock interview on this topic

Related topics

  • Parameter types and guards

Previous

268 / 350

Next: Parameter Types and Guards

Up next

Up nextParameter Types and Guards

Sign in to save progress

Practice this lessonReport an error

See also

  • Functions and Tasks
  • Interfaces, Modports, and Portability
  • Compile-Order and Package Errors, Decoded