Home
  • Products
  • Solutions
  • Support
  • Company

This search text may be transcribed, used, stored, or accessed by our third-party service providers per our Cookie Policy and Privacy Policy.

This search text may be transcribed, used, stored, or accessed by our third-party service providers per our Cookie Policy and Privacy Policy.

  • Products
  • Solutions
  • Support
  • Company
Community Blogs Verification > Using PSS Registers with Perspec for Portable Programming…
ZeevK
ZeevK

Community Member

Blog Activity
Options
  • Subscribe by email
  • More
  • Cancel
Perspec
perspec system verifier
pss

Using PSS Registers with Perspec for Portable Programming Sequences

28 Apr 2025 • 6 minute read

When you use Cadence’s Perspec System Verifier and the Portable Test and Stimulus Standard (PSS) to model your system, you will likely need a way to operate on the memory-mapped registers of various programmable devices within your system. This blog will guide you through the following processes:  

  • Creating your PSS register model (or translating existing register metadata to PSS using Perspec’s pss_gen utility)
  • Operating on the PSS registers in your PSS model 

Why Bother Modeling the Registers? 

Accessing the registers in your system is crucial for creating meaningful test scenarios. Configuring the system, driving traffic through it, and checking its behavior will all involve register reads/writes. Most verification methodologies and languages have one or more Register Abstraction Layer (RAL). For example, C/C++ utilize structs, unions and macros while UVM introduces the UVM register model.

One of the main benefits of a PSS register model is the abstraction and portability of PSS. You can use PSS to write portable programming sequences or drivers, which can then be translated by Perspec into executable code depending on the target platform on which the test will run. The same PSS programming sequence can be generated into different executable code for each of the following platforms your project might use:

  • A UVM-based testbench (simulation)
  • A Palladium-based testbench (acceleration/emulation)
  • The actual system itself (post-silicon)

As you can see, the potential for reuse and verification efficiency is vast.

Creating the PSS Register Model

The PSS LRM defines a core library that supports the modeling of registers. This includes among other things:

  • Base component types for registers and register-groups: addr_reg_pkg::reg_c and addr_reg_pkg::reg_group_c
  • Facilities to nest registers and groups of registers, with controlled offsets
  • Address assignment to map instances of register-groups in the system’s address-space

The following code snippets show a simple example. It begins with a struct that declares the inner-structure of a register including all its fields in the correct packing order (as this is a packed data structure):

struct my_reg_s : std_pkg::packed_s<> {
  bit [16] source; // bits 15:0
  bit [16] target; // bits 31:16
}

Next, a register component type is declared by specializing the core-library’s reg_c template type. It takes three parameters: the struct type, an access-mode enum (READWRITE, READONLY, WRITEONLY) and the size of the register:

pure component my_reg_c : reg_c<my_reg_s, READWRITE, 32> {};

Finally, here is a register group that instantiates the register above. The core library is used by deriving from a base register-group type and implementing its functions:

pure component my_reg_grp_c : reg_group_c {
  my_reg_c reg0;
  my_reg_c reg_array[8];

  function bit[64] get_offset_of_instance(string name) {
     if (name == "reg0") { return 0x0; }
     else { error("Invalid register-instance name"); }
  }


  function bit[64] get_offset_of_instance_array(string name, int index) {
     if (name == "reg_array") { return (4 + index*4); }
     else { error("Invalid register-array name"); }
  }

}

The two functions get_offset_of_instance() and get_offset_of_instance_array() are called by the PSS tool (Perspec) to compute the offset of each register instance relative to the base-address of this register group. The LRM defines additional functions that can be implemented for each register group. Register groups can also contain other register groups, forming a tree of registers.

As you can see above, declaring the register model requires a significant amount of coding. However, all the above code can be automatically generated from your existing register metadata, specifically if you have an IP-XACT description of your system. See the last section of this blog for more details.

Instantiating Top Register Groups

Once the register model is defined, you can instantiate the top register group(s) and map them to a memory address that corresponds with your system’s memory map. The code below instantiates a sample register group and calls its set_handle() function to assign an address-handle to it. This handle serves as the base address for the register group and is used along with the relative offsets, to calculate the concrete address of each register. The address handle is typically computed from a non-allocatable address-region declared in the system and is omitted for simplicity.

component my_component_c {
  my_reg_grp_c grp0;

  exec init_down {
     grp0.set_handle(…);
  }

}

Operating on Registers

The PSS core library defines functions to access registers at run-time, allowing you to read and write their values. These functions are immediately usable as soon as you create the PSS register model. The following example shows a target-function declaration that will be converted into executable code on the target platform. This function increments an instance of my_reg_c register. It reads the register value using the read() core-library function, increments both its fields, and then writes back the updated values using the write() core-library function:

extend component my_component_c {
  target function void increment_my_reg( ref my_reg_c reg) {
    my_reg_s st;
    st = reg.read();
    st.source += 1;
    st.target += 1;
    reg.write(st);
  }

}

Note the abstraction, reusability, and safety of this coding style:

  • The function uses high-level PSS syntax, which is easy to create and maintain over time. It will be translated to low-level executable code by Perspec as part of the generated test.
  • A register reference of the appropriate type is used as an argument, ensuring proper type-checking and access to inner members.
  • The generated code is correct by design, eliminating the risk of accessing the wrong register type/instance if plain addresses were passed.


Finally, the following shows an action that uses the function to increment the values of all the registers within our register group:

extend component my_component_c {
action incr_regs {

  exec body {
    comp.increment_my_reg (comp.grp0.reg0);
    foreach (r: comp.grp0.reg_array) {
      comp.increment_my_reg(r);
    }
  }
}

Automatic Model Creation from IP-XACT

It is likely that your register's information is contained within the memory map entities of IP-XACT files (IP-XACT is an IEEE standard for defining the structure of IPs). Perspec provides a utility that converts IP-XACT description into the equivalent PSS register model called pss_gen, which is delivered with the Perspec release. The pss_gen utility extracts all register fields along with their defined attributes (such as bit width, offset, reset values, etc.) and creates an equivalent standard PSS register model.

The following example shows a simple invocation of pss_gen using two parameters: The first parameter(-top) specifies the input IP-XACT file, and the second parameter (-dut_name) provides the name of the DUT top component. In this example, the output PSS file will be named uart.pss and will be written to the current directory. The pss_gen utility has more advanced options (such as supporting hierarchical IP-XACT, etc.), all of which are documented in Cadence Support Center:  

`perspec -home` / sia / bin / pss_gen -top uart.xml -dut_name uart

Conclusion

If you are using PSS, you probably realized the potential of creating system-level scenarios in a very different way than before. Instead of relying on hardcoded, directed tests, a PSS tool allows you to randomize both the contents of the scenario and their scheduling.

Similarly, instead of hand-written programming sequences that access various registers in your system, the PSS tool can generate all of that for you, compatible with multiple different execution platforms. All you need to do is create a simpler, higher-level abstraction code just once. Are you ready to give it a try?

© 2025 Cadence Design Systems, Inc. All Rights Reserved.

  • Terms of Use
  • Privacy
  • Cookie Policy
  • US Trademarks
  • Do Not Sell or Share My Personal Information