Skip to main content

Reusable FPGA simulation and hardware-in-the-loop verification tools

Project description

fpga-verification

Reusable FPGA verification helpers. The package provides shared data formats, wire-protocol codecs, cocotb simulation utilities, and an Intel System Console HIL transport.

Install

Install the base package for format conversion and protocol codecs:

python -m pip install fpga-verification

Install cocotb simulation helpers:

python -m pip install "fpga-verification[sim]"

Install Intel System Console HIL helpers:

python -m pip install "fpga-verification[hil]"

Install everything:

python -m pip install "fpga-verification[all]"

Public API

from fpga_verification.formats import QFormat, UIntFormat, qformat
from fpga_verification.protocols.avalon_st.intel_video import (
    VIPControlPacket,
    VIPFrame,
    VIPInterlacing,
    VIPPacketType,
    VIPUserPacket,
    VIPVideoPacket,
    vip_packet_from_symbols,
)
from fpga_verification.sim.buses import (
    AvalonSTBeat,
    AvalonSTBus,
    AvalonSTFrame,
    AvalonSTMonitor,
    AvalonSTSink,
    AvalonSTSource,
)
from fpga_verification.sim.bfms.intel_dma import (
    DMAAddressRegion,
    IntelDMABFM,
    IntelDMACommandMonitor,
    SparseByteMemory,
)
from fpga_verification.sim.platform_designer import platform_test_cocotb
from fpga_verification.sim.runners import intel_component_test_cocotb, rtl_test_cocotb
from fpga_verification.hil.intel import IntelSystemConsoleSession

Numeric Formats

UIntFormat and QFormat convert between Python/numpy values and raw integer words used by hardware buses, memories, and scoreboards.

UIntFormat

from fpga_verification.formats import UIntFormat

pixel = UIntFormat(width=10)
raw_pixels = pixel.array([0, 1023, 1024, -1])

assert raw_pixels.tolist() == [0, 1023, 0, 1023]
assert raw_pixels.dtype == pixel.dtype

Inputs:

  • width: unsigned word width in bits, from 1 to 64.
  • zeros(shape): creates a zero-filled numpy array.
  • wrap(values): masks values to the configured width.
  • array(values, shape=None): masks, casts to the smallest unsigned storage dtype, and optionally reshapes.

Outputs:

  • dtype: numpy unsigned dtype selected from uint8, uint16, uint32, or uint64.
  • mask: integer bit mask for the configured width.

QFormat

from fpga_verification.formats import QFormat

sample = QFormat(qi=3, qf=2, signed=True)
raw = sample.float_to_qraw([1.25, -1.0])
back = sample.qraw_to_float(raw)

assert raw.tolist() == [5, 28]
assert back.tolist() == [1.25, -1.0]

Inputs:

  • qi: integer width. For signed formats, this includes the sign bit.
  • qf: fractional width.
  • signed: True for two's-complement signed values, False for unsigned.
  • float_to_qraw(x, saturate=True): converts floats to raw fixed-point words.
  • int_to_qraw(raw, saturate=True): converts signed integer values to raw stored words.
  • qraw_to_int(raw): converts raw words to signed or unsigned integers.
  • qraw_to_float(raw): converts raw words to floating-point values.
  • multiply(left_raw, right_format, right_raw, out_qf=None): multiplies two raw fixed-point arrays. If out_qf is provided, the result is shifted to the requested fractional width.
  • zeros(size=None), ones(size=None), full(size, value, raw=False), and randomize(...): create test data.

Outputs:

  • width: total raw word width, qi + qf.
  • scale: 2 ** qf.
  • mask: integer bit mask for the raw word.
  • min_float, max_float: representable numeric range.
  • dtype: numpy unsigned storage dtype for the raw word.

Avalon-ST Protocols

Avalon-ST helpers follow the Avalon interface terminology used by Intel/Altera. The protocol reference is: https://docs.altera.com/r/docs/683091/22.3/avalon-interface-specifications/introduction-to-the-avalon-interface-specifications

The protocol codec layer is independent of cocotb and simulator state. It accepts and returns Python lists of symbols.

Intel Avalon-ST Video Packets

