Skip to content

vpi: extended VCD ($dumpports) writer + native structural VPI (process/primitive/driver/contassign)#1389

Open
wm2015email wants to merge 2 commits into
steveicarus:masterfrom
wm2015email:feature/evcd
Open

vpi: extended VCD ($dumpports) writer + native structural VPI (process/primitive/driver/contassign)#1389
wm2015email wants to merge 2 commits into
steveicarus:masterfrom
wm2015email:feature/evcd

Conversation

@wm2015email

Copy link
Copy Markdown

Summary

Two related additions to Icarus's VPI, on top of one another:

  1. Extended VCD ($dumpports) writer — a system.vpi task family
    ($dumpports, $dumpportsoff/on/all/flush/limit) that writes an IEEE
    1364-2005 Clause 18 extended VCD recording a module instance's ports with
    direction and drive strength. Output is byte-compatible with the GHDL
    --evcd writer, so one reader works across Verilog and VHDL.

  2. Native structural-connectivity VPI — the IEEE 1364 object model that
    Icarus previously left unimplemented, so structural consumers (dataflow /
    waveform / analysis tools) get real data instead of empty fallbacks. Each
    object is sourced faithfully from the compiler and threaded
    ivl → tgt-vvp → .vvp directive → assembler → runtime __vpi* → scope
    iterator → vpi_user.h:

    • vpiProcess (always/initial/final): type, location, scope.
    • vpiPrimitive / vpiPrimTerm (gate/switch/UDP): vpiPrimType,
      vpiSize, terminals with vpiDirection/vpiTermIndex.
    • vpiDriver: per-driver value (vpiStrengthVal) and scope, read from
      the tri resolver (tagged at elaboration via .resolv_drv).
    • vpiContAssign (+vpiLhs/vpiRhs): continuous assignments preserved
      through the frontend (NetScopedll_target::end_design → new
      ivl_scope_cassigns/ivl_cassign_* API → .contassign
      __vpiContAssign). vpiLhs/size/location always faithful; vpiRhs is the
      r-value net for simple assigns, null for an expression r-value.

Building on vpiDriver, the $dumpports writer now separates an inout's
module-side drive from its external drive and emits the full IEEE 1364-2005
§18.4.3 conflict-state characters (0/1/?/F, the A/a/B/b/C/c conflicts, and
d/u/l/h by drive strength) rather than collapsing a genuine bus conflict to
?.

Constants

New object/relationship/property codes are added to vpi_user.h using the
standard IEEE values where they are free in Icarus's numbering, and
Icarus-local values (continuing the 13x block) where the standard value
collides with an existing Icarus code (noted inline).

Tests

ivtest regressions: evcd_basic, evcd_bus, evcd_onoff, and evcd_inout
(the inout conflict states) all pass. The full Verilog regression
(regress-vlg.list) stays clean — the remaining failures are pre-existing and
unrelated.

Notes / follow-ups

  • vpiLoad and full expression objects for vpiRhs are left as follow-ups.
  • A single-driver inout (no resolver) reports the resolved-value EVCD char
    rather than the directional H/U; inouts driven from both sides (the
    normal testbench case) get the full set.

🤖 Generated with Claude Code

wm2015email and others added 2 commits June 19, 2026 15:40
Implement the IEEE 1364-2005 Clause 18 extended VCD task family
($dumpports / $dumpportsall / $dumpportsoff / $dumpportson /
$dumpportsflush / $dumpportslimit), which were previously registered as
"not implemented" stubs.

The new vpi/sys_evcd.c reaches each port's value-bearing net through the
port's vpiLowConn relationship and reads per-bit drive strength with
vpiStrengthVal, emitting $var port declarations and p-records (per-bit
state character plus strength0/strength1 components). Scalar and vector
ports are supported; the initial checkpoint is deferred to the read-only
synch so it records settled values. The encoding is byte-compatible with
the GHDL --evcd writer so one waveform reader works across Verilog and
VHDL.

To resolve a port to its net, the VPI is completed: vpiLowConn and
vpiHighConn are now implemented on vpiPortInfo. tgt-vvp emits the port's
low (formal) and high (actual) net symbols in the .port_info directive,
the vvp assembler parses them (compile_port_info), and vpiPortInfo
resolves each to the __vpiSignal sharing that net. New vpi_user.h
constants vpiHighConn (135) and vpiLowConn (136) use iverilog's private
numbering to avoid colliding with vpiArgument/vpiBit.

Tests: ivtest evcd_basic (scalar in/out/inout), evcd_bus (vector ports)
and evcd_onoff ($dumpportsoff/on/all), each gold-diffed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implement the IEEE 1364 structural VPI objects that Icarus previously left
unimplemented, so a single-source consumer (e.g. a dataflow/waveform viewer)
gets real data instead of empty fallbacks. Each object is sourced faithfully
from the compiler and threaded ivl -> tgt-vvp -> .vvp directive -> assembler
-> runtime __vpi* -> scope iterator -> vpi_user.h constants.

* vpiProcess (always/initial/final): .process directive from ivl_design_process;
  object reports vpiType/vpiLineNo/vpiFile/vpiScope.

* vpiPrimitive / vpiPrimTerm (gate/switch/UDP): .primitive directive from
  ivl_scope_logs; reports vpiPrimType, vpiSize (#inputs), name, location, and
  iterates terminals (vpiDirection, vpiTermIndex).

* vpiDriver: the tri resolver already retains each driver's value+strength;
  tgt-vvp tags each input with its driver scope (.resolv_drv), and
  vpi_iterate(vpiDriver, net) exposes per-driver value (vpiStrengthVal) and
  scope. signal->node->fun is the resolver, so no extra linkage is needed.

* vpiContAssign (+vpiLhs/vpiRhs): continuous assignments are preserved through
  the frontend (NetScope records each lval/rval/location/drive in
  PGAssign::elaborate; dll_target::end_design builds ivl_cont_assign_s, exposed
  via the new ivl_scope_cassigns/ivl_cassign_* API); tgt-vvp emits .contassign
  and the runtime materialises __vpiContAssign. vpiLhs/size/location are always
  faithful; vpiRhs resolves to the r-value net for simple assigns and is null
  for an expression r-value (anonymous synthesised temp).

* EVCD ($dumpports) inout conflict states: sys_evcd.c walks vpiDriver on an
  inout's net, separates module-side from external drives by scope, and maps
  the pair to the full IEEE 1364-2005 18.4.3 state characters (0/1/?/F, the
  A/a/B/b/C/c conflicts, and d/u/l/h by drive strength) instead of collapsing
  a genuine bus conflict to '?'. New ivtest evcd_inout covers it.

Full Verilog regression (regress-vlg.list) stays clean: the only failures are
pre-existing (gold-less Expected-Fail tests, a VHDL test, a test needing
-gno-io-range-error, and a pre-existing .port_info bug).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@caryr

caryr commented Jun 20, 2026

Copy link
Copy Markdown
Collaborator

How odd. I was reading the extended dump part of the standard earlier wondering if it was ever going to be needed in Icarus.

Regarding the duplicate value in vpi_user.h there should only be a value collision if they are used as part of the same property. If we have inadvertently coded something with the wrong value that should be fixed otherwise we should be following what is given in the standard. This is true even if the values are the same.

@caryr

caryr commented Jun 20, 2026

Copy link
Copy Markdown
Collaborator

Some test failures to look at and to get windows to compile you will need to add the new routines to the appropriate .def file. One of the oddities of windows versus Unix based systems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants