Rich data¶
This clockless example is inferred directly from its SystemVerilog source. It shows how generated types handle a 137-bit port, arbitrary slices, Q2.14 fixed point, unpacked and multidimensional arrays, and nested packed struct/enum fields.
make cpp-dpi-rich-data-run
make cpp-dpi-rich-data-sv-run
Wide values and slices¶
Bits<N> stores arbitrary packed widths in least-significant-word-first
order. Slices are explicit and retain their width in the C++ type:
auto wide = Bits<137>::from_words(
{0xc001'd00du, 0xdead'beefu, 0x89ab'cdefu, 0x0123'4567u,
0x0000'01abu});
wide.set_slice<16>(48, Bits<16>::from_uint(0xbeef));
dut.wide_i.set(wide);
co_await Delay{1_ps};
const auto slice = dut.wide_o.get().slice<16>(48);
test.expect_eq("arbitrary output slice", slice,
expected_wide.slice<16>(48));
Fixed point¶
The fixed-point helper separates representation, rounding, and overflow policy. DUT ports still carry the raw packed value:
using Q2_14 = Fixed<16, 2, Signedness::Signed>;
const auto a = Q2_14::from_raw(0x6000);
const auto b = Q2_14::from_raw(0x2000);
dut.fixed_a_i.set(a.raw().to_uint());
dut.fixed_b_i.set(b.raw().to_uint());
const auto expected = quantize<Q2_14>(
mul_full(a, b), Round::NearestEven, Overflow::Saturate);
co_await Delay{1_ps};
test.expect_eq("Q2.14 multiply", dut.fixed_y_o.get(),
expected.raw().to_uint());
Arrays and generated views¶
Each declared unpacked dimension contributes an [index] level, preserving
the source bounds including descending and negative ranges:
dut.scalar_array_i[2].set(0x1020'3042u);
dut.matrix_i[2][-1].set(
Bits<65>::from_words({0x1020'3043u, 0x5060'7083u, 1u}));
Packed structs and enums receive generated value and view types:
auto packet = PacketTValue::from_signal_value(0);
packet.set_opcode(Bits<3>::from_uint(5)).set_mode(ModeT::ModeRun);
packet.view().inner()
.set_tag(Bits<2>::from_uint(1))
.set_payload(Bits<3>::from_uint(2));
dut.packet_i.set(packet.signal_value());
co_await Delay{1_ps};
const auto actual = PacketTValue::from_signal_value(dut.packet_o.get());
test.expect("signed enum", actual.mode().is(ModeT::ModeRun));
test.expect_eq("nested payload", actual.inner().payload().to_uint(), 7u);
The test batches all independent writes before settling once. Its C++ and
pure-SV forms each perform 17 checks and advance no clock cycles. Dedicated
authoring benchmarks (wide_echo_137, wide_slice, fixed_mac,
array_multidim, and packed_view) provide longer apples-to-apples
performance measurements for these same constructs.