from fpga_verification.protocols.avalon_st.intel_video import (
    VIPControlPacket,
    VIPFrame,
    VIPInterlacing,
    VIPUserPacket,
    vip_packet_from_symbols,
)

control = VIPControlPacket(
    width=1920,
    height=1080,
    interlacing=VIPInterlacing.PROGRESSIVE_FRAME,
)
symbols = control.to_symbols()
decoded = vip_packet_from_symbols(symbols)

assert decoded.width == 1920
assert decoded.height == 1080

frame = VIPFrame(
    width=2,
    height=2,
    pixels=[0x10, 0x20, 0x30, 0x40],
    user_packets=[VIPUserPacket(1, [0xA, 0xB])],
)
packets = frame.packets()

Inputs:

  • VIPControlPacket(width, height, interlacing=...): frame dimensions and interlacing metadata. Width and height must fit in 16 bits.
  • VIPVideoPacket(payload): video payload symbols.
  • VIPUserPacket(user_type, payload): user packet type 1..8 and payload symbols.
  • VIPFrame(width, height, pixels, interlacing=..., user_packets=...): a black-box container that produces user, control, and video packets.
  • vip_packet_from_symbols(symbols, symbols_per_beat=1): decodes one packet from raw symbols. symbols_per_beat controls how many symbols belong to the first Avalon-ST beat; payload starts after that first beat.

Outputs:

  • to_symbols(): returns a list of 4-bit packet symbols.
  • VIPFrame.control_packet(): returns a VIPControlPacket.
  • VIPFrame.video_packet(): returns a VIPVideoPacket.
  • VIPFrame.packets(): returns user packets followed by control and video packets.
  • VIPInterlacing.description: human-readable interlacing mode.

Ancillary packets are currently reported as unsupported by the decoder.

Avalon-ST Cocotb Bus Helpers

The cocotb bus helpers drive and observe Avalon-ST interfaces through cocotb handles. They support scalar valid/ready, optional packet signals, optional empty, error, and channel, and ready modes ready_latency=0 or ready_latency=1.

Instantiating A Source And Sink

import cocotb
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge

from fpga_verification.sim.buses import (
    AvalonSTBus,
    AvalonSTFrame,
    AvalonSTSink,
    AvalonSTSource,
)


@cocotb.test()
async def stream_loopback_test(dut):
    cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())

    dut.reset.value = 1
    await RisingEdge(dut.clk)
    dut.reset.value = 0

    source = AvalonSTSource(
        AvalonSTBus.from_prefix(dut, "sink"),
        dut.clk,
        reset=dut.reset,
        data_bits_per_symbol=8,
        packets=True,
    )
    sink = AvalonSTSink(
        AvalonSTBus.from_prefix(dut, "source"),
        dut.clk,
        reset=dut.reset,
        data_bits_per_symbol=8,
        packets=True,
    )

    await source.send(AvalonSTFrame([0x11, 0x22, 0x33]))
    received = await sink.recv()

    assert received.data == [0x11, 0x22, 0x33]

Inputs:

  • AvalonSTBus.from_prefix(dut, prefix): binds signals named like <prefix>_data, <prefix>_valid, <prefix>_ready, <prefix>_startofpacket, and <prefix>_endofpacket.
  • AvalonSTFrame(data, channel=None, error=None, empty=None, tx_complete=None): frame payload and optional sideband metadata.
  • AvalonSTSource(bus, clock, reset=None, data_bits_per_symbol=8, symbols_per_beat=None, first_symbol_in_high_order_bits=False, ready_latency=0, ready_allowance=None, packets=None, idle_value="x").
  • AvalonSTSink(...) and AvalonSTMonitor(...): use the same bus format options as AvalonSTSource.
  • send(frame) / send_nowait(frame): queue transmit data.
  • recv() / recv_nowait(): receive complete frames.
  • recv_beat() / recv_beat_nowait(): receive one transferred beat.
  • set_pause_generator(generator): apply backpressure or idle insertion from an iterable of booleans.

