If you’re new to PSS you could be forgiven for thinking that it automagically makes stimulus reusable, vertically from IPs to systems, horizontally between derivatives and between hardware-based and software-based testing. From a big-picture point of view these are certainly all potential benefits of PSS.
What PSS does provide is a standardized way to describe test intent declaratively. The declarative part is important; this means that you describe what should be tested rather than the detailed mechanics of how it should be tested. This abstracts the test intent from the test implementation, providing that standardized mechanism which should cover the needs of many possible types of testing.
This is not unlike the benefits that derive from object-oriented programming. Through complexity-hiding in classes you can hide the details of how something is implemented, say rotating a graphical image, so that an object based on such a class can easily be rotated without a user of that class having to worry about the details. Object-oriented programming (OOP), in languages like C++ or Java, gives you the mechanisms to do that hiding, but there’s nothing magical in delivering the underlying methods. You (or someone) still has to flesh out the implementation; it’s just more easily reusable across applications. If you don’t think carefully about reusability, there can be limited gain in using OOP. You may benefit in your own code but no-one else will.
The same applies to PSS, but hardware verification is an inherently very collaborative exercise so reuse across multiple applications is even more important, vertically, horizontally and between hardware-driven and software-driven testing. This means that the implementation layer – the connection between PSS models/scenarios and the various testing targets (IP/subsystem/SoC or UVM/C++) must be designed with reuse in mind, tuned to your verification methodology and between these targets.
Matthew Ballance (PE and PSS technologist at Mentor) has written a very nice white-paper on this topic. As a sidebar, I write quite a lot so I’m always working at improving my skills. I also read a lot of material written by others. Quite a lot of what I read gets the job done but can be challenging to absorb because it feels like it was written by someone for whom writing does not come naturally. I have to tip my hat to Matthew as a polished writer, easy to read and to get his point across in one pass.
But I digress. Advice in the paper breaks down into a few main sections: Identifying opportunities for reuse in existing testing code (SV or C++), building reusable PSS libraries, reusable data generation and checking, and making test realization reusable. In the first section he suggests looking at constraints, certainly SV constraints which have a global nature such as for configuration classes and operation modes. He also suggests looking at memory maps which can equally provide a source of PSS constraints. In test realization (the implementation topic above), you’re very likely to have standard UVM routines to perform basic operations on an IP such as configuration, and read and write. These can be reused in the PSS test realization. He also shows an example for a similar C++ access function.
(Tricky question here. Do you move that function to PSS? Probably not, because you may still need to use it in standalone UVM. So you have a wrapper in PSS and reference the UVM function. But then how do you ensure someone doesn’t change the function in UVM and break the PSS dependency? Clearly you need careful configuration management here.)
On building reusable PSS libraries, he points out that the standard is too new to provide already a library of predefined types and other objects. Those will presumably come in time; for now he makes some suggestions on some basic types you should consider. For checking he suggests something that seems self-evident, but will no doubt require care in design. When you’re building checks for an IP, separate those that will be globally meaningful from those that only have local relevance. The global checks are the ones you will want to use at the SoC level.
The last and longest section is on reusable test realization. Here he suggests you first focus on a common API that can be reused at the SV level and at the C++ level. Mentor offers a Micro-Executor (UEX) API at the C-level to simplify this task. The goal of this package is to provide a standardized realization interface to memory management, interrupt handling and thread handling, with realizations for SV/UVM and C at bare-metal and OS levels. This makes sense to me – not trying to bridge the whole gap between hardware-driven and software-driven interfaces in a standard library but rather bridging part of that gap.
This is obviously a complex topic that can’t be boiled down to a short how-to guide, but this seems like a good starting point. You can download the white-paper HERE.