How to Create Test Sequences for RISC-V Cores and SoCs Automatically
For decades, the idea of an open-source central processing unit (CPU) core was virtually unknown—let alone using it for commercial applications. The processor has been the most critical part of any computing system, from SystemRDL to RTL, and, thus, the most valuable and profitable. Over the years, many large companies have built their empires from developing CPUs as part of their systems, offering CPU cores as intellectual property (IP), or licensing their instruction set architecture (ISA).
Today, the entire computing industry is in the middle of a paradigm shift where the traditional CPU might no longer be the main processing element. This is mainly driven by the exploding growth of data produced and consumed in our global society. Big data is used for prediction, analytics, and machine learning training. Fast data is used for real-time applications such as Internet of Things (IoT) edge, geo-spatial systems, and autonomous vehicles.
Data is taking center stage, and the computing architecture of the future must be data-centric. The traditional CPU is only one type of computing hardware, along with graphic processing units (GPUs), custom accelerators, and special-purpose CPUs. In complex system-on-chip (SoC) devices, all these processing elements are interconnected, along with memory and storage, to an open-source interface, all powered by an open-source ISA.
The History and Rise of RISC-V
The ISA that is poised to be the key enabler for all of this is RISC-V. Ever since a group at UC Berkeley completed RISC-V and released it as open source in 2014, the industry has been changing dramatically. The flexibility of the RISC-V ISA enables its use in many different applications and computing architectures. The RISC-V Foundation was started in 2015 and now has hundreds of member organizations, ensuring that the world’s first open ISA is proving useful and being adopted widely.
Western Digital Corporation (WDC), one of the founding leaders of the RISC-V Foundation, released a commercial-grade open-source CPU core based on RISC-V called VeeR™. This 32-bit CPU supports RISC-V integer, multiplication, division, and compression instructions as well as instruction-fetch fence and CSR instruction extensions. The core is a 9-stage, dual-issue, superscalar pipeline.
The spec, design source code, and UVM testbench for VeeR™ are available on Github. The testbench is quite limited for commercial applications, so if you’re planning to incorporate the VeeR™ core into your commercial SoC, a more robust hardware verification approach is needed. At a minimum, you will need the test scenarios and test environments for simulation using the Universal Verification Methodology (UVM), firmware tests, and board testing in the bringup lab. For stimulus, typically you need:
SystemVerilog/UVM sequences for simulation
C/C++ based sequences for firmware tests
C/C++ based sequences for hardware/board tests
One of the main challenges in creating these sequences is that multiple engineers with varying types of expertise are needed to write them in multiple target languages, even though the sequence functionality is the same. Before they can even code the sequences, the engineers must first understand the test spec, which in itself requires a significant amount of time and effort.
How to Efficiently Generate Sequences?
In this post, we show how you can describe the sequences in pseudo-code only once, and use IDS-Verify™ and IDE-Validate™ to generate the sequences in the target languages automatically. These two components are part of Agnisys IDesignSpec™ Suite, the industry’s best solution for developing correct-by-construction IP and SoC designs from executable golden specifications.
To demonstrate this, we’ve chosen VeeR's Programmable Interrupt Controller (PIC). The PIC is responsible for evaluating all pending/enabled interrupt requests and picking the highest-priority request with the lowest interrupt source ID. Section 5.5, Theory of Operation, of the VeeR specification describes the sequences for initialization and regular operation.
Sequences mainly consist of various register read/writes that are executed in series or in parallel. First, you need to make sure that you have the VeeR Control Status Register (CSR) set properly defined. Figure 2 shows the CSR definition in spreadsheet format using the IDesignSpec™ Suite. VeeR has 14 unique registers, of which a few such as ‘meigwctrl’ and ‘meipl’ are simply repeated twice.
The corresponding register-transfer-level (RTL) design, UVM register model, C/C++ headers, and documentation can be easily and automatically generated from a single source using IDesignSpec GDI™ or IDS-Batch CLI™. Whenever your specification changes, you can simply re-generate all the output files. By capturing the CSR in this manner, you will be able to minimize SoC functional flaws caused by errors or changes in the specification.
Figure 2: Example of SystemRDL
PIC Core Initialization and Regular Operation Sequences
You can specify hierarchical sequences in pseudo-code using text documents, spreadsheets, or our intuitive sequence editor. Language constructs such as ‘if-else’, ‘while’, and ‘for’ can be used, as well as ‘fork-join’ statements for concurrent sequences. Similarly, randomization and constraints for fields and variables are also supported. There are five main sections used for describing the sequences:
IP – You specify the absolute or relative path of the register map defined in IP-XACT, System RDL, RALF, document, or spreadsheet. You can provide a general description of the sequence and define properties used for the entire sequence.
Arguments – You define the variables or expressions passed from the caller of a macro sequence (or function) into another macro sequence. The macro sequence accepts the argument, and returns a sequence back to the calling sequence, establishing two-way communication.
Constants – You specify the number format (binary, decimal, hexadecimal, etc.) for the value of constants. Values that won’t alter during execution can be described here.
Variables – You declare variables with or without values. IDS-Verify and IDS-Validate substitute the body of the variable at each point where the variable is called at run time.
Sequence Steps – You specify the steps that must be performed sequentially.
Figure 3: Example of PSS Sequences
Generation of Sequences in Multiple Languages
A number of problems can occur during the creation of the sequences, so IDS-Verify and IDS-Validate are equipped with a smart syntax and semantic checker for validating the format and syntax within the specification. Once the checks are passing, you can generate the output sequences in the target languages. In this example, the following outputs are generated:
UVM sequence – This package is used for UVM-based simulation. We create sequence classes that are extended from ‘uvm_reg_sequence’ with arguments handled using the ‘init´ function. Read/write transactions on registers occurs via register model ‘rm’ inside the task body.
C/C++ sequence – This package is used for firmware tests. We create functions with a ‘return type’ that you can change in the configuration settings. We perform register and field reads/writes through default APIs that we provide. You can also use your own APIs by adding templates in the configure section.
Platform sequence – This package is used for board testing. You can specify the board to use for testing, the base address of the IP implemented in the board, APIs used for writing/reading the registers, and pre-defined initialization and clean-up functions. Based on the board type, the requisite libraries are automatically included in the generated C/C++ code. Once generated, the sequences are ready to run on the board.
Specifying the sequences in a single golden source specification allows our code generator to retarget them in various languages. Any changes to the sequences must be done in the spec as opposed to making them in the output code. This prevents any discrepancies between the sequence functionality used in simulation, firmware tests, and board testing.
There are numerous other IP blocks within the VeeR core, and you can also specify the sequences for these and generate sequence code automatically using our solution. As you integrate the VeeR core into your SoC you will also have off-chip IPs connected to the core through a system bus such as AXI4 or AHB-Lite. These standard buses are supported by our tools such that the bus interface logic is also auto-generated along with the RTL and UVM register model.
RISC-V: The Superior Choice
The world is producing an unbelievable amount of data at an ever-increasing rate. High-quality data is now a valuable commodity that could potentially become more useful than oil as it can be reused, replicated, and transferred at the speed of light to power many AI applications that will simplify our daily life. Open-source interfaces and open-source CPU cores are now evolving to become more data-centric.
RISC-V has clearly emerged as the ISA of choice for this evolution, and our IDesignSpec Suite can help you adopt and integrate processor cores such as VeeR. Please contact us for more details about the full package of input and output files mentioned in this post, or to learn more about Agnisys and our products.