Outputs:

  • AvalonSTFrame.data: list of symbols.
  • AvalonSTFrame.channel, error, empty: captured sideband metadata.
  • AvalonSTFrame.sim_time_start, sim_time_end: simulation timestamps.
  • AvalonSTBeat: one handshake beat with data, decoded symbols, sop, eop, empty, error, channel, and sim_time.
  • wait(): waits for a source to become idle or a monitor/sink to see activity, depending on the helper type.

Intel DMA BFM

IntelDMABFM is a cocotb black-box model for Intel read and write DMA streaming interfaces. It consumes DMA command descriptors, emits DMA responses, sources read data from memory, and stores write data into memory.

import cocotb
from cocotb.clock import Clock
from cocotb.triggers import RisingEdge

from fpga_verification.sim.buses import AvalonSTBus
from fpga_verification.sim.bfms.intel_dma import (
    DMAAddressRegion,
    IntelDMABFM,
    IntelDMACommandMonitor,
    SparseByteMemory,
)


@cocotb.test()
async def dma_component_test(dut):
    cocotb.start_soon(Clock(dut.clk, 10, units="ns").start())

    memory = SparseByteMemory()
    memory.write(0x1000, b"\x01\x02\x03\x04")

    dma = IntelDMABFM(
        dut,
        clock=dut.clk,
        reset=dut.reset,
        memory=memory,
        mode="full",
    ).start()

    command_monitor = IntelDMACommandMonitor(
        clock=dut.clk,
        reset=dut.reset,
        rdma_cmd_bus=AvalonSTBus.from_prefix(dut, "rdma_cmd"),
        wdma_cmd_bus=AvalonSTBus.from_prefix(dut, "wdma_cmd"),
        read_address_regions=[DMAAddressRegion("input", 0x1000, 0x4000)],
        write_address_regions=[DMAAddressRegion("output", 0x8000, 0x4000)],
    ).start()

    dut.reset.value = 1
    await RisingEdge(dut.clk)
    dut.reset.value = 0

    # Drive the DUT here. The BFM responds on the DMA Avalon-ST interfaces.
    # Later, inspect memory or descriptor logs as black-box outputs.
    written_bytes = memory.read(0x8000, 16)
    read_descriptors = command_monitor.read_descriptors

    dma.stop()
    command_monitor.stop()

Inputs:

  • IntelDMABFM(dut, clock, reset, memory=None, read_response_delay_cycles=2, write_response_delay_cycles=2, ..., mode="full").
  • mode: "full", "read"/"read_only", or "write"/"write_only".
  • memory: optional SparseByteMemory shared by read and write paths.
  • Optional bus overrides: rdma_cmd_bus, rdma_resp_bus, wdma_cmd_bus, wdma_resp_bus, din_bus, and dout_bus. If omitted, buses are discovered from DUT prefixes with the same names.
  • SparseByteMemory.write(address, data): initializes byte-addressed memory.
  • DMAAddressRegion(name, start, size): allowed address interval for passive checking. End address is exclusive.
  • IntelDMACommandMonitor(...): pass command buses or existing AvalonSTMonitor instances and optional allowed address regions.

Outputs:

  • SparseByteMemory.read(address, length): returns bytes stored by the BFM.
  • IntelDMABFM.read_commands, write_commands: descriptor queues observed by the model.
  • IntelDMABFM.read_responses, write_responses: queues of descriptors whose responses were issued.
  • IntelDMACommandMonitor.read_descriptors, write_descriptors: decoded descriptor history.
  • ReadDMADescriptor.decode(value) and WriteDMADescriptor.decode(value): convert raw descriptor words into address, length, and control fields.

HIL Session

IntelSystemConsoleSession opens one persistent system-console process and uses it sequentially for Avalon-MM memory access and JTAG UART commands. Intel Quartus system-console must be available on PATH.

import numpy as np

from fpga_verification.hil.intel import IntelSystemConsoleSession

frame = np.arange(1024 * 1280, dtype=np.uint16).reshape(1024, 1280)

with IntelSystemConsoleSession(
    system_console="system-console",
    master_index=0,
    uart_index=0,
    startup_timeout=30.0,
    work_dir=".",
) as hw:
    hw.write_memory(frame, address=0x01E84800)
    response = hw.command("g\n", timeout=3.0)
    frame_out = hw.read_memory((1024, 1280), address=0x02DC6C00)

