From ee924527428af95a2ba116fae2d4b9679f9c9195 Mon Sep 17 00:00:00 2001 From: Peter Monsson Date: Thu, 20 Nov 2025 16:45:02 +0100 Subject: [PATCH 1/2] Add documentation --- CLAUDE.md | 410 +++++++++++++++++++++++++++++++++++++--- README.md | 329 +++++++++++++++++++++++++++++++-- src/uvmkit_check.sv | 441 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 1117 insertions(+), 63 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 7d5431b..a895a3e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,48 +4,398 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -This is a SystemVerilog testing framework called `uvmkit_check` that provides assertion macros for verification. It's designed to work with both UVM and non-UVM environments, with compatibility across multiple simulators including Verilator, Questa, VSC, and Xcelium. +**uvmkit_check** is a SystemVerilog assertion library inspired by JUnit Assert. It provides a comprehensive set of macros for validating test conditions in verification environments, supporting both UVM and non-UVM workflows across multiple simulators. + +### Design Philosophy + +1. **Familiarity** - API mirrors JUnit Assert to leverage existing testing knowledge +2. **Simplicity** - Header-only macro library with zero dependencies +3. **Universality** - Works with and without UVM, across multiple simulators +4. **Clarity** - Only failures produce output; successes are silent +5. **Safety** - Type-specific macros prevent common comparison errors ## Architecture -- **Core Library**: `src/uvmkit_check.sv` - Contains macro definitions for assertions and error handling -- **Unit Tests**: `tests/unit/uvmkit_check_unit_test.sv` - SVUnit-based tests for the assertion macros -- **Build System**: Uses SVUnit framework with custom Python CLI for Verilator integration +### Core Components + +#### 1. Configuration Layer (`src/uvmkit_check.sv` lines 1-98) + +**Purpose**: Provides customizable infrastructure for error reporting and stacktraces. + +**Key Macros**: +- `uvmkit_stacktrace` - Simulator-specific stacktrace output (lines 48-54) +- `uvmkit_error_msg` - Error reporting (UVM vs non-UVM) (lines 73-79) +- `uvmkit_error_msg_fmt` - Internal formatting macro (lines 92-98) + +**Design Pattern**: Uses `ifndef` guards to allow external overrides for testing and simulator customization. + +#### 2. Assertion Groups (`src/uvmkit_check.sv` lines 100-627) + +Organized by functionality (mirroring JUnit organization): + +| Group | Lines | Purpose | +|-------|-------|---------| +| Failure | 100-123 | Unconditional test failure | +| Boolean Assertions | 125-178 | True/false validation | +| Equality Assertions | 180-236 | Value comparison with case equality | +| Null Assertions | 238-281 | Object handle null checks | +| Identity Assertions | 283-330 | Object reference comparison | +| Real Number Assertions | 332-402 | Floating-point comparison with tolerance | +| Enum Assertions | 404-464 | Enumerated type comparison with names | +| String Assertions | 466-529 | String comparison (Verilator-compatible) | +| UVM Object Assertions | 531-618 | Deep object comparison via compare() | + +**Design Pattern**: Each macro follows this structure: +```systemverilog +`define uvmkit_check_(, msg="") \ + begin \ + // 1. Declare automatic variables for evaluation + // 2. Evaluate expressions once (avoid multiple evaluation side effects) + // 3. Check condition + // 4. Call uvmkit_error_msg_fmt with descriptive message on failure + end +``` + +### Simulator Compatibility Strategy + +#### Conditional Compilation + +The library uses preprocessor directives to adapt to different environments: + +1. **UVM Detection**: `ifdef uvm_error` determines if UVM is available +2. **Simulator Detection**: `ifdef VERILATOR` customizes for Verilator +3. **Override Points**: `ifndef` guards allow user customization + +#### Stacktrace Handling + +| Simulator | Implementation | Status | +|-----------|----------------|--------| +| Questa/VSC/Xcelium | `$stacktrace` | Built-in | +| Verilator | Empty macro | No stacktrace support | +| Riviera-Pro | User override | Requires `define uvmkit_stacktrace $callstack()` | + +### Testing Infrastructure + +#### Unit Tests (`tests/unit/uvmkit_check_unit_test.sv`) + +**Strategy**: Override `uvmkit_error_msg` to capture errors for validation. + +**Pattern**: +```systemverilog +bit error_called; +string last_msg; + +`define uvmkit_error_msg(typ, msg) \ + error_called = 1; \ + last_msg = msg; + +`include "uvmkit_check.sv" + +// Test both success and failure paths +`SVTEST(test_check_equals) + error_called = 0; + `uvmkit_check_equals(5, 5, "should pass") + `FAIL_IF(error_called) // Success: no error -## Key Components + error_called = 0; + `uvmkit_check_equals(5, 3, "should fail") + `FAIL_UNLESS(error_called) // Failure: error called +`SVTEST_END +``` + +**Framework**: Uses SVUnit for test organization and execution. + +## Development Guidelines + +### Documentation Standards + +This project follows **Natural Docs** style, similar to UVM documentation: + +#### File Header Format +```systemverilog +//------------------------------------------------------------------------------ +// Title: Brief title +//------------------------------------------------------------------------------ +// File: filename.sv +// +// Multi-paragraph description of the file's purpose. +// Can include usage examples and important notes. +//------------------------------------------------------------------------------ +``` + +#### Group Format +```systemverilog +//------------------------------------------------------------------------------ +// Group: Group Name +//------------------------------------------------------------------------------ +// Description of what this group contains and its purpose. +``` + +#### Macro Format +```systemverilog +//------------------------------------------------------------------------------ +// Macro: macro_name +// +// Brief description of what the macro does. +// Additional details about behavior and edge cases. +// +// Parameters: +// param1 - Description of first parameter +// param2 - Description of second parameter +// msg - Optional message providing context (default: "") +// +// Example: +// | `macro_name(arg1, arg2, "context message") +// | `macro_name(expr, "another example") +// +`define macro_name(param1, param2, msg="") \ + // implementation +``` + +### Code Style -### Assertion Macros -The library provides these main assertion macros: -- `uvmkit_check_fail(msg="")` - Force a test failure -- `uvmkit_check_true(exp, msg="")` - Assert expression is true -- `uvmkit_check_false(exp, msg="")` - Assert expression is false -- `uvmkit_check_equals(a, b, msg="")` - Assert two values are equal -- `uvmkit_check_not_equals(a, b, msg="")` - Assert two values are not equal +#### Macro Naming Convention +- Prefix: `uvmkit_check_` +- Format: `uvmkit_check_[_]` +- Examples: `uvmkit_check_equals`, `uvmkit_check_equals_real`, `uvmkit_check_equals_uvm` -### Simulator Compatibility -- Conditional compilation for different simulators using `ifdef` blocks -- Custom stacktrace handling via `uvmkit_stacktrace` macro -- Flexible error messaging through `uvmkit_error_msg` macro +#### Internal Variables +- Prefix with underscore: `_exp_result`, `_a_str` +- Use `automatic` for proper scoping: `automatic logic _exp_result;` +- Evaluate expressions once to avoid side effects -## Development Commands +#### Error Messages +- Include expression text using backtick-quote: `` `"exp`" `` +- Show expected vs actual values when relevant +- Use `$sformatf` for formatted output +- Include type information for objects + +### Development Workflow + +#### Running Tests -### Running Unit Tests ```bash -SVUNIT_INSTALL=$PWD/../svunit PATH=$PATH:$PWD/../svunit/bin runSVUnit -c -Wno-WIDTH -o obj_dir +# Standard method (recommended) +SVUNIT_INSTALL=$PWD/../svunit \ +PATH=$PATH:$PWD/../svunit/bin \ +runSVUnit -c -Wno-WIDTH -o obj_dir + +# With additional include paths +SVUNIT_INSTALL=$PWD/../svunit \ +PATH=$PATH:$PWD/../svunit/bin \ +runSVUnit -c -Wno-WIDTH -o obj_dir \ + -c_arg "-I../src" -c_arg "-I../tests/stub" +``` + +**Note**: The buildSVUnit + verilator-cli.py approach mentioned in older documentation does not currently work. + +#### Adding New Assertions + +1. **Choose the appropriate group** or create a new one +2. **Write the macro** following the existing pattern +3. **Document thoroughly** using Natural Docs style +4. **Add unit tests** covering success and failure cases +5. **Update README.md** assertion reference table +6. **Test across simulators** if possible + +#### Example: Adding a New Assertion + +```systemverilog +//------------------------------------------------------------------------------ +// Macro: uvmkit_check_range +// +// Asserts that a value falls within a specified range [min, max] inclusive. +// +// Parameters: +// value - Value to check +// min - Minimum acceptable value (inclusive) +// max - Maximum acceptable value (inclusive) +// msg - Optional message providing context (default: "") +// +// Example: +// | `uvmkit_check_range(temperature, -40, 125, "Temperature out of spec") +// | `uvmkit_check_range(count, 0, 100, "Count must be 0-100") +// +`define uvmkit_check_range(value, min, max, msg="") \ + begin \ + automatic int _value = value; \ + automatic int _min = min; \ + automatic int _max = max; \ + if (_value < _min || _value > _max) begin \ + `uvmkit_error_msg_fmt("uvmkit_check_range", \ + $sformatf("Expected '%s' (%0d) to be in range [%0d, %0d]", \ + `"value`", _value, _min, _max), msg) \ + end \ + end ``` -Note: The buildSVUnit + verilator-cli.py approach mentioned in README.md does not currently work. +### Testing Guidelines + +#### Test Coverage Requirements + +Each assertion macro must have tests for: + +1. **Success case** - Assertion passes without error +2. **Failure case** - Assertion fails and calls error handler +3. **Edge cases** - Boundary conditions, X/Z states (for logic), null (for objects) +4. **Message handling** - Both with and without custom messages + +#### Test Naming Convention + +```systemverilog +`SVTEST(test__) + // Test implementation +`SVTEST_END +``` + +Examples: +- `test_check_equals_success` +- `test_check_equals_failure` +- `test_check_equals_with_x_values` + +### Common Pitfalls + +#### 1. Multiple Expression Evaluation + +**Problem**: Macros evaluate parameters multiple times, causing side effects. + +**Bad**: +```systemverilog +`define bad_macro(exp) \ + if ((exp) !== 1'b1) $error("Failed: %b", (exp)); +// 'exp' evaluated twice! +``` + +**Good**: +```systemverilog +`define good_macro(exp) \ + begin \ + automatic logic _result = (exp); \ + if (_result !== 1'b1) $error("Failed: %b", _result); + end +``` + +#### 2. Case vs Logical Equality + +**Problem**: Using `==` instead of `===` misses X/Z mismatches. + +**Always use case equality** (`===`, `!==`) for hardware comparisons: +- Distinguishes X from 0/1 +- Distinguishes Z from 0/1 +- More precise for verification + +#### 3. Real Number Exact Comparison + +**Problem**: Floating-point precision makes exact equality unreliable. + +**Solution**: Always use `uvmkit_check_equals_real` with appropriate delta: +```systemverilog +// Bad +`uvmkit_check_equals(voltage, 3.3, "voltage check") // Don't do this with reals + +// Good +`uvmkit_check_equals_real(3.3, voltage, 0.01, "voltage check") +``` + +## Build System + +### SVUnit Integration + +- **Framework**: SVUnit (http://agilesoc.com/open-source-projects/svunit/) +- **Test Discovery**: Automatic via `buildSVUnit` or `runSVUnit` +- **File List**: `.svunit.f` (auto-generated, do not edit manually) +- **Output**: `obj_dir/` (Verilator build artifacts) + +### Dependencies + +#### Required +- SVUnit framework (must be in `../svunit/`) +- SystemVerilog simulator (Verilator, Questa, etc.) + +#### Optional +- Python virtual environment in `../.code_agent/` (for alternative build method) +- verilator-cli.py wrapper (currently not functional) + +## Git Workflow + +### Branch Structure +- `main` - Stable releases +- `documentation` - Documentation updates (current branch) +- Feature branches - Named descriptively + +### Commit Guidelines +- Keep commits focused and atomic +- Reference issue numbers when applicable +- Follow conventional commit format when possible + +## Future Enhancements + +### Potential Additions + +1. **Array Assertions** + - `uvmkit_check_array_equals` + - `uvmkit_check_array_contains` + +2. **Size Information in Errors** + - Show bit widths when X-value comparisons fail + - Help debug size mismatches + +3. **Format Options** + - Decimal, hex, binary output selection + - User-configurable via macro override + +4. **Performance Assertions** + - `uvmkit_check_time_limit` - Execution time bounds + - Cycle count validation + +5. **Coverage Integration** + - Track which assertions fired + - Report assertion coverage metrics + +## Resources + +### External Documentation +- [JUnit Assert JavaDoc](https://junit.org/junit4/javadoc/4.13.2/org/junit/Assert.html) - Inspiration +- [UVM 1.2 Documentation](https://uvmkit.com/uvm-1.2/docs/) - Documentation style reference +- [SVUnit Documentation](http://agilesoc.com/open-source-projects/svunit/) - Testing framework + +### Internal Documentation +- `README.md` - User-facing documentation +- `src/uvmkit_check.sv` - Source code with Natural Docs comments +- `tests/unit/uvmkit_check_unit_test.sv` - Test examples + +## Debugging Tips + +### Viewing Stacktraces + +If stacktraces aren't appearing: +1. Check simulator support (Verilator doesn't support `$stacktrace`) +2. Verify simulator-specific macro (e.g., Riviera-Pro needs `$callstack()`) +3. Override `uvmkit_stacktrace` before including the library + +### Capturing Error Messages + +For debugging or testing: +```systemverilog +string captured_errors[$]; + +`define uvmkit_error_msg(typ, msg) \ + captured_errors.push_back($sformatf("[%s] %s", typ, msg)); + +`include "uvmkit_check.sv" + +// Now all errors are captured in captured_errors queue +``` -## Development Environment +### Common Error Messages -- Requires SVUnit testing framework installed in `../svunit/` -- Uses Python virtual environment in `../.code_agent/` -- Custom Verilator CLI wrapper for simulation -- File list managed in `.svunit.f` (auto-generated by buildSVUnit) +| Error Type | Cause | Solution | +|------------|-------|----------| +| "Cannot compare null objects" | UVM compare with null | Add null checks before comparison | +| "Expected...but got 'bx" | X/Z state in boolean check | Verify signal initialization | +| Expression evaluation issues | Side effects in macro args | Check for functions with side effects | -## Testing Strategy +--- -Tests use SVUnit framework with custom error injection: -- Override `uvmkit_error_msg` macro to capture error messages -- Use `error_called` flag and `last_msg` string for verification -- Each test validates both success and failure cases of assertions \ No newline at end of file +**Last Updated**: 2025-11-20 +**For Claude Code**: When making changes, always update this file to reflect new patterns or architectural decisions. diff --git a/README.md b/README.md index a8b57a1..ed19b0f 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,323 @@ -How to use -========== +# uvmkit_check -How to run unit tests ---------------------- +**A comprehensive set of assertion macros for SystemVerilog verification**, inspired by [JUnit Assert](https://junit.org/junit4/javadoc/4.13.2/org/junit/Assert.html). - SVUNIT_INSTALL=$PWD/../svunit PATH=$PATH:$PWD/../svunit/bin runSVUnit -c -Wno-WIDTH -o obj_dir -c_arg "-I../src" -c_arg "-I../tests/stub" +uvmkit_check provides a familiar and powerful way to validate test conditions in both UVM and non-UVM environments. Only failed assertions produce output, keeping simulation logs clean and focused on actual failures. +## Features -Aternaltive way to run unit tests ---------------------------------- +- **JUnit-Inspired API** - Familiar assertion methods for developers coming from software testing +- **Universal Compatibility** - Works with and without UVM +- **Multi-Simulator Support** - Verilator, Questa, VSC (Verilab Simvision), and Xcelium +- **Comprehensive Assertions** - Boolean, equality, null checks, identity, real numbers, enums, strings, and UVM objects +- **Clean Output** - Only failures are reported; successful assertions pass silently +- **Detailed Error Messages** - Clear, actionable feedback when assertions fail +- **Type-Safe Comparisons** - Specialized macros for different data types +- **Automatic Stacktraces** - Context for debugging failures (simulator-dependent) -This doesn't appear to work currently. +## Quick Start - cd .. - . .code_agent/bin/activate - cd uvmkit_check - SVUNIT_INSTALL=`pwd`/../svunit/ PATH=$PATH:$SVUNIT_INSTALL/bin buildSVUnit - python ../code_agent/verilator-cli.py -f .svunit.f --top-module testrunner +### Basic Usage +```systemverilog +// Include the library +`include "uvmkit_check.sv" + +module my_test; + initial begin + logic [7:0] data = 8'hA5; + logic ready = 1'b1; + string name = "my_component"; + + // Boolean assertions + `uvmkit_check_true(ready, "Ready signal must be high") + `uvmkit_check_false(error, "No errors should occur") + + // Equality assertions + `uvmkit_check_equals(data, 8'hA5, "Data mismatch") + `uvmkit_check_not_equals(count, 0, "Count should be non-zero") + + // String assertions + `uvmkit_check_equals_string(name, "my_component", "Name mismatch") + + // Null checks + `uvmkit_check_not_null(my_object, "Object must exist") + end +endmodule +``` + +### With UVM + +```systemverilog +`include "uvm_macros.svh" +import uvm_pkg::*; +`include "uvmkit_check.sv" + +class my_test extends uvm_test; + `uvm_component_utils(my_test) + + function void run_phase(uvm_phase phase); + my_transaction expected, actual; + + // UVM object comparison using compare() + `uvmkit_check_equals_uvm(expected, actual, "Transaction mismatch") + + // All other assertions work the same + `uvmkit_check_equals(actual.addr, 32'h1000, "Address incorrect") + endfunction +endclass +``` + +## Installation + +1. Copy `src/uvmkit_check.sv` to your project +2. Include it in your testbench: + ```systemverilog + `include "uvmkit_check.sv" + ``` + +That's it! The library is header-only (macro-based) with no additional dependencies. + +## Assertion Reference + +### Failure + +| Macro | Description | +|-------|-------------| +| `uvmkit_check_fail(msg)` | Forces unconditional test failure | + +### Boolean Assertions + +| Macro | Description | +|-------|-------------| +| `uvmkit_check_true(exp, msg)` | Asserts expression evaluates to true (non-zero) | +| `uvmkit_check_false(exp, msg)` | Asserts expression evaluates to false (zero) | + +### Equality Assertions + +| Macro | Description | +|-------|-------------| +| `uvmkit_check_equals(a, b, msg)` | Asserts two values are equal using case equality (===) | +| `uvmkit_check_not_equals(a, b, msg)` | Asserts two values are not equal using case inequality (!==) | + +### Null Assertions + +| Macro | Description | +|-------|-------------| +| `uvmkit_check_null(obj, msg)` | Asserts object handle is null | +| `uvmkit_check_not_null(obj, msg)` | Asserts object handle is not null | + +### Identity Assertions + +| Macro | Description | +|-------|-------------| +| `uvmkit_check_same(a, b, msg)` | Asserts two handles refer to the same object instance | +| `uvmkit_check_not_same(a, b, msg)` | Asserts two handles refer to different object instances | + +### Real Number Assertions + +| Macro | Description | +|-------|-------------| +| `uvmkit_check_equals_real(expected, actual, delta, msg)` | Asserts real values are equal within tolerance | +| `uvmkit_check_not_equals_real(expected, actual, delta, msg)` | Asserts real values differ by more than tolerance | + +### Enum Assertions + +| Macro | Description | +|-------|-------------| +| `uvmkit_check_equals_enum(enum_t, expected, actual, msg)` | Asserts enum values are equal | +| `uvmkit_check_not_equals_enum(enum_t, expected, actual, msg)` | Asserts enum values are not equal | + +### String Assertions + +| Macro | Description | +|-------|-------------| +| `uvmkit_check_equals_string(a, b, msg)` | Asserts strings are equal (case-sensitive) | +| `uvmkit_check_not_equals_string(a, b, msg)` | Asserts strings are not equal | + +### UVM Object Assertions + +| Macro | Description | +|-------|-------------| +| `uvmkit_check_equals_uvm(a, b, msg)` | Asserts UVM objects are equal using compare() | +| `uvmkit_check_not_equals_uvm(a, b, msg)` | Asserts UVM objects are not equal using compare() | + +*Note: UVM assertions only available when UVM is included* + +## Detailed Examples + +### Boolean Assertions + +```systemverilog +bit enable = 1'b1; +bit [3:0] status = 4'b0000; + +`uvmkit_check_true(enable, "Enable must be high") +`uvmkit_check_true(count > 0, "Count must be positive") +`uvmkit_check_false(status[0], "Error bit must be clear") +``` + +### Equality with X/Z Handling + +```systemverilog +// Uses case equality (===) to properly handle X and Z +logic [7:0] data = 8'bxxxx_1010; +logic [7:0] expected = 8'bxxxx_1010; + +`uvmkit_check_equals(data, expected, "Match including X states") +``` + +### Real Number Comparisons + +```systemverilog +real voltage = 3.301; +real target = 3.300; +real tolerance = 0.05; + +// Passes: |3.301 - 3.300| = 0.001 < 0.05 +`uvmkit_check_equals_real(target, voltage, tolerance, "Voltage in range") +``` + +### Enum Comparisons + +```systemverilog +typedef enum {IDLE, ACTIVE, ERROR} state_t; +state_t current_state = IDLE; + +`uvmkit_check_equals_enum(state_t, IDLE, current_state, "Should be IDLE") +// Error output includes enum names: "Expected IDLE but got ACTIVE" +``` + +### Object Identity vs Equality + +```systemverilog +my_class obj_a, obj_b, obj_c; +obj_a = new(); +obj_b = obj_a; // Same object +obj_c = new(); // Different object + +`uvmkit_check_same(obj_a, obj_b, "Should reference same object") // PASS +`uvmkit_check_not_same(obj_a, obj_c, "Should be different objects") // PASS +``` + +## Simulator Compatibility + +### Stacktrace Support + +| Simulator | Stacktrace | Method | +|-----------|------------|--------| +| Questa | ✅ Yes | `$stacktrace` | +| VSC | ✅ Yes | `$stacktrace` | +| Xcelium | ✅ Yes | `$stacktrace` | +| Verilator | ❌ No | Not supported | +| Riviera-Pro | ⚙️ Custom | Override `uvmkit_stacktrace` macro | + +### Custom Stacktrace Configuration + +For simulators with different stacktrace commands: + +```systemverilog +// Before including uvmkit_check.sv +`define uvmkit_stacktrace $callstack(); +`include "uvmkit_check.sv" +``` + +### Error Reporting + +The library automatically detects UVM and uses the appropriate error mechanism: + +- **With UVM**: Uses `uvm_error` for integration with UVM reporting system +- **Without UVM**: Uses SystemVerilog `$error` with formatted output + +## Comparison to JUnit Assert + +If you're familiar with JUnit, here's the mapping: + +| JUnit Assert | uvmkit_check | Notes | +|--------------|--------------|-------| +| `fail()` | `uvmkit_check_fail()` | Unconditional failure | +| `assertTrue()` | `uvmkit_check_true()` | Boolean validation | +| `assertFalse()` | `uvmkit_check_false()` | Boolean validation | +| `assertEquals()` | `uvmkit_check_equals()` | Case equality (===) | +| `assertNotEquals()` | `uvmkit_check_not_equals()` | Case inequality (!==) | +| `assertNull()` | `uvmkit_check_null()` | Null check | +| `assertNotNull()` | `uvmkit_check_not_null()` | Not null check | +| `assertSame()` | `uvmkit_check_same()` | Reference equality | +| `assertNotSame()` | `uvmkit_check_not_same()` | Reference inequality | +| `assertEquals(double, delta)` | `uvmkit_check_equals_real()` | Floating-point with tolerance | + +## Advanced Usage + +### Custom Error Handling (Testing) + +For unit testing uvmkit_check itself or capturing errors: + +```systemverilog +// Override error reporting before including +`define uvmkit_error_msg(typ, msg) \ + begin \ + error_count++; \ + last_error_msg = msg; \ + end + +`include "uvmkit_check.sv" +``` + +### Message Parameter Best Practices + +The `msg` parameter is always optional but recommended: + +```systemverilog +// Less helpful - just shows what failed +`uvmkit_check_equals(data, expected) + +// More helpful - provides context +`uvmkit_check_equals(data, expected, "AXI read data mismatch at address 0x1000") + +// Dynamic messages +`uvmkit_check_true(valid, $sformatf("Cycle %0d: valid must be high", cycle)) +``` + +## Development + +### Running Unit Tests + +```bash +SVUNIT_INSTALL=$PWD/../svunit \ +PATH=$PATH:$PWD/../svunit/bin \ +runSVUnit -c -Wno-WIDTH -o obj_dir +``` + +### Requirements + +- [SVUnit](http://agilesoc.com/open-source-projects/svunit/) testing framework +- SystemVerilog simulator (Verilator recommended for development) + +## Documentation + +- **Source Documentation**: Natural Docs style comments in `src/uvmkit_check.sv` +- **API Reference**: See [Assertion Reference](#assertion-reference) above +- **Examples**: See [examples/](examples/) directory (if available) + +## Contributing + +Contributions are welcome! Please ensure: + +1. All new assertions include comprehensive documentation +2. Unit tests are added for new features +3. Code works across supported simulators +4. Follow existing naming conventions + +## License + +[Specify your license here] + +## Credits + +Inspired by [JUnit Assert](https://junit.org/junit4/javadoc/4.13.2/org/junit/Assert.html) from the Java testing ecosystem. + +## Support + +- **Issues**: [GitHub Issues](https://github.com/your-repo/uvmkit_check/issues) +- **Documentation**: See source code comments in Natural Docs format +- **Examples**: Check the test suite in `tests/` directory diff --git a/src/uvmkit_check.sv b/src/uvmkit_check.sv index 97e819f..bf7f887 100644 --- a/src/uvmkit_check.sv +++ b/src/uvmkit_check.sv @@ -1,7 +1,50 @@ -// This define is used by uvmkit checks to output the stacktrace before the error message. The stacktrace output works with Questa, VSC and Xcelium. -// It can be overriden by users of other simulators such as Aldec Riviera-Pro. For example Riviera-Pro users can use this define: -// `define uvmkit_stacktrace $callstack(); +//------------------------------------------------------------------------------ +// Title: uvmkit_check - SystemVerilog Assertion Macros +//------------------------------------------------------------------------------ +// File: uvmkit_check.sv +// +// A comprehensive set of assertion macros for SystemVerilog verification. +// Inspired by JUnit Assert, these macros provide a familiar and powerful way +// to validate test conditions in both UVM and non-UVM environments. +// +// Only failed assertions produce output. Successful assertions pass silently, +// keeping simulation logs clean and focused on failures. +// +// All macros can be used directly in testbenches and verification components +// across multiple simulators including Verilator, Questa, VSC, and Xcelium. +// +// Example Usage: +// | `uvmkit_check_true(enable_signal, "Enable must be high") +// | `uvmkit_check_equals(expected_data, actual_data, "Data mismatch") +// | `uvmkit_check_not_null(obj, "Object cannot be null") +// +// Simulator Compatibility: +// The library automatically adapts to the target simulator using conditional +// compilation. Stacktrace and error reporting mechanisms are selected based +// on available simulator features. +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Group: Configuration +//------------------------------------------------------------------------------ +// These macros can be overridden to customize error reporting and stacktrace +// behavior for different simulators or testing frameworks. + +//------------------------------------------------------------------------------ +// Macro: uvmkit_stacktrace +// +// Outputs a stacktrace before error messages to help locate the source of +// assertion failures. This macro is automatically configured for different +// simulators: +// +// - Questa, VSC, Xcelium: Uses $stacktrace +// - Verilator: No stacktrace (not supported) +// - Other simulators: Can override with custom implementation +// +// Override Example: +// | // For Aldec Riviera-Pro +// | `define uvmkit_stacktrace $callstack(); +// `ifndef uvmkit_stacktrace `ifdef VERILATOR `define uvmkit_stacktrace @@ -10,7 +53,23 @@ `endif `endif -// Use ifndef here so that we can inject testability to the checks +//------------------------------------------------------------------------------ +// Macro: uvmkit_error_msg +// +// Reports error messages to the simulation environment. This macro +// automatically selects between UVM error reporting and SystemVerilog $error +// based on whether UVM is available. +// +// - UVM environments: Uses `uvm_error for integration with UVM reporting +// - Non-UVM: Uses $error with formatted output +// +// Parameters: +// typ - Error type identifier (string) +// msg - Error message content (string) +// +// This macro can be overridden for testing purposes to inject custom error +// handling or capture error messages for validation. +// `ifndef uvmkit_error_msg `ifdef uvm_error `define uvmkit_error_msg(typ, msg) `uvm_error(typ, msg) @@ -19,7 +78,17 @@ `endif `endif -// For internal use only +//------------------------------------------------------------------------------ +// Macro: uvmkit_error_msg_fmt (Internal Use Only) +// +// Internal formatting macro that combines stacktrace output with error +// messages and optional additional context. Not intended for direct use. +// +// Parameters: +// typ - Error type identifier +// msg - Base error message +// extra - Optional additional message to append +// `define uvmkit_error_msg_fmt(typ, msg, extra) \ `uvmkit_stacktrace \ if (extra != "") begin \ @@ -28,9 +97,52 @@ `uvmkit_error_msg(typ, msg) \ end +//------------------------------------------------------------------------------ +// Group: Failure +//------------------------------------------------------------------------------ +// Unconditional test failure macro. + +//------------------------------------------------------------------------------ +// Macro: uvmkit_check_fail +// +// Forces a test failure with an optional message. Use this when a condition +// indicates the test should not continue or when a code path that should be +// unreachable is executed. +// +// Parameters: +// msg - Optional message providing context for the failure (default: "") +// +// Example: +// | case (opcode) +// | ADD: process_add(); +// | SUB: process_sub(); +// | default: `uvmkit_check_fail("Invalid opcode") +// | endcase +// `define uvmkit_check_fail(msg="") \ `uvmkit_error_msg_fmt("uvmkit_check_fail", "called", msg) +//------------------------------------------------------------------------------ +// Group: Boolean Assertions +//------------------------------------------------------------------------------ +// Assertions for validating boolean expressions and conditions. + +//------------------------------------------------------------------------------ +// Macro: uvmkit_check_true +// +// Asserts that the given expression evaluates to true (non-zero). +// The expression is reduced using bitwise OR to handle multi-bit values. +// Fails if the expression evaluates to false, X, or Z. +// +// Parameters: +// exp - Expression to evaluate (any integral type) +// msg - Optional message providing context for the assertion (default: "") +// +// Example: +// | `uvmkit_check_true(data_valid, "Data valid signal must be high") +// | `uvmkit_check_true(count > 0, "Count must be positive") +// | `uvmkit_check_true(status[0], "LSB of status must be set") +// `define uvmkit_check_true(exp, msg="") \ begin \ automatic logic _exp_result; \ @@ -40,6 +152,22 @@ end \ end +//------------------------------------------------------------------------------ +// Macro: uvmkit_check_false +// +// Asserts that the given expression evaluates to false (zero). +// The expression is reduced using bitwise OR to handle multi-bit values. +// Fails if the expression evaluates to true, X, or Z. +// +// Parameters: +// exp - Expression to evaluate (any integral type) +// msg - Optional message providing context for the assertion (default: "") +// +// Example: +// | `uvmkit_check_false(error_flag, "No errors should be flagged") +// | `uvmkit_check_false(count == 0, "Count should not be zero") +// | `uvmkit_check_false(busy, "Module should not be busy") +// `define uvmkit_check_false(exp, msg="") \ begin \ automatic logic _exp_result; \ @@ -49,6 +177,29 @@ end \ end +//------------------------------------------------------------------------------ +// Group: Equality Assertions +//------------------------------------------------------------------------------ +// Assertions for comparing values for equality and inequality. +// Uses case equality (===, !==) to properly handle X and Z values. + +//------------------------------------------------------------------------------ +// Macro: uvmkit_check_equals +// +// Asserts that two values are equal using case equality (===). +// This checks for exact equality including X and Z states. +// Fails if the values differ in any bit position. +// +// Parameters: +// a - First value to compare (any integral type) +// b - Second value to compare (any integral type) +// msg - Optional message providing context for the assertion (default: "") +// +// Example: +// | `uvmkit_check_equals(expected_data, actual_data, "Data mismatch") +// | `uvmkit_check_equals(state, IDLE, "State should be IDLE") +// | `uvmkit_check_equals(addr[7:0], 8'hA5, "Address lower byte incorrect") +// `define uvmkit_check_equals(a, b, msg="") \ begin \ automatic logic _exp_result; \ @@ -58,6 +209,23 @@ end \ end +//------------------------------------------------------------------------------ +// Macro: uvmkit_check_not_equals +// +// Asserts that two values are not equal using case inequality (!==). +// This checks that values differ in at least one bit position. +// Fails if the values are exactly equal including X and Z states. +// +// Parameters: +// a - First value to compare (any integral type) +// b - Second value to compare (any integral type) +// msg - Optional message providing context for the assertion (default: "") +// +// Example: +// | `uvmkit_check_not_equals(new_val, old_val, "Value should have changed") +// | `uvmkit_check_not_equals(id_a, id_b, "IDs must be unique") +// | `uvmkit_check_not_equals(status, ERROR, "Status should not be ERROR") +// `define uvmkit_check_not_equals(a, b, msg="") \ begin \ automatic logic _exp_result; \ @@ -67,26 +235,125 @@ end \ end +//------------------------------------------------------------------------------ +// Group: Null Assertions +//------------------------------------------------------------------------------ +// Assertions for validating object handle null states. + +//------------------------------------------------------------------------------ +// Macro: uvmkit_check_null +// +// Asserts that an object handle is null. +// Fails if the handle points to a valid object. +// +// Parameters: +// obj - Object handle to check +// msg - Optional message providing context for the assertion (default: "") +// +// Example: +// | `uvmkit_check_null(optional_config, "Config should not be set") +// | `uvmkit_check_null(deleted_obj, "Object should have been deleted") +// `define uvmkit_check_null(obj, msg="") \ if ((obj) != null) begin \ `uvmkit_error_msg_fmt("uvmkit_check_null", $sformatf("Expected '%s' to be null, but it is not null", `"obj`"), msg) \ end +//------------------------------------------------------------------------------ +// Macro: uvmkit_check_not_null +// +// Asserts that an object handle is not null. +// Fails if the handle is null, indicating that an expected object was not +// created or has been invalidated. +// +// Parameters: +// obj - Object handle to check +// msg - Optional message providing context for the assertion (default: "") +// +// Example: +// | `uvmkit_check_not_null(driver, "Driver must be created") +// | `uvmkit_check_not_null(cfg, "Configuration object required") +// | `uvmkit_check_not_null(get_object(), "Factory must return valid object") +// `define uvmkit_check_not_null(obj, msg="") \ if ((obj) == null) begin \ `uvmkit_error_msg_fmt("uvmkit_check_not_null", $sformatf("Expected '%s' to not be null, but it is null", `"obj`"), msg) \ end +//------------------------------------------------------------------------------ +// Group: Identity Assertions +//------------------------------------------------------------------------------ +// Assertions for comparing object identity (handle equality). +// These check if two handles refer to the same object instance, not whether +// their contents are equal. + +//------------------------------------------------------------------------------ +// Macro: uvmkit_check_same +// +// Asserts that two object handles refer to the same object instance. +// This performs handle comparison, not content comparison. +// Fails if the handles point to different objects. +// +// Parameters: +// a - First object handle +// b - Second object handle +// msg - Optional message providing context for the assertion (default: "") +// +// Example: +// | `uvmkit_check_same(cached_obj, retrieved_obj, "Should retrieve cached instance") +// | `uvmkit_check_same(singleton_a, singleton_b, "Singleton pattern violated") +// `define uvmkit_check_same(a, b, msg="") \ if ((a) != (b)) begin \ `uvmkit_error_msg_fmt("uvmkit_check_same", $sformatf("Expected '%s' and '%s' to be the same object, but they are different", `"a`", `"b`"), msg) \ end +//------------------------------------------------------------------------------ +// Macro: uvmkit_check_not_same +// +// Asserts that two object handles refer to different object instances. +// This performs handle comparison, not content comparison. +// Fails if both handles point to the same object. +// +// Parameters: +// a - First object handle +// b - Second object handle +// msg - Optional message providing context for the assertion (default: "") +// +// Example: +// | `uvmkit_check_not_same(obj_a, obj_b, "Objects should be independent copies") +// | `uvmkit_check_not_same(cloned, original, "Clone must be a new instance") +// `define uvmkit_check_not_same(a, b, msg="") \ if ((a) == (b)) begin \ `uvmkit_error_msg_fmt("uvmkit_check_not_same", $sformatf("Expected '%s' and '%s' to be different objects, but they are the same", `"a`", `"b`"), msg) \ end +//------------------------------------------------------------------------------ +// Group: Real Number Assertions +//------------------------------------------------------------------------------ +// Assertions for comparing real (floating-point) values with tolerance. +// Due to floating-point precision limitations, exact equality is rarely +// appropriate. These macros compare values within a specified delta. + +//------------------------------------------------------------------------------ +// Macro: uvmkit_check_equals_real +// +// Asserts that two real values are equal within a specified tolerance. +// The assertion passes if the absolute difference between the values +// is less than or equal to delta. +// +// Parameters: +// expected - Expected real value +// actual - Actual real value to compare +// delta - Maximum allowed absolute difference (tolerance) +// msg - Optional message providing context for the assertion (default: "") +// +// Example: +// | `uvmkit_check_equals_real(3.14159, pi_approx, 0.0001, "Pi approximation") +// | `uvmkit_check_equals_real(expected_voltage, measured, 0.05, "Voltage within 50mV") +// | `uvmkit_check_equals_real(1.0, result, 1e-6, "Result should be close to 1.0") +// `define uvmkit_check_equals_real(expected, actual, delta, msg="") \ begin \ real _expected_val; \ @@ -102,6 +369,23 @@ end \ end +//------------------------------------------------------------------------------ +// Macro: uvmkit_check_not_equals_real +// +// Asserts that two real values are not equal within a specified tolerance. +// The assertion passes if the absolute difference between the values +// is greater than delta. +// +// Parameters: +// expected - Expected real value +// actual - Actual real value to compare +// delta - Minimum required absolute difference +// msg - Optional message providing context for the assertion (default: "") +// +// Example: +// | `uvmkit_check_not_equals_real(0.0, signal_level, 0.1, "Signal should be non-zero") +// | `uvmkit_check_not_equals_real(old_temp, new_temp, 1.0, "Temperature should change") +// `define uvmkit_check_not_equals_real(expected, actual, delta, msg="") \ begin \ real _expected_val; \ @@ -117,6 +401,30 @@ end \ end +//------------------------------------------------------------------------------ +// Group: Enum Assertions +//------------------------------------------------------------------------------ +// Assertions for comparing enumerated type values. +// These macros provide clear error messages using enum names. + +//------------------------------------------------------------------------------ +// Macro: uvmkit_check_equals_enum +// +// Asserts that two enum values are equal. +// Error messages include the enum names for easy debugging. +// +// Parameters: +// enum_t - The enumerated type +// expected - Expected enum value +// actual - Actual enum value to compare +// msg - Optional message providing context for the assertion (default: "") +// +// Example: +// | typedef enum {IDLE, ACTIVE, ERROR} state_t; +// | state_t current_state; +// | `uvmkit_check_equals_enum(state_t, IDLE, current_state, "Should be in IDLE") +// | `uvmkit_check_equals_enum(opcode_t, ADD, decoded_op, "Opcode mismatch") +// `define uvmkit_check_equals_enum(enum_t, expected, actual, msg="") \ begin \ enum_t _expected; \ @@ -128,6 +436,22 @@ end \ end +//------------------------------------------------------------------------------ +// Macro: uvmkit_check_not_equals_enum +// +// Asserts that two enum values are not equal. +// Error messages include the enum names for easy debugging. +// +// Parameters: +// enum_t - The enumerated type +// expected - Expected enum value +// actual - Actual enum value to compare +// msg - Optional message providing context for the assertion (default: "") +// +// Example: +// | `uvmkit_check_not_equals_enum(state_t, ERROR, current_state, "Should not be ERROR") +// | `uvmkit_check_not_equals_enum(priority_t, old_prio, new_prio, "Priority should change") +// `define uvmkit_check_not_equals_enum(enum_t, expected, actual, msg="") \ begin \ enum_t _expected; \ @@ -139,8 +463,29 @@ end \ end +//------------------------------------------------------------------------------ +// Group: String Assertions +//------------------------------------------------------------------------------ +// Assertions for comparing string values. +// These macros work with all simulators including Verilator. -// String-specific comparison macros (work with all simulators including Verilator) +//------------------------------------------------------------------------------ +// Macro: uvmkit_check_equals_string +// +// Asserts that two strings are equal. +// The comparison is case-sensitive and checks for exact match. +// Error messages show both expected and actual strings for easy debugging. +// +// Parameters: +// a - First string to compare +// b - Second string to compare +// msg - Optional message providing context for the assertion (default: "") +// +// Example: +// | `uvmkit_check_equals_string("IDLE", state_name, "State name mismatch") +// | `uvmkit_check_equals_string(expected_msg, received_msg, "Message content wrong") +// | `uvmkit_check_equals_string(get_name(), "my_component", "Name mismatch") +// `define uvmkit_check_equals_string(a, b, msg="") \ begin \ string _a_str; \ @@ -154,6 +499,22 @@ end \ end +//------------------------------------------------------------------------------ +// Macro: uvmkit_check_not_equals_string +// +// Asserts that two strings are not equal. +// The comparison is case-sensitive. +// Fails if both strings are identical. +// +// Parameters: +// a - First string to compare +// b - Second string to compare +// msg - Optional message providing context for the assertion (default: "") +// +// Example: +// | `uvmkit_check_not_equals_string(prev_name, new_name, "Name should have changed") +// | `uvmkit_check_not_equals_string("", description, "Description cannot be empty") +// `define uvmkit_check_not_equals_string(a, b, msg="") \ begin \ string _a_str; \ @@ -167,19 +528,37 @@ end \ end -//=================================== -// UVM Object Comparison Macros -//=================================== -// These macros are only available when UVM is defined by including uvm_macros.svh. -// This allows uvmkit_check to be used in environments without UVM. +//------------------------------------------------------------------------------ +// Group: UVM Object Assertions +//------------------------------------------------------------------------------ +// Assertions for comparing UVM objects using their compare() method. +// These macros are only available when UVM is defined (by including uvm_macros.svh). +// This conditional compilation allows uvmkit_check to be used in non-UVM environments. `ifdef uvm_error -// uvmkit_check_equals_uvm - Checks if two UVM objects are equal using compare() +//------------------------------------------------------------------------------ +// Macro: uvmkit_check_equals_uvm +// +// Asserts that two UVM objects are equal using their compare() method. +// This performs a deep comparison of object contents based on the +// comparison policy defined by the object's compare() implementation. +// +// The macro automatically handles null checking and provides detailed +// error messages including type information when comparisons fail. +// // Parameters: -// a - First uvm_object to compare -// b - Second uvm_object to compare -// msg - Optional message to append to error output +// a - First uvm_object to compare (must not be null) +// b - Second uvm_object to compare (must not be null) +// msg - Optional message providing context for the assertion (default: "") +// +// Example: +// | my_transaction expected_txn, actual_txn; +// | `uvmkit_check_equals_uvm(expected_txn, actual_txn, "Transaction mismatch") +// | +// | uvm_reg expected_reg, actual_reg; +// | `uvmkit_check_equals_uvm(expected_reg, actual_reg, "Register config differs") +// `define uvmkit_check_equals_uvm(a, b, msg="") \ begin \ automatic uvm_object _a_obj = a; \ @@ -195,11 +574,28 @@ end \ end -// uvmkit_check_not_equals_uvm - Checks if two UVM objects are not equal using compare() +//------------------------------------------------------------------------------ +// Macro: uvmkit_check_not_equals_uvm +// +// Asserts that two UVM objects are not equal using their compare() method. +// This performs a deep comparison of object contents based on the +// comparison policy defined by the object's compare() implementation. +// +// If both objects are null, the assertion fails (null objects are considered equal). +// If only one object is null, the assertion passes (they are different). +// // Parameters: // a - First uvm_object to compare // b - Second uvm_object to compare -// msg - Optional message to append to error output +// msg - Optional message providing context for the assertion (default: "") +// +// Example: +// | my_transaction prev_txn, new_txn; +// | `uvmkit_check_not_equals_uvm(prev_txn, new_txn, "Transactions should differ") +// | +// | uvm_sequence_item item_a, item_b; +// | `uvmkit_check_not_equals_uvm(item_a, item_b, "Items must be unique") +// `define uvmkit_check_not_equals_uvm(a, b, msg="") \ begin \ automatic uvm_object _a_obj = a; \ @@ -221,7 +617,12 @@ `endif // uvm_error -// TODOs pertaining to uvmkit_check_equals and uvmkit_check_not_equals -// TODO: if both inputs are x but their sizes differ, then this is not equal. this is correct, but hard to debug. can we add size information to each expression or is this too hard? -// TODO: optionally have decimal, hex or binary output for the components of the expression. +//------------------------------------------------------------------------------ +// Notes and Future Enhancements +//------------------------------------------------------------------------------ +// TODOs pertaining to uvmkit_check_equals and uvmkit_check_not_equals: +// - If both inputs are X but their sizes differ, they are not equal. This is +// correct, but hard to debug. Consider adding size information to error messages. +// - Consider adding format options (decimal, hex, binary) for expression output. +//------------------------------------------------------------------------------ From da8bade020669c9b57f8b6f7c2861cadebe4f487 Mon Sep 17 00:00:00 2001 From: Peter Monsson Date: Thu, 20 Nov 2025 19:34:55 +0100 Subject: [PATCH 2/2] Fix typos --- CLAUDE.md | 4 ++-- README.md | 4 ++-- src/uvmkit_check.sv | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index a895a3e..688f6e3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -29,7 +29,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co **Design Pattern**: Uses `ifndef` guards to allow external overrides for testing and simulator customization. -#### 2. Assertion Groups (`src/uvmkit_check.sv` lines 100-627) +#### 2. Assertion Groups (`src/uvmkit_check.sv` lines 100-628) Organized by functionality (mirroring JUnit organization): @@ -70,7 +70,7 @@ The library uses preprocessor directives to adapt to different environments: | Simulator | Implementation | Status | |-----------|----------------|--------| -| Questa/VSC/Xcelium | `$stacktrace` | Built-in | +| Questa/VCS/Xcelium | `$stacktrace` | Built-in | | Verilator | Empty macro | No stacktrace support | | Riviera-Pro | User override | Requires `define uvmkit_stacktrace $callstack()` | diff --git a/README.md b/README.md index ed19b0f..fc18abd 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ uvmkit_check provides a familiar and powerful way to validate test conditions in - **JUnit-Inspired API** - Familiar assertion methods for developers coming from software testing - **Universal Compatibility** - Works with and without UVM -- **Multi-Simulator Support** - Verilator, Questa, VSC (Verilab Simvision), and Xcelium +- **Multi-Simulator Support** - Verilator, Questa, VCS, and Xcelium - **Comprehensive Assertions** - Boolean, equality, null checks, identity, real numbers, enums, strings, and UVM objects - **Clean Output** - Only failures are reported; successful assertions pass silently - **Detailed Error Messages** - Clear, actionable feedback when assertions fail @@ -207,7 +207,7 @@ obj_c = new(); // Different object | Simulator | Stacktrace | Method | |-----------|------------|--------| | Questa | ✅ Yes | `$stacktrace` | -| VSC | ✅ Yes | `$stacktrace` | +| VCS | ✅ Yes | `$stacktrace` | | Xcelium | ✅ Yes | `$stacktrace` | | Verilator | ❌ No | Not supported | | Riviera-Pro | ⚙️ Custom | Override `uvmkit_stacktrace` macro | diff --git a/src/uvmkit_check.sv b/src/uvmkit_check.sv index bf7f887..e142b34 100644 --- a/src/uvmkit_check.sv +++ b/src/uvmkit_check.sv @@ -11,7 +11,7 @@ // keeping simulation logs clean and focused on failures. // // All macros can be used directly in testbenches and verification components -// across multiple simulators including Verilator, Questa, VSC, and Xcelium. +// across multiple simulators including Verilator, Questa, VCS, and Xcelium. // // Example Usage: // | `uvmkit_check_true(enable_signal, "Enable must be high") @@ -37,7 +37,7 @@ // assertion failures. This macro is automatically configured for different // simulators: // -// - Questa, VSC, Xcelium: Uses $stacktrace +// - Questa, VCS, Xcelium: Uses $stacktrace // - Verilator: No stacktrace (not supported) // - Other simulators: Can override with custom implementation //