Native C Library API

The PyUCIS Native C Library provides a complete implementation of the UCIS 1.0 C API using SQLite3 as the backend storage. This native library can be used directly from C/C++ applications or through Python’s ctypes interface.

Overview

Features

  • UCIS 1.0 Standard Compliant - Full implementation of the official C API

  • SQLite3 Backend - Persistent, queryable storage with SQL access

  • High Performance - Optimized with prepared statements and caching

  • Thread-Safe - Can be used in multi-threaded applications

  • Cross-Platform - Builds on Linux, macOS, and Windows

  • Python Integration - Can be loaded via ctypes from Python

Architecture

Opaque Handles

All API types are opaque pointers backed by an internal handle table for type safety.

Prepared Statements

Frequently-used SQL queries are prepared once and cached for performance.

Foreign Keys

Referential integrity enforced at the database level.

WAL Mode

Write-Ahead Logging enabled for better concurrency.

Handle Caching

Frequently-accessed data cached in handle entries to minimize database queries.

Building

Requirements

  • C compiler (GCC, Clang, or MSVC)

  • CMake 3.10 or later

  • SQLite3 (included as amalgamation)

Build Steps

# Navigate to native directory
cd src/native

# Build with CMake
mkdir build
cd build
cmake ..
make

# Or use make directly
cd src/native
make

# Install library (optional)
sudo make install

Build Outputs

  • libucis.so (Linux) - Shared library

  • libucis.dylib (macOS) - Shared library

  • ucis.dll (Windows) - Dynamic link library

  • libucis.a - Static library (optional)

Testing

# Run test suite
make test

# Or run individual tests
cd test
python3 test_basic.py
python3 test_scopes.py

API Reference

Database Lifecycle

ucis_Open

ucisT ucis_Open(const char *name)

Open or create a UCIS database.

Parameters:
  • name – File path or NULL for in-memory database

Returns:

Database handle or NULL on error

Example:

ucisT db = ucis_Open("coverage.ucisdb");
if (!db) {
    fprintf(stderr, "Error: %s\n", ucis_GetLastError());
    return 1;
}

ucis_Close

void ucis_Close(ucisT db)

Close a UCIS database and free all resources.

Parameters:
  • db – Database handle

Example:

ucis_Close(db);

ucis_Write

int ucis_Write(ucisT db, const char *name)

Write database to a file (useful for in-memory databases).

Parameters:
  • db – Database handle

  • name – Target file path

Returns:

0 on success, non-zero on error

Example:

if (ucis_Write(db, "output.ucisdb") != 0) {
    fprintf(stderr, "Write failed: %s\n", ucis_GetLastError());
}

ucis_GetAPIVersion

const char *ucis_GetAPIVersion(void)

Get UCIS API version string.

Returns:

Version string (e.g., “1.0”)

Example:

printf("UCIS API Version: %s\n", ucis_GetAPIVersion());

ucis_GetLastError

const char *ucis_GetLastError(void)

Get the last error message from any UCIS operation.

Returns:

Error message string or NULL if no error

Example:

if (!db) {
    fprintf(stderr, "Error: %s\n", ucis_GetLastError());
}

Scope Operations

ucis_CreateScope

ucisScopeT ucis_CreateScope(ucisT db, ucisScopeT parent, const char *name, ucisSourceInfoT *source, int weight, ucisSourceT source_type, ucisScopeTypeT type, int flags)

Create a new scope in the hierarchy.

Parameters:
  • db – Database handle

  • parent – Parent scope or NULL for root-level scope

  • name – Scope name

  • source – Source location info or NULL

  • weight – Coverage weight (typically 1)

  • source_type – Source type (UCIS_VHDL, UCIS_VLOG, etc.)

  • type – Scope type (UCIS_INSTANCE, UCIS_COVERGROUP, etc.)

  • flags – Scope flags (coverage enables, UOR flags)

Returns:

New scope handle or NULL on error

Example:

// Create top-level instance
ucisScopeT top = ucis_CreateScope(
    db,
    NULL,               // No parent
    "top",
    NULL,               // No source info
    1,                  // Weight
    UCIS_VLOG,
    UCIS_INSTANCE,
    0                   // No flags
);

ucis_CreateInstance

ucisScopeT ucis_CreateInstance(ucisT db, ucisScopeT parent, const char *name, ucisSourceInfoT *source, int weight, ucisSourceT source_type)

Create an instance scope (convenience wrapper).

Parameters:
  • db – Database handle

  • parent – Parent scope

  • name – Instance name

  • source – Source location or NULL

  • weight – Coverage weight

  • source_type – Source type

Returns:

New instance scope or NULL on error

Example:

ucisScopeT dut = ucis_CreateInstance(db, top, "dut", NULL, 1, UCIS_VLOG);

ucis_GetScopeName

const char *ucis_GetScopeName(ucisT db, ucisScopeT scope)

Get the name of a scope.

Parameters:
  • db – Database handle

  • scope – Scope handle

Returns:

Scope name or NULL on error

Example:

const char* name = ucis_GetScopeName(db, scope);
printf("Scope name: %s\n", name);

ucis_GetScopeType

ucisScopeTypeT ucis_GetScopeType(ucisT db, ucisScopeT scope)

Get the type of a scope.

Parameters:
  • db – Database handle

  • scope – Scope handle

Returns:

Scope type flags

Example:

ucisScopeTypeT type = ucis_GetScopeType(db, scope);
if (type & UCIS_INSTANCE) {
    printf("This is an instance\n");
}

ucis_GetParent

ucisScopeT ucis_GetParent(ucisT db, ucisScopeT scope)

Get the parent scope.

Parameters:
  • db – Database handle

  • scope – Scope handle

Returns:

Parent scope handle or NULL if root

Example:

ucisScopeT parent = ucis_GetParent(db, scope);
if (parent) {
    printf("Parent: %s\n", ucis_GetScopeName(db, parent));
}

ucis_SetScopeWeight

int ucis_SetScopeWeight(ucisT db, ucisScopeT scope, int weight)

Set the coverage weight of a scope.

Parameters:
  • db – Database handle

  • scope – Scope handle

  • weight – New weight value

Returns:

0 on success, non-zero on error

ucis_SetScopeGoal

int ucis_SetScopeGoal(ucisT db, ucisScopeT scope, int goal)

Set the coverage goal percentage.

Parameters:
  • db – Database handle

  • scope – Scope handle

  • goal – Goal percentage (0-100)

Returns:

0 on success, non-zero on error

ucis_SetScopeFlags

int ucis_SetScopeFlags(ucisT db, ucisScopeT scope, int flags)

Set scope flags.

Parameters:
  • db – Database handle

  • scope – Scope handle

  • flags – Flag bits (coverage enables, UOR flags)

Returns:

0 on success, non-zero on error

ucis_ScopeIterate

int ucis_ScopeIterate(ucisT db, ucisScopeT parent, ucisScopeTypeT type_mask, ucisScopeCBT callback, void *userdata)

Iterate over child scopes with optional type filtering.

Parameters:
  • db – Database handle

  • parent – Parent scope or NULL for root-level scopes

  • type_mask – Scope type filter (bitwise OR of types, -1 for all)

  • callback – Callback function called for each scope

  • userdata – User data passed to callback

Returns:

0 on success, non-zero on error

Callback signature:

typedef int (*ucisScopeCBT)(void* userdata, ucisScopeT scope);

Example:

int print_scope(void* userdata, ucisScopeT scope) {
    ucisT db = (ucisT)userdata;
    printf("Scope: %s\n", ucis_GetScopeName(db, scope));
    return 0;  // Continue iteration
}

// Iterate all child scopes
ucis_ScopeIterate(db, parent, -1, print_scope, db);

// Iterate only instances
ucis_ScopeIterate(db, parent, UCIS_INSTANCE, print_scope, db);

Coverage Operations

ucis_CreateNextCover

ucisCoverT ucis_CreateNextCover(ucisT db, ucisScopeT parent, const char *name, ucisCoverDataT *data, ucisSourceInfoT *source)

Create the next coverage item in a scope.

Parameters:
  • db – Database handle

  • parent – Parent scope

  • name – Coverage item name

  • data – Coverage data (type and initial count)

  • source – Source location or NULL

Returns:

New coverage item handle or NULL on error

Example:

ucisCoverDataT data = {0};
data.type = UCIS_CVGBIN;
data.data = 0;  // Initial count

ucisCoverT bin = ucis_CreateNextCover(db, coverpoint, "low", &data, NULL);

ucis_GetCoverData

int ucis_GetCoverData(ucisT db, ucisCoverT cover, ucisCoverDataT *data)

Get coverage data for an item.

Parameters:
  • db – Database handle

  • cover – Coverage item handle

  • data – Output parameter for coverage data

Returns:

0 on success, non-zero on error

Example:

ucisCoverDataT data;
if (ucis_GetCoverData(db, cover, &data) == 0) {
    printf("Hit count: %d\n", data.data);
}

ucis_SetCoverData

int ucis_SetCoverData(ucisT db, ucisCoverT cover, ucisCoverDataT *data)

Set coverage data for an item.

Parameters:
  • db – Database handle

  • cover – Coverage item handle

  • data – New coverage data

Returns:

0 on success, non-zero on error

ucis_IncrementCoverData

int ucis_IncrementCoverData(ucisT db, ucisCoverT cover, int amount)

Increment coverage count.