print(response)
print(frame_out.shape)

Inputs:

  • system_console: executable name or path.
  • master_index: System Console Avalon-MM master index.
  • uart_index: JTAG UART service index.
  • startup_timeout: seconds to wait for the Tcl worker to become ready.
  • work_dir: directory used for temporary binary transfer files.
  • write_memory(data, address, chunk_size=4096): writes numpy-compatible data as little-endian 16-bit words.
  • read_memory(shape, address, chunk_size=4096): reads little-endian 16-bit words and reshapes them.
  • command(command, timeout=3.0, debug=False): sends a UTF-8 command over JTAG UART and waits for the first non-empty response line.

Outputs:

  • read_memory(...): numpy array with the requested shape.
  • command(...): response string.
  • Methods raise TimeoutError or RuntimeError if System Console stops or reports a protocol error.

Simulation Runners

The simulation helpers cover three levels of generated and non-generated designs:

rtl_runner
  RTL sources -> cocotb build/test

intel_component_runner
  *_hw.tcl -> ip-generate -> generated composition HDL + original RTL -> rtl_runner

platform_runner
  already generated Platform Designer sim dir/msim_setup.tcl -> simulator flow

rtl_test_cocotb is the direct RTL path. Pass it explicit HDL sources or source directories, and it delegates build/test to the selected cocotb simulator runner.

intel_component_test_cocotb is for Platform Designer component .tcl files. It generates only the HDL needed for simulation, keeps composition HDL that has no source equivalent, replaces generated copies of project RTL with exact matches from source_dirs, and then calls rtl_test_cocotb.

Its generated-catalog flow is:

source_dirs
  -> ip-make-ipx --thorough-descent --source-directory=<source_dirs>
  -> components.ipx in generated temp dir
  -> ip-generate --search-path=<components.ipx>,$
  -> parse .spd
  -> replace generated RTL copies with original source files
  -> rtl_test_cocotb

Pass generate_only=True to retain and return the generated composition directory without running simulation.

platform_test_cocotb is for already generated Platform Designer simulation trees. The expected layout is:

project_root/
  <hdl_toplevel>/
    <hdl_toplevel>/
      testbench/
        mentor/
          msim_setup.tcl

For Questa, the platform runner compiles through msim_setup.tcl and runs cocotb against the generated simulator libraries. For Verilator, it reads Verilog/SystemVerilog sources from msim_setup.tcl and builds them directly.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

fpga_verification-0.2.1-cp314-cp314-win_amd64.whl (662.7 kB view details)

Uploaded CPython 3.14Windows x86-64

