A RISC-V processor inside an SoC can do more than execute firmware. During pre-silicon verification, it can become part of the stimulus mechanism itself. By running carefully designed C or assembly programmes on the processor, verification teams can exercise boot sequences, memory-mapped peripherals, interrupt paths, system-control registers and other hardware/software interactions through the same architectural interfaces that production software will eventually use.
That is powerful, but simply getting a programme to execute is not sufficient verification.
A useful software-driven test needs a clearly defined verification objective, an observable expected result, a reliable pass/fail mechanism, functional-coverage evidence and enough diagnostic information to investigate failure. Otherwise, a test that appears to pass may prove little more than the fact that some instructions executed.
Key takeaway: use C and assembly as complementary verification stimulus. Use assembly where exact architectural control matters; use C where maintainable system-level behaviour matters. In both cases, make the verification result observable outside the software itself and connect the test to the verification plan.
What software-driven RISC-V SoC verification actually tests
At CPU level, verification asks whether the processor implements its required architectural and microarchitectural behaviour. At SoC level, the question becomes broader: does that processor operate correctly when connected to memories, interconnects, peripherals, interrupt logic and system-control functions?
A test programme loaded into a simulation memory and executed by the processor naturally produces instruction fetches, data accesses and control flows. OpenHW’s CORE-V verification documentation uses this model: its test programmes are normally written in C or RISC-V assembler, translated by a toolchain into machine code and then executed by the RTL processor model. [4]
At SoC level, this makes software useful for checking behaviours such as:
- Reset release followed by successful boot
- Accesses to the intended memory-map regions
- Reads and writes to memory-mapped peripheral registers
- Interrupt generation, routing, entry, servicing and acknowledgement
- Software-visible system-control functions
- Sequences involving several IP blocks rather than one isolated interface
- Representative hardware/software use cases
The important distinction is that software execution is the stimulus mechanism, not the verification argument. Verification still requires an expected result and independent evidence that the hardware behaved correctly.
C or assembly? Choose according to verification intent
There is no general rule that all RISC-V verification software should be written in one language. C and assembly expose different levels of control, and many practical flows use both.
| Need | Assembly is usually stronger when… | C is usually stronger when… |
| Instruction control | Exact instructions or ordering matter | Instruction selection is not the primary test objective |
| Early boot | Minimal runtime assumptions are desirable | Startup infrastructure is already trusted |
| CSR/privileged sequences | Precise architectural operations are required | Small assembly helpers can isolate privileged operations |
| Peripheral flows | Very small deterministic sequence | Longer, maintainable register-level scenarios are required |
| Interrupt scenarios | Entry/return details are under test | Higher-level handler behaviour is under test |
| System use cases | Exact control is more important than maintainability | Several peripherals and software operations must be coordinated |
| Debug | A short instruction sequence is easiest to inspect | Source-level logic makes a longer scenario easier to understand |
| Reuse | Target is deliberately architecture-specific | Common libraries and helper functions reduce duplication |
Assembly is particularly useful during low-level bring-up because it minimises hidden runtime behaviour. If the test objective is an exact privilege transition, trap sequence or instruction pattern, allowing a compiler to choose the final instruction sequence may obscure the thing being verified. C becomes attractive as scenarios grow. A test that configures a peripheral, enables an interrupt, waits for a hardware event, checks status and then verifies resulting memory contents can become much easier to review and maintain in C.
The most useful pattern is often mixed: a small amount of assembly handles startup or architecture-specific operations while C expresses the main SoC scenario. This hardware/software crossover is explicitly part of Alpinum’s structured RISC-V CPU and SoC verification training, where the SoC section uses C and assembler programmes as part of practical integration, self-checking, coverage and debug work.
Build a verification test around the SCOPE model
A software test should begin with a hardware requirement, not with an attractive piece of firmware to run.
A practical way to review each test is the SCOPE model:
| Step | Question the verification engineer should answer |
| S: Specify | What exact hardware behaviour or requirement is being verified? |
| C: Cause | How will C or assembly cause that behaviour through the processor? |
| O: Observe | At what trustworthy boundary can the result be observed? |
| P: Prove pass/fail | What makes the result automatically and unambiguously checkable? |
| E: Evidence | Which coverage and debug artefacts demonstrate what happened? |
Consider an interrupt-routing test.
“Enable an interrupt and see whether software continues” is too weak. A better verification intent would define the interrupt source, routing configuration, expected privilege/handler behaviour, acknowledgement, resulting peripheral state and the coverage points that must be sampled. The programme can configure the hardware and perform the handler-side operations. The testbench can independently observe the interrupt request, relevant interface activity, completion indication and functional-coverage events.
The same framework applies to boot, reset, peripheral access, system control and other software-driven scenarios.
Make pass/fail explicit
A regression should never depend on an engineer reading a console message and deciding that the output “looks right”. A self-checking test should determine whether its expected result occurred and finish through a defined pass/fail protocol. Common mechanisms include a reserved memory-mapped completion location, a status code or a result/signature region monitored by the testbench. OpenHW’s verification environment, for example, includes virtual peripherals for end-of-simulation status and signature output. [4]
riscv-dv demonstrates the same broader principle through a handshake mechanism. Generated assembly can write status information to a configurable signature address monitored by the RTL environment, including explicit test-pass/test-fail information. [7]
Do not let the DUT be its only checker
There is an important limitation to self-checking software: the code performing the check is itself executing on the device under test.
The riscv-tests documentation makes this limitation explicit. Its tests can use branches to compare calculated and expected values, but it warns that self-checks depend on correct operation of the processor instructions used to implement the checking logic. It therefore recommends additional verification mechanisms and also supports result signatures that can be compared against a golden model. [6]
That gives a useful verification principle:
Software may make the first pass/fail decision, but critical results should also be observable from outside the processor.
Depending on the verification objective, that second layer may be:
- An interface monitor
- An assertion
- A scoreboard
- An architectural signature
- An instruction-set reference model
- Retirement-level lockstep comparison
OpenHW’s UVM environment illustrates the stronger model: results checking can be performed in the environment rather than forcing every test programme to establish its own correctness, while an ISS can provide expected architectural behaviour. [5]
For cases where instruction-by-instruction architectural comparison is required, Alpinum’s guide to RISC-V lockstep co-simulation examines that approach in more detail.
Keep the board-support layer separate from the test intent
A C or assembly programme rarely reaches the hardware without some infrastructure. Even a small bare-metal test may depend on startup code, a linker script, memory placement, exception vectors and target-specific addresses. OpenHW describes this relationship explicitly: its board-support package and linker align the test programme with the resources exposed by the verification environment. [4]
That separation matters for debug.
If the verification test silently combines boot infrastructure, target setup and feature checking into one large programme, a failure can be difficult to classify. A cleaner organisation separates:
- Platform/startup infrastructure
- Reusable hardware-access helpers
- The feature-specific stimulus
- The expected-result calculation
- Completion and diagnostic reporting
This also makes it easier to determine whether a failure belongs to the DUT, the test, the linker/memory configuration or the software environment. For a broader view of those dependencies, see Alpinum’s article on the RISC-V software ecosystem.
Connect software tests to functional coverage
A passing programme is evidence of one execution. It is not, by itself, evidence that the verification plan is complete. The useful question is not “How many C tests passed?” but “Which required SoC behaviours have those tests demonstrated?”
A requirement-to-test matrix can make that visible:
| Verification intent | Software action | Independent observation | Useful coverage evidence |
| Reset/boot | Execute from reset into known software checkpoint | Boot address/transactions/completion marker | Reset type × boot path |
| Peripheral access | Read/write selected MMIO registers | Bus/interface monitor and register behaviour | Peripheral × access type × response |
| Interrupt | Configure source and enable path | Interrupt assertion, handler entry and acknowledgement | Source × route × privilege/handler state |
| System control | Programme control register and exercise resulting behaviour | Assertions/interface effects | Control setting × resulting mode |
| Error handling | Cause supported error condition | Trap/status/error interface | Error source × handling path |
| Multi-IP use case | Execute a defined sequence across blocks | End-to-end scoreboard/checkpoints | Use case × subsystem interaction |
Functional coverage should represent verification intent rather than merely count software execution. If a test passes while an expected coverage bin remains unhit, one of several things may be wrong: the stimulus did not create the intended hardware condition, the coverage monitor sampled the wrong point, or the requirement-to-coverage mapping was incomplete.
Alpinum’s separate article on functional coverage for RISC-V verification should continue to own the deeper coverage-modelling subject. For the complementary problem of creating random and directed stimulus to close holes, see RISC-V test generation for coverage closure.
A practical debug workflow for software-driven SoC tests
Software-driven failures can cross several abstraction layers. The fastest debug path is therefore usually to classify the failure before opening thousands of waveform signals.
| Observed symptom | Investigate first | Then inspect |
| Test never reaches completion | reset, boot address, image loading, linker/memory map | traps, PC flow, bus access |
| Early unexpected trap | exception cause, privilege assumptions, memory region | offending instruction and address |
| Wrong result/signature | first incorrect checkpoint | bus transactions, register/CSR state, reference result |
| Interrupt never handled | enable/configuration and source assertion | routing, masking, handler entry and acknowledgement |
| Test passes but coverage remains empty | whether intended condition actually occurred | monitor sampling and coverage definition |
| Assembly passes, equivalent C fails | startup/runtime/compiler-generated sequence | ABI assumptions, memory placement and generated assembly |
| Failure changes with optimisation/build | compiler-visible assumptions and memory access semantics | generated code and ordering |
| Regression hangs intermittently | missing timeout, polling and asynchronous event assumptions | event ordering and uninitialised state |
Preserve enough evidence to reproduce the failure. That normally means retaining the binary/ELF identity, relevant build configuration, simulator configuration, test name, seed where applicable, failure code and the smallest useful execution trace. For architectural failures, finding the first point of divergence is often substantially more useful than analysing the final corrupted state. A reference model or lockstep environment can help with this because it compares state at defined architectural boundaries rather than waiting until the end of the programme.
The same principle appears in OpenHW’s UVM flow, where integrated reference-model checking provides instruction-level comparison intended to improve failure localisation. [5] Alpinum’s analysis of the five places RISC-V projects lose verification time provides the wider programme-level context for model alignment, coverage and system-integration debug.
Common mistakes that weaken C and assembly verification
Treating firmware bring-up as verification
A programme that boots, prints a message and accesses a peripheral can be an excellent bring-up milestone. It is not automatically a verification test.
Verification requires an explicit requirement, expected outcome and pass/fail evidence.
Using C where the exact instruction sequence is the test
Compilers are designed to translate language semantics efficiently, not to preserve an engineer’s preferred instruction sequence.
If exact instructions, privilege transitions, fences or other architectural details are the verification target, assembly or tightly controlled low-level helpers are usually more appropriate.
Making the self-check the only oracle
If a processor defect can also corrupt the branch, load, store or other operation used by the software checker, a false pass becomes possible.
Layer software checking with external observation when the risk justifies it.
Confusing architectural certification with design verification
RISC-V’s current Architectural Certification Tests are assembly-language tests intended to certify architectural implementation. Their own documentation explicitly states that they are not complete verification tests and that additional verification is required. [3]
SoC integration introduces requirements outside architectural certification: memory-map behaviour, peripherals, reset, interrupts, system controls and implementation-specific interactions still need verification.
Allowing infinite polling
Every regression test needs a bounded failure path.
The older riscv-tests framework explicitly recommends a timeout facility so that a test that never completes is classified as a failure rather than leaving the simulation indefinitely active. [6]
Trying to solve every problem in software
Software-driven verification is only one part of the verification environment. Assertions may be better for temporal and protocol relationships. UVM agents may be better for controllable interface stimulus. Reference models may be stronger for architectural checking. Formal methods can explore behaviours that are difficult to reach through simulation.
Alpinum’s guide to formal verification in RISC-V verification covers that complementary evidence source.
What a regression-ready software test should demonstrate
Before adding a C or assembly programme to a sign-off regression, a review should be able to answer these questions:
- Which requirement or verification-plan item does the test address?
- Why is processor-executed software an appropriate way to create the stimulus?
- Why was C, assembly or a mixed implementation chosen?
- Which startup, linker and target assumptions does the test depend on?
- What is the expected result?
- How is pass/fail communicated automatically?
- Is any critical outcome checked independently of DUT-executed software?
- What timeout detects non-completion?
- Which functional-coverage points should the test exercise?
- What evidence is retained when the test fails?
- Can a failure be reproduced with the same binary and environment configuration?
- What does this test deliberately not verify?
That final question is important. Good verification tests have boundaries. A software-driven peripheral test should not silently become evidence that every internal implementation state has been verified.
From software execution to verification evidence
Using C and assembly for RISC-V SoC verification is effective because software can exercise the processor and surrounding hardware through realistic system paths. The engineering challenge is turning that execution into evidence. Assembly provides precise control. C provides scalable system-level expression. Self-checking provides automation. External monitors and reference models strengthen the oracle. Functional coverage shows whether planned behaviour occurred. A disciplined debug path makes failures actionable.
None of these techniques is sufficient alone.
Teams building this capability should therefore treat software-driven testing as one layer of a broader verification strategy rather than as a replacement for UVM, assertions, reference-model checking or formal analysis. For engineers who want to practise this workflow in a structured environment, Alpinum’s RISC-V Verification Training course includes RISC-V C and assembly execution, open-source verification environments, self-checking tests, functional coverage and SoC debug exercises.
References
[1] RISC-V International, “The RISC-V Instruction Set Manual, Volume I: Unprivileged Architecture,” Version 20260120, 2026. [Online]. Available:https://docs.riscv.org/reference/isa/v20260120/unpriv/unpriv-index.html
[2] RISC-V International, “The RISC-V Instruction Set Manual, Volume II: Privileged Architecture,” Version 20260120, 2026. [Online]. Available: https://docs.riscv.org/reference/isa/v20260120/priv/priv-index.html
[3] RISC-V International, “RISC-V Architectural Certification Tests,” riscv-arch-test, 2026. [Online]. Available:https://github.com/riscv/riscv-arch-test. The current ACT documentation explicitly distinguishes architectural certification from complete processor verification.
[4] OpenHW Group, “Test Programs — CORE-V Verification Strategy,” 2026. [Online]. Available:https://docs.openhwgroup.org/projects/core-v-verif/en/latest/test_programs.html
[5] OpenHW Group, “UVM Testcases in the CORE-V-VERIF Environments,” 2026. [Online]. Available:https://docs.openhwgroup.org/projects/core-v-verif/en/latest/uvm_tests.html
[6] RISC-V Software Source, “riscv-tests,” [Online]. Available: https://github.com/riscv-software-src/riscv-tests. The documentation covers explicit pass/fail, self-checking limitations, result signatures and timeout requirements.
[7] CHIPS Alliance, “riscv-dv: Random Instruction Generator for RISC-V Processor Verification,” [Online]. Available: https://github.com/chipsalliance/riscv-dv. Its documented capabilities include functional coverage, testbench handshaking, hand-coded assembly, interrupt/debug handling and ISS co-simulation.
[8] CHIPS Alliance, “RISCV-DV Testbench Handshake,” [Online]. Available: https://github.com/chipsalliance/riscv-dv/blob/master/docs/source/handshake.rst
FAQs
Both are useful. C is well suited to longer system-level scenarios and peripheral flows, while assembly provides tighter control over instructions, startup and architecture-specific behaviour. Many verification flows benefit from combining them.
A self-checking test evaluates an expected result automatically and produces an unambiguous pass/fail indication. For stronger verification, important outcomes should also be observable outside the DUT through a monitor, signature, assertion, scoreboard or reference model.
Each test should map to defined verification-plan scenarios or requirements. Coverage should record whether the intended hardware behaviour occurred, rather than simply recording that a C or assembly programme executed.
No. The ACT project itself states that its architectural certification tests are not complete verification tests. SoC-specific integration, peripherals, reset, interrupts, system controls and implementation-specific behaviours require additional verification.
It is particularly useful when architectural state must be compared independently and when locating the first incorrect retired instruction is more informative than observing only a final pass/fail result.
First determine whether the test reached reset release, its boot address and expected checkpoints. Then inspect traps, memory-map assumptions, interrupt/event dependencies and polling loops. Every regression test should also have a bounded timeout.