Parameters:
  • db – Database handle

  • cover – Coverage item handle

  • amount – Amount to increment (default: 1)

Returns:

0 on success, non-zero on error

Example:

// Increment by 1
ucis_IncrementCoverData(db, cover, 1);

ucis_CoverIterate

int ucis_CoverIterate(ucisT db, ucisScopeT parent, ucisCoverTypeT type_mask, ucisCoverCBT callback, void *userdata)

Iterate over coverage items in a scope.

Parameters:
  • db – Database handle

  • parent – Parent scope

  • type_mask – Coverage type filter (-1 for all)

  • callback – Callback function

  • userdata – User data passed to callback

Returns:

0 on success, non-zero on error

Callback signature:

typedef int (*ucisCoverCBT)(void* userdata, ucisCoverT cover);

History Operations

ucis_CreateHistoryNode

ucisHistoryNodeT ucis_CreateHistoryNode(ucisT db, ucisHistoryNodeT parent, const char *logicalname, const char *physicalname, ucisHistoryNodeKindT kind)

Create a test history node.

Parameters:
  • db – Database handle

  • parent – Parent history node or NULL

  • logicalname – Logical test name

  • physicalname – Physical file path or NULL

  • kind – History node kind (TEST, MERGE, TESTPLAN)

Returns:

New history node handle or NULL on error

ucis_SetTestStatus

int ucis_SetTestStatus(ucisT db, ucisHistoryNodeT node, ucisTestStatusT status)

Set test execution status.

Parameters:
  • db – Database handle

  • node – History node handle

  • status – Test status (OK, WARNING, FATAL)

Returns:

0 on success, non-zero on error

ucis_HistoryNodeIterate

int ucis_HistoryNodeIterate(ucisT db, ucisHistoryNodeKindT kind_mask, ucisHistoryNodeCBT callback, void *userdata)

Iterate over history nodes.

Parameters:
  • db – Database handle

  • kind_mask – Kind filter (-1 for all)

  • callback – Callback function

  • userdata – User data

Returns:

0 on success, non-zero on error

Type Definitions

Opaque Handle Types

typedef struct ucis_s* ucisT;                    // Database handle
typedef struct ucis_scope_s* ucisScopeT;         // Scope handle
typedef struct ucis_cover_s* ucisCoverT;         // Coverage item handle
typedef struct ucis_history_s* ucisHistoryNodeT; // History node handle

Scope Types

typedef enum {
    UCIS_TOGGLE        = 0x0001,   // Toggle coverage
    UCIS_BRANCH        = 0x0002,   // Branch coverage
    UCIS_EXPR          = 0x0004,   // Expression coverage
    UCIS_COND          = 0x0008,   // Condition coverage
    UCIS_INSTANCE      = 0x0010,   // Design instance
    UCIS_DU_MODULE     = 0x0020,   // Module design unit
    UCIS_DU_ARCH       = 0x0040,   // Architecture design unit
    UCIS_COVERGROUP    = 0x1000,   // Covergroup type
    UCIS_COVERINSTANCE = 0x2000,   // Covergroup instance
    UCIS_COVERPOINT    = 0x4000,   // Coverpoint
    UCIS_CROSS         = 0x8000,   // Cross coverage
    UCIS_FSM           = 0x400000, // Finite state machine
    UCIS_ASSERT        = 0x800000  // Assertion
} ucisScopeTypeT;

Coverage Types

typedef enum {
    UCIS_CVGBIN     = 0x0001,   // Covergroup bin
    UCIS_STMTBIN    = 0x0002,   // Statement
    UCIS_BRANCHBIN  = 0x0004,   // Branch
    UCIS_EXPRBIN    = 0x0008,   // Expression
    UCIS_CONDBIN    = 0x0010,   // Condition
    UCIS_TOGGLEBIN  = 0x0200,   // Toggle
    UCIS_ASSERTBIN  = 0x0400,   // Assertion
    UCIS_FSMBIN     = 0x0800,   // FSM state/transition
    UCIS_IGNOREBIN  = 0x80000,  // Ignore bin
    UCIS_ILLEGALBIN = 0x100000, // Illegal bin
    UCIS_DEFAULTBIN = 0x200000  // Default bin
} ucisCoverTypeT;

Coverage Data

typedef struct {
    ucisCoverTypeT type;  // Coverage type
    int data;             // Hit count
    int goal;             // Goal value
    int weight;           // Weight
    int at_least;         // Minimum count for coverage
} ucisCoverDataT;

Source Information

typedef struct {
    const char* file;  // Source file path
    int line;          // Line number
    int token;         // Token position
} ucisSourceInfoT;

Source Types

typedef enum {
    UCIS_NONE = 0,
    UCIS_VHDL,
    UCIS_VLOG,
    UCIS_SV,
    UCIS_PSL,
    UCIS_E,
    UCIS_VERA
} ucisSourceT;

