Chapter 13: Modeling Best Practices

LRM Semantic Conventions and Portability

How to read shall, should, may, implementation-defined behavior, errors, transient objects, and portability boundaries in SystemC standards.

How to Read This Lesson

This lesson is for the moments when two experienced engineers argue about whether a behavior is guaranteed, merely allowed, or just what one simulator happens to do. The LRM has a vocabulary for that. Learn it once and your reviews become much calmer.

Source and LRM Trail

Use Docs/LRMs/SystemC_LRM_1666-2023.pdf for the semantic conventions around required behavior, implementation-defined behavior, non-compliant applications, errors, and returned references or pointers. The same style of language appears in the AMS, CCI, UVM-SystemC, and synthesis LRMs. Source examples live in .codex-src/systemc, .codex-src/cci, and .codex-src/uvm-systemc, but source behavior is not automatically a portability guarantee.

Why This Matters

SystemC code often runs on several simulators: the Accellera proof-of-concept library, commercial simulators, vendor HLS tools, and internal platform frameworks. If your model depends on a behavior that the LRM does not require, it may pass today and fail during tool migration.

So before you write a clever workaround, classify the behavior:

  • Required by the LRM: portable model behavior.
  • Implementation-defined: portable only after you check and document the implementation.
  • Unspecified or undefined: do not build architecture around it.
  • Accellera PoC detail: useful for debugging and learning, but not a public contract.
  • Project policy: valid inside your organization, but document it as local guidance.

Reading "Shall", "Should", "May", and "Can"

When the LRM says an implementation shall do something, your model can rely on it. When it says an application shall do something, your code must obey that rule to be compliant.

When the LRM says should, it is a recommendation. A tool may still be compliant if it behaves differently, but you should have a strong reason before ignoring it.

When it says may, the standard allows an option. This is where portability review matters. If an implementation may choose behavior A or B, your model should not silently require A unless you constrain the toolchain.

When it says can, the text is usually explanatory: it tells you what is possible, not what is required.

Implementation-Defined Is Not a Loophole

Implementation-defined behavior means the implementation must define what it does. It does not mean the behavior is universal.

A common example is diagnostic detail. The standard may require an error to be reported, but the exact wording, message ID, stack shape, or recovery action can be implementation-specific.

Good project documentation says:

// Portable rule: unbound mandatory ports are an elaboration error.
// Tool note: our regression parser recognizes Accellera message IDs,
// but the model must not depend on the exact message text.

That comment separates the standard rule from the local automation.

Returned References and Transient Objects

Many SystemC APIs return references or pointers. The LRM carefully describes when those objects are stable and when they are transient.

The practical rule is simple: do not store references to temporary lists, event expressions, or objects whose lifetime is tied to a single expression unless the API explicitly promises that lifetime.

This pattern is safe:

SC_METHOD(on_event);
sensitive << a.value_changed_event();

This pattern deserves review:

const sc_core::sc_event_or_list& events = a.default_event() | b.default_event();

The expression creates helper objects used to describe dynamic sensitivity. Storing a reference to such expression machinery is exactly the sort of lifetime mistake semantic conventions are warning you about.

Source Insight: Why the Rule Exists

In the Accellera implementation, event expressions and sensitivity helpers are represented by C++ objects that help the kernel build sensitivity lists. Some are persistent events; others are temporary expression wrappers. The source makes the standard's warning feel practical: C++ object lifetime rules still apply, even when the syntax looks like hardware notation.

Review Checklist

  • Is this behavior explicitly required by the LRM?
  • If not, is it implementation-defined, unspecified, or merely observed in Accellera source?
  • Are comments clear about which category the behavior belongs to?
  • Does the code store references or pointers returned by APIs with unclear lifetime?
  • Would this model still work on a different compliant simulator?

Comments and Corrections