Written by : Mike Bartley
Mike started in software testing in 1988 after completing a PhD in Math, moving to semiconductor Design Verification (DV) in 1994, verifying designs (on Silicon and FPGA) going into commercial and safety-related sectors such as mobile phones, automotive, comms, cloud/data servers, and Artificial Intelligence. Mike built and managed state-of-the-art DV teams inside several companies, specialising in CPU verification.
Mike founded and grew a DV services company to 450+ engineers globally, successfully delivering services and solutions to over 50+ clients.
Mike started Alpinum in April 2016 to deliver a range of start-of-the art industry solutions:
Alpinum AI provides tools and automations using Artificial Intelligence to help companies reduce development costs (by up to 90%!) Alpinum Services provides RTL to GDS VLSI services from nearshore and offshore centres in Vietnam, India, Egypt, Eastern Europe, Mexico and Costa Rica. Alpinum Consulting also provides strategic board level consultancy services, helping companies to grow. Alpinum training department provides self-paced, fully online training in System Verilog, UVM Introduction and Advanced, Formal Verification, DV methodologies for SV, UVM, VHDL and OSVVM and CPU/RISC-V. Alpinum Events organises a number of free-to-attend industry events
You can contact Mike (mike@alpinumconsulting.com or +44 7796 307958) or book a meeting with Mike using Calendly (https://calendly.com/mike-alpinum-consulting).
Stay Informed and Stay Ahead
Latest Articles, Guides and News
Explore related insights from Alpinum that dive deeper into design verification challenges, practical solutions, and expert perspectives from across the global engineering landscape.