Test Status

typedef enum {
    UCIS_TESTSTATUS_OK = 0,      // Test passed
    UCIS_TESTSTATUS_WARNING = 1, // Test passed with warnings
    UCIS_TESTSTATUS_FATAL = 2    // Test failed
} ucisTestStatusT;

History Node Kinds

typedef enum {
    UCIS_HISTORYNODE_TEST = 0x01,     // Test run
    UCIS_HISTORYNODE_MERGE = 0x02,    // Merged coverage
    UCIS_HISTORYNODE_TESTPLAN = 0x04  // Test plan node
} ucisHistoryNodeKindT;

Complete Examples

Basic Usage

#include "ucis.h"
#include <stdio.h>

int main() {
    // Create database
    ucisT db = ucis_Open("example.ucisdb");
    if (!db) {
        fprintf(stderr, "Failed to open database\n");
        return 1;
    }

    // Create hierarchy
    ucisScopeT top = ucis_CreateInstance(db, NULL, "top", NULL, 1, UCIS_VLOG);
    ucisScopeT dut = ucis_CreateInstance(db, top, "dut", NULL, 1, UCIS_VLOG);

    // Create covergroup
    ucisScopeT cg = ucis_CreateScope(db, dut, "addr_cg", NULL, 1,
                                     UCIS_SV, UCIS_COVERGROUP, 0);
    ucisScopeT cp = ucis_CreateScope(db, cg, "addr_cp", NULL, 1,
                                     UCIS_SV, UCIS_COVERPOINT, 0);

    // Add bins
    ucisCoverDataT data = {UCIS_CVGBIN, 0, 0, 1, 1};
    data.data = 25;
    ucis_CreateNextCover(db, cp, "low", &data, NULL);

    data.data = 50;
    ucis_CreateNextCover(db, cp, "high", &data, NULL);

    // Create test record
    ucisHistoryNodeT test = ucis_CreateHistoryNode(db, NULL, "test1",
                                                   "test1.sv",
                                                   UCIS_HISTORYNODE_TEST);
    ucis_SetTestStatus(db, test, UCIS_TESTSTATUS_OK);

    // Close database
    ucis_Close(db);

    return 0;
}

Iterating Scopes

typedef struct {
    ucisT db;
    int depth;
} IterCtx;

int print_scope_cb(void* userdata, ucisScopeT scope) {
    IterCtx* ctx = (IterCtx*)userdata;

    // Print with indentation
    for (int i = 0; i < ctx->depth; i++) printf("  ");
    printf("%s (type=0x%x)\n",
           ucis_GetScopeName(ctx->db, scope),
           ucis_GetScopeType(ctx->db, scope));

    // Recurse to children
    ctx->depth++;
    ucis_ScopeIterate(ctx->db, scope, -1, print_scope_cb, ctx);
    ctx->depth--;

    return 0;
}

void print_hierarchy(ucisT db) {
    IterCtx ctx = {db, 0};
    ucis_ScopeIterate(db, NULL, -1, print_scope_cb, &ctx);
}

Using from Python

import ctypes

# Load library
libucis = ctypes.CDLL("./libucis.so")

# Define function signatures
libucis.ucis_Open.argtypes = [ctypes.c_char_p]
libucis.ucis_Open.restype = ctypes.c_void_p

libucis.ucis_CreateInstance.argtypes = [
    ctypes.c_void_p,  # db
    ctypes.c_void_p,  # parent
    ctypes.c_char_p,  # name
    ctypes.c_void_p,  # source
    ctypes.c_int,     # weight
    ctypes.c_int      # source_type
]
libucis.ucis_CreateInstance.restype = ctypes.c_void_p

libucis.ucis_Close.argtypes = [ctypes.c_void_p]

# Use the API
db = libucis.ucis_Open(b"test.ucisdb")
top = libucis.ucis_CreateInstance(db, None, b"top", None, 1, 2)  # UCIS_VLOG=2
libucis.ucis_Close(db)

Performance

Optimization Tips

  1. Use transactions for bulk operations:

    sqlite3_exec(db_conn, "BEGIN TRANSACTION", NULL, NULL, NULL);
    // Create many scopes/covers
    sqlite3_exec(db_conn, "COMMIT", NULL, NULL, NULL);
    
  2. Cache handles when possible:

    Store scope/cover handles rather than looking them up repeatedly.

  3. Use type masks in iteration:

    Filter iterations to specific types to reduce overhead.

  4. Close database when done:

    Call ucis_Close() to ensure all data is flushed.

Typical Performance

  • Scope creation: ~10,000 scopes/second

  • Coverage item creation: ~20,000 items/second

  • Iteration: ~50,000 items/second

  • Handle lookup: ~1,000,000 lookups/second

See Also