This document describes the complete program testing framework that allows testing entire C programs compiled to RISC-V machine code on the Vigna processor.
The complete program testing framework extends the existing instruction-level tests to support:
- C program compilation to RISC-V RV32I machine code
- Execution of complete programs on the processor
- Verification of program results through memory-mapped I/O
- Performance measurement through cycle counting
vigna/
├── programs/ # C test programs
│ ├── Makefile # Build system for C programs
│ ├── simple_test.c # Basic arithmetic and control flow
│ ├── fibonacci_simple.c # Fibonacci sequence calculation
│ ├── sorting_test.c # Bubble sort algorithm
│ └── build/ # Generated files
│ ├── *.elf # Compiled ELF binaries
│ ├── *.bin # Raw binary (text section)
│ ├── *.mem # $readmemh format
│ └── *.vh # Verilog include format
├── sim/
│ └── program_testbench.v # Complete program testbench
└── tools/
└── bin_to_verilog_mem.py # Binary to Verilog converter
The programs are automatically built using the RISC-V cross-compiler:
cd programs
make all # Build all programs
make clean # Clean generated files
make disasm # Generate disassembly for debuggingPrograms are compiled with the following settings:
- Target:
rv32i(RISC-V 32-bit base integer instruction set) - ABI:
ilp32(32-bit integers, longs, and pointers) - No standard library (
-nostdlib -nostartfiles) - Optimization:
-O2
Tests basic processor functionality:
- Arithmetic operations (addition)
- Loop execution (for loop with accumulation)
- Conditional execution (ternary operator)
- Memory writes
Expected Results:
- test_output[0] = 30 (10 + 20)
- test_output[1] = 15 (1+2+3+4+5)
- test_output[2] = 20 (max of 10, 20)
- test_output[3] = 0xDEADBEEF (completion marker)
Tests iterative computation without arrays:
- Variable assignments and updates
- Loop-based calculation
- Sequential memory writes
Expected Results:
- test_output[0..7] = {0, 1, 1, 2, 3, 5, 8, 13} (Fibonacci sequence)
- test_output[8] = 0x12345678 (completion marker)
Tests more complex algorithms:
- Nested loops
- Array operations
- Conditional swapping
Expected Results:
- test_output[0..4] = {1, 2, 5, 8, 9} (sorted from {5, 2, 8, 1, 9})
- test_output[5] = 0xABCDEF00 (completion marker)
Programs use memory-mapped I/O for result verification:
- Test Output Base: 0x1000
- Memory Mapping: Address 0x1000+N maps to
data_memory[N/4]in testbench - Data Width: 32-bit words
# Quick test (no waveforms)
make program_quick_test
# Full test with waveforms
make program_test
# Syntax checking
make program_syntax
# View waveforms
make program_waveThe testbench provides detailed output showing:
- Program loading status
- Execution cycle count
- Memory contents after execution
- Pass/fail status for each test case
Example output:
Starting Complete Program Tests for Vigna Processor
=================================================
Loading simple_test program...
Simple test program loaded successfully
Running program: Simple Arithmetic Test
Program halted at PC=0x00000034 after 54 cycles
Verifying simple test results...
PASS: Arithmetic test result = 30 (expected 30)
PASS: Loop test result = 15 (expected 15)
PASS: Conditional test result = 20 (expected 20)
PASS: Completion marker = 0xdeadbeef (expected 0xDEADBEEF)
Complete Program Test Summary:
=============================
Tests Passed: 13
Tests Failed: 0
Total Tests: 13
All complete program tests PASSED!
The complete program tests verify that the Vigna processor correctly implements:
- Load/Store Operations: Memory access with proper addressing
- Arithmetic Operations: ADD, ADDI with correct results
- Logical Operations: Bitwise operations and comparisons
- Control Flow: Loops, branches, and program termination
- Immediate Handling: Proper sign extension and value loading
- Memory Interface: Correct read/write operations
- PC Management: Sequential execution and branch targets
- Register File: Data storage and retrieval
- Pipeline: Instruction fetch, decode, and execute phases
- Cycle Counts: Actual vs. expected execution time
- Memory Latency: Data access timing
- Branch Prediction: Control flow efficiency
- Use
make disasmto view generated assembly - Check that target architecture matches processor capabilities
- Verify that no unsupported instructions are generated
- Enable debug output in testbench for memory trace
- Check waveforms for signal timing
- Verify memory address mapping
- Compare actual vs. expected memory contents
- Check completion markers to ensure full execution
- Verify cycle counts are reasonable
To add new test programs:
- Create new C file in
programs/directory - Add program name to
PROGRAMSvariable inprograms/Makefile - Add load task and verification task to
program_testbench.v - Update test sequence in main initial block
- Use memory-mapped I/O at base address 0x1000
- Include completion marker for reliable termination detection
- Avoid standard library functions
- Keep stack usage minimal
- Use volatile pointers for memory-mapped I/O
The complete program tests complement the existing instruction-level tests:
- Instruction Tests: Verify individual instruction behavior
- Program Tests: Verify complete software functionality
- Combined Coverage: Ensures both hardware correctness and software compatibility
This provides comprehensive verification from basic instruction execution to real-world software functionality.