Overview
SPURS (SPU Runtime System) is a high-level framework that provides sophisticated task scheduling and workload management across multiple SPUs. It abstracts away low-level thread management and provides automatic load balancing and priority scheduling.
SPURS is ideal for complex applications with dynamic workloads, multiple task types, and varying computational demands. For simple, static SPU workloads, direct SPU thread groups may be more appropriate.
What is SPURS?
SPURS provides:
Automatic Load Balancing Distributes tasks across available SPUs dynamically
Priority Scheduling Supports multiple priority levels for task execution
Task Preemption Higher priority tasks can interrupt lower priority ones
Resource Management Manages SPU thread groups and system resources
SPURS Architecture
┌─────────────────────────────────────────┐
│ Application Layer │
│ (Task submission, job management) │
└─────────────────────────────────────────┘
│
┌─────────────────────────────────────────┐
│ SPURS Framework │
│ - Task scheduling │
│ - Load balancing │
│ - Priority management │
└─────────────────────────────────────────┘
│
┌─────────────────────────────────────────┐
│ SPU Thread Group Layer │
│ (Managed automatically by SPURS) │
└─────────────────────────────────────────┘
│
┌─────────┴─────────┐
│ │
┌───▼───┐ ┌────▼───┐
│ SPU 0 │ ... │ SPU 5 │
└───────┘ └────────┘
Basic SPURS Usage
Initialization
Initialize SPU System
#include <sys/spu.h>
// Initialize 6 SPUs (no raw SPUs)
s32 ret = sysSpuInitialize ( 6 , 0 );
if (ret) {
printf ( "SPU initialization failed: %08x \n " , ret);
return ret;
}
Allocate SPURS Instance
#include <spurs/spurs.h>
#include <malloc.h>
// Allocate aligned SPURS structure
Spurs * spurs = (Spurs * ) memalign (SPURS_ALIGN, sizeof (Spurs));
if ( ! spurs) {
printf ( "Failed to allocate SPURS instance \n " );
return - 1 ;
}
Configure SPURS Attributes
SpursAttribute attr;
// Initialize attribute structure
ret = spursAttributeInitialize ( & attr ,
6 , // Number of SPUs
250 , // SPU thread group priority
200 , // PPU thread priority
true // Exit if no work
);
if (ret) {
printf ( "Attribute initialization failed: %08x \n " , ret);
free (spurs);
return ret;
}
Set Optional Attributes
// Set name prefix for debugging
ret = spursAttributeSetNamePrefix ( & attr ,
"MyApp" , strlen ( "MyApp" ));
// Enable SPU printf (if available)
ret = spursAttributeEnableSpuPrintfIfAvailable ( & attr );
// Set thread group type (optional)
ret = spursAttributeSetSpuThreadGroupType ( & attr ,
SPU_THREAD_GROUP_TYPE_NORMAL);
Initialize SPURS
// Initialize SPURS with attributes
ret = spursInitializeWithAttribute (spurs, & attr );
if (ret) {
printf ( "SPURS initialization failed: %08x \n " , ret);
free (spurs);
return ret;
}
printf ( "SPURS initialized successfully \n " );
Complete Example
SPURS Initialization Example
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <ppu-types.h>
#include <spurs/spurs.h>
#include <lv2/spu.h>
#include <sys/thread.h>
#include <sys/spu.h>
#define SPU_NUMBER 6
#define SPURS_PREFIX_NAME "example"
#define SPU_THREAD_GROUP_PRIORITY 250
int main ( void ) {
int ret;
sys_ppu_thread_t ppu_thread_id;
int ppu_prio;
unsigned int nthread;
// Initialize SPU system
ret = sysSpuInitialize (SPU_NUMBER, 0 );
printf ( "sysSpuInitialize: %08x \n " , ret);
// Get PPU thread priority for SPURS
ret = sysThreadGetId ( & ppu_thread_id);
printf ( "sysThreadGetId: %08x (ID: %lx ) \n " , ret, ppu_thread_id);
ret = sysThreadGetPriority (ppu_thread_id, & ppu_prio);
printf ( "sysThreadGetPriority: %08x (Priority: %d ) \n " ,
ret, ppu_prio);
// Allocate SPURS instance
Spurs * spurs = (Spurs * ) memalign (SPURS_ALIGN, sizeof (Spurs));
// Initialize attributes
SpursAttribute attr;
ret = spursAttributeInitialize ( & attr,
SPU_NUMBER,
SPU_THREAD_GROUP_PRIORITY,
ppu_prio - 1 , // PPU priority slightly lower
true // Exit if no work
);
if (ret) {
printf ( "spursAttributeInitialize failed: %08x \n " , ret);
free (spurs);
return ret;
}
// Set name prefix
ret = spursAttributeSetNamePrefix ( & attr,
SPURS_PREFIX_NAME, strlen (SPURS_PREFIX_NAME));
if (ret) {
printf ( "spursAttributeSetNamePrefix failed: %08x \n " , ret);
free (spurs);
return ret;
}
// Initialize SPURS
ret = spursInitializeWithAttribute (spurs, & attr);
if (ret) {
printf ( "spursInitializeWithAttribute failed: %08x \n " , ret);
free (spurs);
return ret;
}
// Query SPURS information
ret = spursGetNumSpuThread (spurs, & nthread);
if (ret) {
printf ( "spursGetNumSpuThread failed: %08x \n " , ret);
} else {
printf ( "SPURS has %u SPU threads available \n " , nthread);
}
// Get SPU thread IDs
sys_spu_thread_t * threads =
( sys_spu_thread_t * ) malloc ( sizeof ( sys_spu_thread_t ) * nthread);
ret = spursGetSpuThreadId (spurs, threads, & nthread);
if (ret) {
printf ( "spursGetSpuThreadId failed: %08x \n " , ret);
} else {
for ( unsigned int i = 0 ; i < nthread; i ++ ) {
printf ( "SPU Thread %u : ID = %08x \n " , i, threads [i]);
}
}
// Get detailed SPURS info
SpursInfo info;
ret = spursGetInfo (spurs, & info);
if (ret) {
printf ( "spursGetInfo failed: %08x \n " , ret);
} else {
printf ( " \n SPURS Information: \n " );
printf ( " Number of SPUs: %d \n " , info . nSpus );
printf ( " SPU Group Priority: %d \n " , info . spuGroupPriority );
printf ( " PPU Thread Priority: %d \n " , info . ppuThreadPriority );
printf ( " Exit If No Work: %s \n " ,
info . exitIfNoWork ? "true" : "false" );
printf ( " Name Prefix: %s \n " , info . namePrefix );
printf ( " SPU Thread IDs: \n " );
for ( int i = 0 ; i < info . nSpus ; i ++ ) {
printf ( " SPU %d : %08x \n " , i, info . spuThreads [i]);
}
}
// SPURS is now ready for task submission
// Tasks can be submitted using SPURS workload APIs
// Finalize SPURS
printf ( " \n Finalizing SPURS \n " );
ret = spursFinalize (spurs);
if (ret) {
printf ( "spursFinalize failed: %08x \n " , ret);
free (threads);
free (spurs);
return ret;
}
free (threads);
free (spurs);
printf ( "SPURS example completed successfully \n " );
return 0 ;
}
SPURS Attributes
SpursAttribute Structure
SpursAttribute attr;
// Initialize with parameters
spursAttributeInitialize ( & attr ,
nSpus, // Number of SPUs (1-6)
spuPriority, // SPU thread group priority
ppuPriority, // PPU handler thread priority
exitIfNoWork // Exit when no tasks remain
);
// Set name prefix (max 15 characters)
spursAttributeSetNamePrefix ( & attr , "TaskScheduler" , 13 );
// Set thread group type
spursAttributeSetSpuThreadGroupType ( & attr ,
SPU_THREAD_GROUP_TYPE_NORMAL);
// Enable SPU printf debugging
spursAttributeEnableSpuPrintfIfAvailable ( & attr );
// Assign memory container (optional)
sys_mem_container_t container;
// ... create container ...
spursAttributeSetMemoryContainerForSpuThread ( & attr , container);
Thread Group Types
NORMAL
SEQUENTIAL
SYSTEM
MEMORY_FROM_CONTAINER
Standard thread group with normal scheduling SPU_THREAD_GROUP_TYPE_NORMAL
Tasks execute sequentially (useful for debugging) SPU_THREAD_GROUP_TYPE_SEQUENTIAL
System-level priority thread group SPU_THREAD_GROUP_TYPE_SYSTEM
Use dedicated memory container SPU_THREAD_GROUP_TYPE_MEMORY_FROM_CONTAINER
SPURS Management
Query SPURS State
SpursInfo info;
ret = spursGetInfo (spurs, & info );
printf ( "SPURS Configuration: \n " );
printf ( " SPUs: %d \n " , info.nSpus);
printf ( " SPU Priority: %d \n " , info.spuGroupPriority);
printf ( " PPU Priority: %d \n " , info.ppuThreadPriority);
printf ( " Exit If No Work: %s \n " ,
info.exitIfNoWork ? "Yes" : "No" );
printf ( " SPURS2: %s \n " , info.spurs2 ? "Yes" : "No" );
printf ( " Name: %s \n " , info.namePrefix);
printf ( " Deadline Misses: %u \n " , info.deadlineMissCounter);
printf ( " Deadline Meets: %u \n " , info.deadlineMeetCounter);
unsigned int num_threads;
ret = spursGetNumSpuThread (spurs, & num_threads );
sys_spu_thread_t * thread_ids =
malloc ( sizeof ( sys_spu_thread_t ) * num_threads );
ret = spursGetSpuThreadId (spurs, thread_ids, & num_threads );
for ( unsigned int i = 0 ; i < num_threads; i ++ ) {
printf ( "SPU Thread %u : ID= %08x \n " , i, thread_ids [i]);
// Can use thread ID with regular SPU thread functions
u64 config;
sysSpuThreadGetConfiguration ( thread_ids [i], & config);
}
free (thread_ids);
Get SPU Thread Group
sys_spu_group_t group_id;
ret = spursGetSpuThreadGroupId (spurs, & group_id );
printf ( "SPURS thread group ID: %08x \n " , group_id);
// Can use with thread group functions
u32 priority;
sysSpuThreadGroupGetPriority (group_id, & priority );
printf ( "Thread group priority: %u \n " , priority);
Priority and Workload Management
Set Priorities
// Set priorities for workload slots (0-15)
u8 priorities [SPURS_MAX_SPU] = {
0 , // Highest priority
0 ,
1 , // Medium priority
1 ,
2 , // Lower priority
2 ,
3 , // Lowest priority
3
};
ret = spursSetPriorities (spurs,
0 , // Workload number
priorities // Priority array
);
Set Maximum Contention
// Limit number of SPUs that can run a specific workload
ret = spursSetMaxContention (spurs,
0 , // Workload number
4 // Max 4 SPUs for this workload
);
Wake Up SPURS
// Wake up SPURS to check for new work
// Useful when exitIfNoWork=true
ret = spursWakeUp (spurs);
SPURS vs SPURS2
PSL1GHT supports both SPURS and SPURS2:
SPURS (Standard)
SPURS2 (Extended)
Spurs * spurs = memalign (SPURS_ALIGN, sizeof (Spurs));
// Size: 4096 bytes (SPURS_SIZE)
spursInitializeWithAttribute (spurs, & attr );
Spurs2 * spurs2 = memalign (SPURS_ALIGN, sizeof (Spurs2));
// Size: 8192 bytes (SPURS_SIZE2)
spursInitializeWithAttribute2 (spurs2, & attr );
SPURS2 provides extended functionality with larger internal buffers. Use when you need advanced features or have many concurrent tasks.
Direct Initialization (Without Attributes)
Spurs * spurs = memalign (SPURS_ALIGN, sizeof (Spurs));
ret = spursInitialize (spurs,
6 , // Number of SPUs
200 , // SPU priority
150 , // PPU priority
true // Exit if no work
);
if (ret) {
printf ( "spursInitialize failed: %08x \n " , ret);
free (spurs);
return ret;
}
Cleanup
Always finalize SPURS before freeing the structure. Failure to do so can cause resource leaks or system instability.
// Finalize SPURS (waits for tasks to complete)
ret = spursFinalize (spurs);
if (ret) {
printf ( "Warning: spursFinalize failed: %08x \n " , ret);
}
// Free SPURS structure
free (spurs);
spurs = NULL ;
Task Scheduling (Advanced)
Task scheduling APIs (task sets, jobs, etc.) are part of SPURS but may require additional headers and libraries. The basic SPURS framework shown here manages the SPU infrastructure; task submission requires additional setup.
Typical SPURS task workflow:
// 1. Initialize SPURS (shown above)
Spurs * spurs = ...
spursInitializeWithAttribute (spurs, & attr );
// 2. Create task set or job queue
// (Requires additional SPURS task APIs)
// 3. Submit tasks to SPURS
// - Tasks are automatically scheduled across SPUs
// - Load balanced based on availability
// - Prioritized according to configuration
// 4. Wait for completion
// (Task-specific wait functions)
// 5. Finalize
spursFinalize (spurs);
Debugging SPURS
Enable SPU Printf
// PPU side - initialize printf service
sysSpuPrintfInitialize ( 100 , my_printf_handler);
// Enable in SPURS attributes
spursAttributeEnableSpuPrintfIfAvailable ( & attr );
// SPU programs can now use:
// #include <spu_printf.h>
// spu_printf("Debug: value = %d\n", x);
// Cleanup
sysSpuPrintfFinalize ();
Monitor Deadlines
SpursInfo info;
spursGetInfo (spurs, & info );
printf ( "Deadlines met: %u \n " , info.deadlineMeetCounter);
printf ( "Deadlines missed: %u \n " , info.deadlineMissCounter);
if (info.deadlineMissCounter > 0 ) {
printf ( "Warning: %u tasks missed their deadlines \n " ,
info . deadlineMissCounter );
}
Best Practices
Size Appropriately Use SPURS for complex workloads; simple tasks may not need it
Set Correct Priorities PPU priority should be lower than SPU group priority
Monitor Performance Check deadline counters and adjust priorities as needed
Clean Exit Set exitIfNoWork=true for applications with finite workloads
Common Use Cases
Use SPURS to manage game subsystems:
Physics simulation (high priority)
Animation updates (medium priority)
AI pathfinding (lower priority)
Audio processing (high priority, low latency)
Distribute computational workloads:
Particle simulations
Matrix operations
Monte Carlo methods
Signal processing
Memory Requirements
Structure Size Alignment Spurs4096 bytes 128 bytes Spurs28192 bytes 128 bytes SpursAttribute512 bytes 8 bytes
// Always use memalign with proper alignment
Spurs * spurs = memalign (SPURS_ALIGN, sizeof (Spurs));
if (( uintptr_t )spurs % SPURS_ALIGN != 0 ) {
printf ( "Error: SPURS not properly aligned! \n " );
free (spurs);
return - 1 ;
}
Troubleshooting
Error : spursInitializeWithAttribute returns non-zeroSolutions :
Verify SPU system initialized with sysSpuInitialize
Check SPURS structure is properly aligned (128 bytes)
Ensure attribute structure is valid
Verify PPU priority is less than SPU priority
Problem : Tasks submitted but not runningSolutions :
Call spursWakeUp if using exitIfNoWork=true
Check task submission code
Verify SPU programs are loaded correctly
Monitor with spursGetInfo to check thread status
API Reference
Initialization Functions
Configuration Functions
Management Functions
SPU Overview Learn SPU fundamentals and architecture
Thread Management Low-level SPU thread group management
DMA Transfers Data transfer for SPU tasks
API Reference Complete function reference