Skip to main content

VxWorks BSP Development Handbook

·1785 words·9 mins
VxWorks BSP Handbook
Table of Contents

1. Introduction
#

The Board Support Package (BSP) acts as the hardware abstraction layer for VxWorks. It is responsible for initializing the CPU, memory, clocks, interrupt controllers, and peripherals during system boot before handing control over to the OS kernel. Without a proper BSP, VxWorks cannot run on your hardware.

BSPs are generally highly hardware-specific but share some common components like:

  • Early CPU initialization
  • Memory management (MMU setup)
  • Device initialization and configuration
  • Bringing up interrupt controllers
  • Support for device drivers via VxBus

Because BSP development involves low-level programming often in C and assembly, understanding processor manuals and board schematics is essential.

2. BSP Development Workflow
#

2.1 Hardware Documentation
#

Start by collecting:

  • CPU/SoC reference manuals
  • Board schematics for memory maps and pin multiplexing
  • Peripheral datasheets for registers and interrupts

2.2 Selecting a Starting BSP
#

VxWorks BSPs for similar CPUs are often available. For example, a Cortex-A9 BSP can be adapted for a new A9-based board by updating memory and device tree entries.

2.3 Porting the BSP
#

  • Memory Map: Adjust physical and virtual memory ranges, ensure proper MMU settings.
  • Clock & PLL Setup: Initialize board clocks as required by peripherals.
  • UART/Console Setup: For early debug output.
  • Interrupt Controller: Initialize the interrupt controller to handle device IRQs.
  • Device Tree: Describe all hardware peripherals.

2.4 Build & Test
#

  • Build BSP using Wind River’s make or vxprj tools.
  • Use JTAG or serial console to debug boot.
  • Use VxWorks shell commands (devs, i, ld) for runtime diagnostics.

3. BSP Directory Structure
#

Example layout:

your_bsp/
├── Makefile          # Build rules and compiler flags
├── config.h          # BSP-wide defines (CPU freq, memory sizes)
├── sysLib.c          # System initialization functions
├── sysClk.c          # System clock (timer) support
├── sysSerial.c       # Serial port driver interface
├── hwconf.c          # Hardware resource configuration tables
├── device-tree/      # Device tree source files (*.dts)
│   ├── myboard.dts
│   └── ...
└── README.md

3.1 sysLib.c
#

Contains CPU setup, memory initialization, and system boot entry points such as:

void sysHwInit(void)
{
    /* Disable interrupts */
    vxCpuIntDisable();

    /* Initialize memory controller */
    sysMemInit();

    /* Initialize UART for early debug */
    sysSerialHwInit();

    /* Setup interrupt controller */
    sysIntInit();

    /* Setup system timer */
    sysClkInit();

    /* Enable interrupts */
    vxCpuIntEnable();
}

3.2 config.h
#

Defines board parameters such as clock frequencies, memory sizes, 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. Essential BSP Components
#

4.1 Boot Loader Overview
#

The boot loader’s job is to perform very early system initialization and load the VxWorks image into memory. Often, this is a minimal program or U-Boot variant that:

  • Configures CPU mode (cache, MMU off or on)
  • Sets up initial DRAM controller
  • Initializes UART for serial debug output
  • Loads kernel image from flash, network, or SD card
  • Jumps to sysStart() entry in VxWorks kernel

4.2 Early Initialization Code Example
#

void sysHwInit0(void)
{
    /* Disable interrupts globally */
    vxCpuIntDisable();

    /* Initialize clocks */
    clkInit();

    /* Initialize UART for early console */
    uartInit(UART_BASE_ADDR, UART_BAUD_RATE);

    /* Print boot message */
    printf("BSP early initialization complete.
");
}

4.3 Boot Loader 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.
");
}

4.4 sysStart() Entry Point
#

The function sysStart() is the kernel start routine invoked by the boot loader after loading the OS image. Typically it performs further hardware initialization and then calls usrRoot() which is the user application entry.

5. Device Tree Configuration
#

5.1 What is a Device Tree?
#

The device tree (DT) is a data structure for describing hardware components in a system in a platform-independent way. Instead of hardcoding hardware parameters in BSP code, the DT allows the OS and drivers to discover and configure devices dynamically at boot.

VxWorks uses device tree blobs (.dtb) compiled from .dts source files to initialize hardware resources and bind drivers via VxBus.

5.2 Basic 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>;
    };
};
  • compatible: identifies the device type or driver to bind
  • reg: base address and size of the device registers
  • interrupts: IRQ number
  • Other properties (e.g., clock frequency) configure the device

5.3 Integrating Device Tree in BSP
#

  • Place .dts files in bsp/your_bsp/device-tree/
  • Add device tree compilation to the BSP Makefile using dtc (device tree compiler)
  • In BSP init code, load and parse the device tree blob before probing devices

Example in hwconf.c (pseudocode):

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. MMU & Cache Setup
#

6.1 Importance of MMU Setup
#

The Memory Management Unit (MMU) controls virtual memory translation, access permissions, and cache attributes. Proper MMU configuration is essential for:

  • Protecting memory regions
  • Enabling cache for performance
  • Mapping peripherals as non-cacheable

