Portable stimulus standard (PSS) is very interesting. That said, It’s surprising simple.

These are important abstraction units specific by the spec:

  • component
  • action
  • Activity

Component Link to heading

Component is a way to encapsulate other elements (mainly actions)

Components serve as a mechanism to encapsulate and reuse elements of functionality in a portable stimulus model. Typically, a model is broken down into parts that correspond to roles played by different actors during test execution. Components often align with certain structural elements of the system and execution environment, such as hardware engines, software packages, or testbench agents.

The example from the spec, we have 2 component and action in the top pss_top component. The action entry uses the actions from sub-components.

component usb_c {
action write {...}
}
component uart_c {
action write {...}
}
component pss_top {
uart_c s1;
usb_c s2;
action entry {
uart_c::write wr; //refers to the write action in uart_c
...
}
}

Actions Link to heading

The spec describes action as follows:

Actions are a key abstraction unit in PSS. Actions serve to decompose scenarios into elements whose definitions can be reused in many different contexts. Along with their intrinsic properties, actions also encapsulate the rules for their interaction with other actions and the ways to combine them in legal scenarios. Atomic actions may be composed into higher-level actions, and, ultimately, to top-level test actions, using activities (see Clause 13)

Well, Action can be either atomic which does specific job like read and write. Compound action is something like the example below where it combines other actions to describe the scenario.

action sub_a {...};
action compound_a {
sub_a a1, a2;
activity {
a1;
a2;
}
}

Activity Link to heading

The spec defines activity as follows:

When a compound action includes multiple operations, these behaviors are described within the action using one or more activity statements. An activity specifies the set of actions to be executed and the scheduling relationship(s) between them

There are several statements that can be used with action. One example is action traversal with the following example.

An action traversal statement designates the point in the execution of an activity where an action is randomized and evaluated (see Syntax 49 and Syntax 50). The action being traversed may be specified via an action handle referring to an action field that was previously declared or the action being traversed may be specified by type, in which case the action instance is anonymous.

action A {
rand bit[3:0]
...
}
f1;
action B {
    A a1, a2;
    activity {
        a1;
        a2 with {
        f1 < 10;
        };
    }
}

Action inferencing Link to heading

Well, did iIsay PSS is simple? maybe not.

Action inference is confusing, but I guess it is the selling point of PSS.

Perhaps the most powerful feature of PSS is the ability to focus purely on the user’s verification intent, while delegating the means to achieve that intent. Previous clauses have introduced the semantic concepts to define such abstract specifications of intent. The modeling constructs and semantic rules thus defined for a portable stimulus model allow a tool to generate a number of scenarios from a single (partial) specification to implement the desired intent.

In a scenario description, the explicit binding of outputs to inputs may be left unspecified. In these cases, an implementation shall execute a scenario that reflects a valid completion of the given partial specification in a way that conforms to pool binding rules. If no valid scenario exists, the tool shall report an error. Completing a partial specification may involve decisions on output-to-input binding of flow objects in actions that are explicitly traversed. It may also involve introducing the traversal of additional actions, beyond those explicitly traversed, to serve as the counterpart of a flow object exchange. The introduction of an action in the execution of a scenario to complete a partially specified flow is called action inferencing. Action inferences are necessary to make a scenario execution legal if the following conditions hold: a) An input of any kind is not explicitly bound to an output, or an output of stream kind is not explicitly bound to an input. b) There is no explicitly traversed action available to legally bind its output/input to the unbound input/ output, i.e.,

  1. There is no action that is or may be scheduled before the inputting action in the case of buffer or state objects.
  2. There is no action that is or may be scheduled in parallel to the inputting/outputting action in the case of stream objects.

Test Realization Link to heading

The moment we all waiting for. Why does PSS even do?

If you have scenario inferred already (all the actions and activity magic is done), One way to realize a test is using a template. Again, example from the spec as follows:

component top {
struct S {
rand int b;
}
action A {
rand int a;
rand S
s1;
exec body C = """
printf("a={{a}} s1.b={{s1.b}} a+b={{a+s1.b}}\n");
""";
}
}

body is one the exec, This is description for the rest:

The following list describes the different exec block kinds: — pre_solve—valid in action, flow/resource object, and struct types. The pre_solve block is pro- cessed prior to solving of random-variable relationships in the PSS model. pre_solve exec blocks are used to initialize non-random variables that the solve process uses. See also 17.4.10. — post_solve—valid in action, flow/resource object, and struct types. The post_solve block is pro- cessed after random-variable relationships have been solved. The post_solve exec block is used to compute values of non-random fields based on the solved values of random fields. See also 17.4.10. — body—valid in action types. The body block constitutes the implementation of an atomic action. The body block of each action is invoked in its respective order during the execution of a sce- nario—after the body blocks of all predecessor actions complete. Execution of an action’s body may be logically time-consuming and concurrent with that of other actions. In particular, the invoca- tion of exec blocks of actions with the same set of scheduling dependencies logically takes place at the same time. Implementation of the standard should guarantee that executions of exec blocks of same-time actions take place as close as possible. — run_start—valid in action, flow/resource object, and struct types. The run_start block is a proce- dural non-time-consuming code block to be executed before any body block of the scenario is invoked. It is used typically for one-time test bring-up and configuration required by the context action or object. exec run_start is restricted to pre-generation flow (see Table 24). — run_end—valid in action, flow/resource object, and struct types. The run_end block is a proce- dural non-time-consuming code block to be executed after all body blocks of the scenario are com- pleted. It is used typically for test bring-down and post-run checks associated with the context action or object. exec run_end is restricted to pre-generation flow (see Table 24). — init_down/init_up(init)—valid in component types. The init_down and init_up blocks are used to assign values to component attributes and to initialize foreign language objects. Component init_down and init_up blocks are called before the scenario root action’s pre_solve block is invoked. init_down and init_up blocks may not call target template functions.

  1. init_down—Starting with the root component, init_down blocks are evaluated top-down for each component in the hierarchy. The relative order of evaluating init_down blocks for compo- nents at the same level of hierarchy is undefined. For any component, the init_down block shall be evaluated before its init_up block is evaluated.
  2. init_up—For a leaf-level component (i.e., one that does not instantiate any subcomponents), the init_up block shall be evaluated after its init_down block (if any). A parent component’s init_up block shall be evaluated only after all subcomponent init_up blocks have been evalu- ated. — header—valid in action, flow/resource object, and struct types. The header block specifies top- level statements for header declarations presupposed by subsequent code blocks of the context action or object. Examples are ‘#include’ directives in C, or forward function or class declara- tions. — declaration—valid in action, flow/resource object, and struct types. The declaration block speci- fies declarative statements used to define entities that ar

There is another thing called Foreign procedural interface, but I am too old for this stuff.