1. Introduction
Board Support Package (BSP) is the hardware abstraction layer for VxWorks, responsible for initializing the processor, memory, clocks, interrupt controllers, and peripherals at system startup, and then handing control over to the operating system kernel. Without a suitable BSP, VxWorks cannot run on your hardware.
BSPs are typically highly dependent on specific hardware but share some common components, such as:
- • Early processor initialization
- • Memory management (memory management unit setup)
- • Device initialization and configuration
- • Starting the interrupt controller
- • Supporting device drivers through VxBus
Since BSP development involves low-level programming using C and assembly language, understanding the processor manual and the circuit board schematic is crucial.
2. BSP Development Workflow
2.1 Hardware Documentation
First, gather the following materials:
- • Processor/System on Chip reference manual
- • Circuit board schematic with memory mapping and pin multiplexing
- • Peripheral data sheets, including register and interrupt information
2.2 Selecting the Starting BSP
VxWorks BSPs suitable for similar processors can usually be found. For example, a Cortex-A9 based BSP can be adapted to a new A9 board by updating memory and device tree entries.
2.3 Porting the BSP
- • Memory Mapping: Adjust the physical and virtual memory ranges, ensuring the memory management unit is set up correctly.
- • Clock and PLL Settings: Initialize the onboard clock according to peripheral requirements.
- • Serial/Console Settings: For early debugging output.
- • Interrupt Controller: Initialize the interrupt controller to handle device interrupt requests (IRQ).
- • Device Tree: Describe all hardware peripherals.
2.4 Build and Test
- • Build the BSP using Wind River’s
<span>make</span>or<span>vxprj</span>tools. - • Debug the boot process using JTAG or serial console.
- • Perform runtime diagnostics using VxWorks shell commands (
<span>devs</span>,<span>i</span>,<span>ld</span>).
3. BSP Directory Structure
Example layout:
your_bsp/
├── Makefile # Build rules and compiler flags
├── config.h # BSP global definitions (processor frequency, memory size)
├── sysLib.c # System initialization functions
├── sysClk.c # System clock (timer) support
├── sysSerial.c # Serial driver interface
├── hwconf.c # Hardware resource configuration table
├── device-tree/ # Device tree source files (*.dts)
│ ├── myboard.dts
│ └── ...
└── README.md
3.1 sysLib.c
Contains processor settings, memory initialization, and system startup entry point, such as:
void sysHwInit(void)
{
/* Disable interrupts */
vxCpuIntDisable();
/* Initialize memory controller */
sysMemInit();
/* Initialize serial port for early debugging */
sysSerialHwInit();
/* Set up interrupt controller */
sysIntInit();
/* Set up system timer */
sysClkInit();
/* Enable interrupts */
vxCpuIntEnable();
}
3.2 config.h
Defines board parameters such as clock frequency, memory size, and BSP configuration macros. Example:
#define SYS_CLK_RATE 100 /* 100 ticks per second */
#define LOCAL_MEM_LOCAL_ADRS 0x80000000
#define LOCAL_MEM_SIZE 0x10000000 /* 256MB */
#define UART_BASE_ADDR 0x10000000
#define UART_BAUD_RATE 115200
4. Core Components of BSP
4.1 Bootloader Overview
The bootloader’s task is to perform very early system initialization and load the VxWorks image into memory. Typically, this is a small program or a variant of U-Boot responsible for:
- • Configuring processor modes (cache, memory management unit on or off)
- • Setting up the initial dynamic random access memory (DRAM) controller
- • Initializing the serial port for debugging output
- • Loading the kernel image from flash, network, or SD card
- • Jumping to the VxWorks kernel’s
<span>sysStart()</span>entry
4.2 Early Initialization Code Example
void sysHwInit0(void)
{
/* Globally disable interrupts */
vxCpuIntDisable();
/* Initialize clock */
clkInit();
/* Initialize serial port for early console output */
uartInit(UART_BASE_ADDR, UART_BAUD_RATE);
/* Print startup information */
printf("BSP early initialization complete.\n");
}
4.3 Bootloader Early Initialization
void sysHwInit0(void)
{
vxCpuIntDisable();
*(volatile uint32_t *)0xF8006000 = 0x12345678; /* DDR timing */
*(volatile uint32_t *)0xF8006004 = 0x0000AABB;
pllConfig(0x1F, 0x2A);
uartInit(0x9000000, 115200);
printf("Board early init done, UART ready.\n");
}
4.4 sysStart() Entry Point
<span>sysStart()</span> function is the kernel startup routine called after the bootloader loads the operating system image. It typically performs further hardware initialization and then calls the user application entry <span>usrRoot()</span><code><span>.</span>
5. Device Tree Configuration
5.1 What is a Device Tree?
A Device Tree (DT) is a data structure that describes the system hardware components in a platform-independent manner. The device tree avoids hardcoding hardware parameters in the BSP code, allowing the operating system and drivers to dynamically discover and configure devices at startup.
VxWorks uses device tree binary files (<span>.dtb</span>) compiled from <span>.dts</span> source files to initialize hardware resources and bind drivers through VxBus.
5.2 Device Tree Syntax Example
/ {
uart0:serial@10000000{
compatible="ns16550";
reg=<0x10000000 0x1000>;
interrupts=<5>;
clock-frequency=<24000000>;
};
timer0:timer@10002000{
compatible="arm,armv7-timer";
reg=<0x10002000 0x1000>;
interrupts=<30>;
};
};
- •
<span>compatible</span>: Identifies the device type or the bound driver - •
<span>reg</span>: Base address and size of the device registers - •
<span>interrupts</span>: Interrupt request number (IRQ) - • Other properties (e.g., clock frequency) used to configure the device
5.3 Integrating Device Tree in BSP
- • Place
<span>.dts</span>files in the<span>bsp/your_bsp/device-tree/</span>directory - • Add device tree compilation in the BSP Makefile using
<span>dtc</span>(Device Tree Compiler) - • Load and parse the device tree binary file in the BSP initialization code, then probe the devices
<span>hwconf.c</span> example (pseudo code):
void hwconfInit(void)
{
dtb = loadDeviceTreeBlob("/boot/myboard.dtb");
vxBusInit(dtb);
}
5.4 Complex Device Tree Fragment
i2c1: i2c@40800000{
compatible="arm,my-i2c";
reg=<0x40800000 0x1000>;
interrupts=<23>;
clock-frequency=<100000>;
temp_sensor@48{
compatible="ti,tmp102";
reg=<0x48>;
};
eeprom@50{
compatible="at,24c256";
reg=<0x50>;
};
};
6. Memory Management Unit and Cache Settings
6.1 Importance of Memory Management Unit Settings
The Memory Management Unit (MMU) controls virtual memory translation, access permissions, and cache attributes. Correct MMU configuration is crucial for:
- • Protecting memory regions
- • Enabling caching to improve performance
- • Mapping peripherals as non-cached regions
6.2 Defining Physical Memory Regions
Use the <span>VM_REGION</span> structure in <span>sysPhysMemDesc[]</span><code><span> to describe the memory layout. Example:</span>
VM_REGION sysPhysMemDesc[] = {
{
(VIRT_ADDR) LOCAL_MEM_LOCAL_ADRS,
(PHYS_ADDR) LOCAL_MEM_LOCAL_ADRS,
LOCAL_MEM_SIZE,
VM_STATE_MASK_VALID | VM_STATE_MASK_WRITABLE,
VM_STATE_VALID | VM_STATE_WRITABLE
},
{
(VIRT_ADDR) PERIPH_BASE_ADDR,
(PHYS_ADDR) PERIPH_BASE_ADDR,
PERIPH_SIZE,
VM_STATE_MASK_VALID | VM_STATE_MASK_WRITABLE | VM_STATE_MASK_CACHEABLE,
VM_STATE_VALID | VM_STATE_WRITABLE /* Non-cached */
}
};
6.3 Enabling Caching
Enable instruction and data caching as early as possible in the startup code:
void cacheEnable(void)
{
cacheEnable(INSTRUCTION_CACHE);
cacheEnable(DATA_CACHE);
}
6.4 Cache Management for Direct Memory Access (DMA) Buffers
For buffers shared between the processor and DMA devices, flush or invalidate the cache to ensure consistency:
void prepareDmaBuffer(void *buffer, size_t size)
{
cacheFlush(DATA_CACHE, buffer, size);
}
7. Interrupt and Timer Initialization
7.1 Interrupt Controller Setup
The BSP must initialize the interrupt controller (e.g., GIC on ARM) to:
- • Route device interrupt requests
- • Enable/disable interrupts
- • Set interrupt priorities
<span>sysIntInit()</span> example:
void sysIntInit(void)
{
gicInit();
gicEnableDistributor();
}
7.2 Connecting Interrupt Service Routines (ISR)
Use <span>intConnect()</span> to associate interrupt vectors with interrupt service routines:
STATUS sysSerialIntConnect(void)
{
return intConnect(INUM_TO_IVEC(UART_INT_VEC), (VOIDFUNCPTR)sysSerialIntHandler, 0);
}
7.3 System Clock Initialization
The system clock drives the operating system ticks and timing. Typical BSP setup:
STATUS sysClkConnect(FUNCPTR routine, int arg)
{
sysClkRoutine = routine;
sysClkArg = arg;
return OK;
}
void sysClkInt(void)
{
if (sysClkRoutine)
sysClkRoutine(sysClkArg);
}
STATUS sysClkEnable(void)
{
timerEnable(TIMER0, SYS_CLK_RATE);
intConnect(INUM_TO_IVEC(TIMER0_INT_VEC), sysClkInt, 0);
intEnable(TIMER0_INT_VEC);
return OK;
}
7.4 ARM GIC Interrupt Setup
void sysIntInit(void)
{
gicDistInit();
gicCpuInit();
gicDistEnable();
gicSetPriority(UART_INT_VEC, 0x80);
gicEnableInterrupt(UART_INT_VEC);
intConnect(INUM_TO_IVEC(UART_INT_VEC), (VOIDFUNCPTR)uartIsr, 0);
intEnable(UART_INT_VEC);
}
8. Adding Device Drivers
8.1 VxBus Framework
VxWorks drivers are managed by VxBus, which uses device tree information to dynamically probe and bind drivers.
8.2 Example: Serial Driver Initialization
#include <vxBusLib.h>
#include <hwif/vxbus/vxBus.h>
LOCAL VXB_DEV_ID uartDev;
STATUS sysSerialHwInit(void)
{
uartDev = vxbInstByNameFind("ns16550", 0);
if (!uartDev)
return ERROR;
return OK;
}
8.3 Driver Probe and Bind Methods
Drivers define probe and bind callback functions to be called by VxBus during device enumeration:
LOCAL STATUS myUartProbe(VXB_DEV_ID pDev)
{
/* Verify hardware presence */
return OK;
}
LOCAL STATUS myUartAttach(VXB_DEV_ID pDev)
{
/* Map registers, initialize device */
return OK;
}
LOCAL VXB_DRV_METHOD myUartMethods[] = {
{ VXB_DEVMETHOD_CALL(vxbDevProbe), (FUNCPTR)myUartProbe },
{ VXB_DEVMETHOD_CALL(vxbDevAttach), (FUNCPTR)myUartAttach },
VXB_DEVMETHOD_END
};
VXB_DRV_DEF(myUartDrv, myUartMethods, "My UART Driver");
8.4 Serial Driver Probe and Bind
LOCAL STATUS uartProbe(VXB_DEV_ID pDev)
{
volatile uint32_t *reg = (volatile uint32_t *)vxbRegBaseAddr(pDev);
if ((*reg & 0xFF) != EXPECTED_UART_ID)
return ERROR;
return OK;
}
LOCAL STATUS uartAttach(VXB_DEV_ID pDev)
{
volatile uint32_t *base = (volatile uint32_t *)vxbRegBaseAddr(pDev);
base[UART_BAUD_REG] = UART_BAUD_115200;
base[UART_CTRL_REG] = UART_ENABLE | UART_RX_INT_ENABLE;
return OK;
}
9. BSP Debugging and Testing
9.1 Using VxWorks Shell
The VxWorks shell provides commands to check and debug the BSP and drivers:
- •
<span>devs</span>— List devices recognized by VxBus - •
<span>i</span>— Show active interrupts - •
<span>ld</span>— List loaded modules/drivers - •
<span>sp</span>— Start tasks to test drivers
Example:
-> devs
ns16550@10000000 (serial)
timer@10002000
9.2 WindView Event Tracing
WindView allows tracing of events such as interrupts and context switches to analyze BSP performance. Integrate WindView macros in critical BSP code sections for fine-grained analysis.
9.3 Hardware Debugger
Use JTAG or BDI for:
- • Stepping through the startup code
- • Inspecting registers and memory
- • Setting breakpoints in BSP code (e.g.,
<span>sysHwInit</span>)
10. Best Practices
- • Start from a similar BSP: Save time and reduce errors
- • Incremental Testing: Test each stage separately — memory, console, interrupts, timers
- • Use Device Trees: Avoid hardcoding hardware descriptions for easier maintenance
- • Document Everything: Memory mappings, interrupt requests, clock settings
- • Isolate BSP and Application Code: Maintain a modular design
11. Advanced BSP Topics
11.1 Multi-core (SMP) Booting
Enabling SMP in VxWorks
Define the number of processors and enable SMP configuration macros:
#define _WRS_CONFIG_SMP 1
#define VX_SMP_NUM_CPUS 4
Booting Secondary Processors
The BSP must boot secondary cores and initialize their kernel data:
void sysSecondaryCpuStart(void)
{
sysSecondaryCpuInit();
kernelCpuInit();
}
Each secondary processor runs this code to join the SMP kernel.
11.2 Adding Custom Peripheral Drivers
Device Tree Example
spi1: spi@40013000 {
compatible = "myvendor,myspi";
reg = <0x40013000 0x1000>;
interrupts = <12>;
bus-frequency = <48000000>;
};
Driver Framework
LOCAL STATUS myspiProbe(VXB_DEV_ID pDev) { return OK; }
LOCAL STATUS myspiAttach(VXB_DEV_ID pDev) { return OK; }
LOCAL VXB_DRV_METHOD myspiMethods[] = {
{ VXB_DEVMETHOD_CALL(vxbDevProbe), (FUNCPTR)myspiProbe },
{ VXB_DEVMETHOD_CALL(vxbDevAttach), (FUNCPTR)myspiAttach },
VXB_DEVMETHOD_END
};
VXB_DRV_DEF(myspiDrv, myspiMethods, "MyVendor SPI Driver");
11.3 Performance Optimization
- • Enable caching and explicitly manage DMA buffer consistency
- • Use fast interrupt connections (
<span>intConnect</span>) and enable only necessary interrupt requests - • Map devices as non-cached memory regions to avoid stale data
- • Minimize interrupt latency by keeping interrupt service routines short and deferring work
11.4 Booting Secondary Processors (ARM Cortex-A9)
void sysSecondaryCpuStart(void)
{
*(volatile uint32_t *)CPU_RELEASE_ADDR = SECONDARY_CPU_START_ADDR;
while(!secondaryCpuReady());
kernelCpuInit();
}
12. Debugging Advanced BSP Issues
- • Multi-core Debugging: Use JTAG tools that support multi-core debugging; be aware of synchronization issues
- • Driver Debugging: Use VxBus commands like
<span>vxbDevShow()</span>and check driver probe/bind return values - • Memory Issues: Carefully check MMU mappings; use VxWorks memory tools to monitor heap/stack
13. Advanced Best Practices
- • Incrementally boot SMP, testing each processor one by one
- • Use device tree overlays to test new hardware without recompiling the BSP
- • Design deterministic interrupt service routines and use kernel-safe APIs
- • Maintain detailed API documentation for BSP driver interfaces
14. Conclusion
Mastering BSP development for VxWorks allows complete control over embedded system hardware. From basic startup initialization to advanced SMP support and custom driver integration, a well-designed BSP is the foundation for reliable, high-performance real-time applications.
With modular design, thorough documentation, and rigorous testing, your BSP can support complex hardware platforms and enable VxWorks systems to meet demanding real-time requirements.
Click “Read the original” for more VxWorks articles.