6.2 Defining Physical Memory Regions
#

Use VM_REGION structs to describe memory layout in sysPhysMemDesc[]. Example:

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 /* no cache */
    }
};

6.3 Enabling Caches
#

Enable instruction and data cache early in the boot code:

void cacheEnable(void)
{
    cacheEnable(INSTRUCTION_CACHE);
    cacheEnable(DATA_CACHE);
}

6.4 Cache Management for DMA Buffers
#

For buffers shared between CPU and DMA devices, flush or invalidate caches to ensure coherency:

void prepareDmaBuffer(void *buffer, size_t size)
{
    cacheFlush(DATA_CACHE, buffer, size);
}

7. Interrupt & Timer Initialization
#

7.1 Interrupt Controller Setup
#

The BSP must initialize the interrupt controller (e.g., GIC on ARM) to:

  • Route device IRQs
  • Enable/disable interrupts
  • Set interrupt priorities

Example in sysIntInit():

void sysIntInit(void)
{
    gicInit();
    gicEnableDistributor();
}

7.2 Connecting Interrupt Service Routines (ISRs)
#

Use intConnect() to associate an interrupt vector with an ISR:

STATUS sysSerialIntConnect(void)
{
    return intConnect(INUM_TO_IVEC(UART_INT_VEC), (VOIDFUNCPTR)sysSerialIntHandler, 0);
}

7.3 System Clock Initialization
#

The system clock drives OS 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 probe and attach drivers dynamically.

8.2 Example: UART 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 & Attach Methods
#

Drivers define probe and attach callbacks that VxBus calls 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 UART Driver Probe and Attach
#

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. Debugging & Testing BSP
#

9.1 Using the VxWorks Shell
#

VxWorks shell provides commands to inspect and debug BSP and drivers:

  • devs — List devices recognized by VxBus
  • i — Show active interrupts
  • ld — List loaded modules/drivers
  • sp — Spawn tasks for testing drivers

Example:

-> devs
ns16550@10000000 (serial)
timer@10002000

9.2 WindView Event Tracing
#

WindView allows tracing events such as interrupts and context switches to profile BSP performance. Integrate WindView macros in critical BSP code sections for fine-grained analysis.

9.3 Hardware Debuggers
#

Use JTAG or BDI to:

  • Step through boot code
  • Inspect registers and memory
  • Set breakpoints in BSP code (e.g., sysHwInit)

10. Best Practices
#

  • Start from a close BSP: saves time and reduces bugs
  • Incremental testing: test each stage separately — memory, console, interrupts, timers
  • Use Device Tree: avoids hardcoded hardware descriptions, easier to maintain
  • Document everything: memory maps, IRQs, clock settings
  • Isolate BSP from application code: maintain a clean modular design

11. Advanced BSP Topics
#

11.1 Multi-Core (SMP) Bring-Up
#

Enabling SMP in VxWorks
#

Define number of CPUs and enable SMP config macros:

#define _WRS_CONFIG_SMP 1
#define VX_SMP_NUM_CPUS 4

Starting Secondary CPUs
#

The BSP must boot secondary cores and initialize their kernel data:

void sysSecondaryCpuStart(void)
{
    sysSecondaryCpuInit();
    kernelCpuInit();
}

Each secondary CPU 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 Skeleton
#

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 Tuning
#

  • Enable caches and manage coherency explicitly for DMA buffers
  • Use fast interrupt connect (intConnect) and enable only needed IRQs
  • Map devices as non-cacheable memory regions to avoid stale data
  • Minimize ISR latency by keeping handlers short and deferring work

11.4 Booting Secondary CPU (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 capable of multi-core debugging; watch for synchronization issues
  • Driver Debugging: Use VxBus commands like vxbDevShow() and check driver probe/attach return values
  • Memory Issues: Check MMU mapping carefully; use VxWorks memory tools to monitor heap/stack

13. Advanced Best Practices
#

  • Bring up SMP incrementally, test each CPU individually
  • Use device tree overlays to test new hardware without recompiling BSP
  • Design ISRs to be deterministic and use kernel-safe APIs
  • Maintain detailed API documentation for BSP driver interfaces

14. Conclusion
#

Mastering BSP development in VxWorks unlocks full control over your embedded system hardware. From basic boot initialization through advanced SMP support and custom driver integration, a well-crafted BSP is foundational to reliable, high-performance real-time applications.

With modular design, proper documentation, and thorough testing, your BSP can support complex hardware platforms and enable your VxWorks system to meet demanding real-time requirements.

Related

A Comparative Analysis of BSP Development: VxWorks vs. Linux
·831 words·4 mins
VxWorks Linux BSP Embedded Systems Device Drivers RTOS
Device Tree-Based Driver Development in VxWorks 7.0
·525 words·3 mins
VxWorks Device Tree Driver Development BSP RTOS GPIO
VxWorks BSP for Zynq UltraScale+ MPSoC Powered System on Modules
·733 words·4 mins
VxWorks BSP Zynq UltraScale+