fpga_verification-0.2.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (4.7 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

fpga_verification-0.2.1-cp313-cp313-win_amd64.whl (648.2 kB view details)

Uploaded CPython 3.13Windows x86-64

fpga_verification-0.2.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (4.8 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

fpga_verification-0.2.1-cp312-cp312-win_amd64.whl (651.8 kB view details)

Uploaded CPython 3.12Windows x86-64

fpga_verification-0.2.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (4.8 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

fpga_verification-0.2.1-cp311-cp311-win_amd64.whl (659.7 kB view details)

Uploaded CPython 3.11Windows x86-64

fpga_verification-0.2.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (4.7 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

fpga_verification-0.2.1-cp310-cp310-win_amd64.whl (660.9 kB view details)

Uploaded CPython 3.10Windows x86-64

fpga_verification-0.2.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (4.5 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64manylinux: glibc 2.28+ x86-64

File details

Details for the file fpga_verification-0.2.1-cp314-cp314-win_amd64.whl.

File metadata

File hashes

Hashes for fpga_verification-0.2.1-cp314-cp314-win_amd64.whl
Algorithm Hash digest
SHA256 317943699cbd3c3d52699f75ed833dcba318ed3fe686c2277b86cebca8983087
MD5 7991d8a65d4edc56725c79ef309fcc7c
BLAKE2b-256 769b5643c78b0c3f113b70e4106e93f1d968281d65ca551b99034fae1c255a22

See more details on using hashes here.

File details

Details for the file fpga_verification-0.2.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for fpga_verification-0.2.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 30b0550b8395da98023b5d33d390d3071d53c0451a8ae72d439c5606de5ce29e
MD5 7dcff2fc5ceb12656ffc049c785a19d3
BLAKE2b-256 70e534c87c226f5fe5c2bdcd020bf58d6383a78654d6f7f03db71543ecb20d50

See more details on using hashes here.

File details

Details for the file fpga_verification-0.2.1-cp313-cp313-win_amd64.whl.

File metadata

File hashes

Hashes for fpga_verification-0.2.1-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 89828af453e4fb888f9aef6690b7e48eda33771e3aa189b996614d6c537d762d
MD5 33ce3f7b1e999825b5b0bbd9964fd3af
BLAKE2b-256 184db5f964d4f3dff8e20d9f58f2e33eeb2e45a19b2e1b982543668b43d5fc5d

See more details on using hashes here.

File details

Details for the file fpga_verification-0.2.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for fpga_verification-0.2.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 30d6153720bd80138aef8fb41973e26c522671ee083e018c2d9efecf407d856a
MD5 79da62bbf1f90442b5e8ccc74d432cd4
BLAKE2b-256 1d1d5c58b5c8ebaf77e9c14760c17e8f85f012d429416d3e600ce98842824110

See more details on using hashes here.

File details

Details for the file fpga_verification-0.2.1-cp312-cp312-win_amd64.whl.

File metadata

File hashes

Hashes for fpga_verification-0.2.1-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 46b20dc21fdbe63fe8e42e051c4269c0ce9a988d611dfdba7ae30f62ddb5c247
MD5 ed5ab93a811705f7d7b21a02400629f8
BLAKE2b-256 fc5e91087cfc4e2a2365c7259815360109132140534bccb683ca82e76ca7a931

See more details on using hashes here.

File details

Details for the file fpga_verification-0.2.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for fpga_verification-0.2.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 bc8459428587175f8233d87b31496480943e1c9617f37bc6a170b9fdec8fc73b
MD5 f8e097dc668a60df4914faf5f3ecb348
BLAKE2b-256 b6184659d98da233725c92b8ef623412cc11b655e13b68527b3a90d92f055876

See more details on using hashes here.

File details

Details for the file fpga_verification-0.2.1-cp311-cp311-win_amd64.whl.

File metadata

File hashes

Hashes for fpga_verification-0.2.1-cp311-cp311-win_amd64.whl
Algorithm Hash digest
SHA256 702e707fa96eafee6f288c7929d4150dbc6f47e55c22ad9a77aefc129ea6e9de
MD5 3d098780fab9c708d7f4e3811062a2ca
BLAKE2b-256 bb7ae68855b86685c5317a208b5c4bdae804560daf00963ae146a07b9c14d6cc

See more details on using hashes here.

File details

Details for the file fpga_verification-0.2.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for fpga_verification-0.2.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 34719f2d0a9bfd4572b5d90a4f03b90f40c2f4baa868177b35e25df5707c7830
MD5 69ae4a8614b6c7fa9bc5442097264143
BLAKE2b-256 dbeb40861cfd010f622e2ea806027506acddb57fd5c999f010b240399172d09c

See more details on using hashes here.

File details

Details for the file fpga_verification-0.2.1-cp310-cp310-win_amd64.whl.

File metadata

File hashes

Hashes for fpga_verification-0.2.1-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 c27afafe13d8af241ec8a37f677b2207fccc69509ec8ec45b3d0df52da0393ca
MD5 227dcb475822c38f71ea02969903d7b3
BLAKE2b-256 f2d439f7ebaf9e49ed28675864e9b47ee4ee7504e15bdc62ad09f8911d077439

See more details on using hashes here.

File details

Details for the file fpga_verification-0.2.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for fpga_verification-0.2.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 dd06b30d80d52ab4b554237f7bd269c977697697f99c23c8d74d6921e10632bf
MD5 48e22631d5f8bb152cbfb250defa9ecb
BLAKE2b-256 34136bc38b673d3b9766ff0324830b7bc9555e3a9d530d891dcd92d91c460fd8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page