When working with VxWorks 7 in production projects, one of the most critical tasks is BSP (Board Support Package) development. The BSP adapts the kernel to your custom ARM-based hardware, making sure that boot-up, memory mapping, and peripheral initialization all happen correctly.
In this article, we provide a comprehensive step-by-step guide to porting VxWorks to a custom ARM board. Whether you are working on a Cortex-A8, Cortex-A53, or a multi-core SoC, this guide will help you navigate the process of bringing your board to life.
What is a VxWorks BSP? #
A Board Support Package is the glue between the operating system and your hardware. It provides:
- Boot and initialization code
- MMU setup and cache policies
- Device tree and hardware description
- Board-specific drivers (UART, Ethernet, I2C, SPI, GPIO)
- System libraries (
sysLib.c
,sysHwInit.c
) - Kernel configuration parameters
Without a BSP, VxWorks cannot run on your board. With a well-designed BSP, the OS can boot, run reliably, and take advantage of all hardware features.
Step 1: Preparing the Development Environment #
-
Install Toolchain:
Use Wind River Workbench or an ARM cross-compiler (arm-wrs-linux-gnueabi-gcc
for ARMv7,aarch64-wrs-linux-gcc
for ARMv8). -
Set Up Source Tree:
VxWorks BSPs are located under:$WIND_HOME/vxworks-7/bsps/arm/<board_name>/
-
Collect Hardware Docs:
- SoC Reference Manual
- Board schematic
- Existing U-Boot/Linux device tree (for reference)
Step 2: BSP Directory Structure #
A typical ARM BSP directory looks like this:
board/
├── Makefile # BSP build rules
├── config.h # Kernel & board configuration
├── sysHwInit.c # Hardware initialization
├── sysLib.c # Board-level system calls
├── sysClk.c # Clock/timer support
├── sysSerial.c # UART/console driver
├── fdt/ # Flattened Device Tree files
├── linkscript.ld # Memory map
└── drivers/ # Custom device drivers
📌 Tip: Start by copying a similar reference BSP (e.g., ARMv8 Cortex-A53) and customize it for your hardware.
Step 3: Bootloader & Startup Code #
Most ARM boards use U-Boot. The BSP must define:
- Linker Script: Defines memory regions (DDR, SRAM, peripherals).
- Startup Assembly: Sets exception vectors, stack, MMU, and caches.
- sysHwInit(): Runs before the kernel starts.
Example memory map (linkscript.ld
):
MEMORY
{
SRAM (rwx) : ORIGIN = 0x00000000, LENGTH = 256K
DDR (rwx) : ORIGIN = 0x80000000, LENGTH = 1G
}
SECTIONS
{
.text : { *(.text*) } > DDR
.data : { *(.data*) } > DDR
.bss : { *(.bss*) } > DDR
}
Example hardware init (sysHwInit.c
):
void sysHwInit(void)
{
/* Setup MMU for DDR and peripherals */
armMmuInit();
/* Initialize system clock */
sysClkInit();
/* Enable UART console */
sysSerialHwInit();
/* Enable interrupts */
intLibInit();
}
Step 4: Device Tree (FDT) #
VxWorks uses Flattened Device Tree (FDT) for hardware description.
Example (custom-board.dts
):
/ {
model = "vendor,custom-arm-board";
compatible = "vendor,custom";
memory@80000000 {
device_type = "memory";
reg = <0x80000000 0x40000000>; /* 1GB DDR */
};
soc {
uart0: serial@101f1000 {
compatible = "arm,pl011";
reg = <0x101f1000 0x1000>;
clock-frequency = <24000000>;
};
eth0: ethernet@10100000 {
compatible = "snps,designware-eth";
reg = <0x10100000 0x2000>;
};
};
};
📌 Pro Tip: Start with the Linux DTS if available, then strip it down and adapt it for VxWorks.
Step 5: Writing Custom Drivers #
Not all peripherals are supported out of the box. Use the VxBus driver model for custom devices.
Example UART driver (sysSerial.c
):
STATUS sysSerialHwInit(void)
{
UART_REG->LCR = 0x3; /* 8 data bits, no parity, 1 stop bit */
UART_REG->DLL = 13; /* Baud divisor */
UART_REG->IER = 0x1; /* Enable RX interrupt */
return OK;
}
📌 Driver Integration:
- Register device with VxBus.
- Test via kernel shell (
-> printf("Hello from UART\n")
).
Step 6: MMU and Cache Setup #
The MMU (Memory Management Unit) ensures correct memory attributes. Example attributes:
- DDR → Cacheable, Bufferable
- Peripheral I/O → Strongly-Ordered, Non-Cacheable
- Boot ROM → Read-Only
In sysLib.c
:
VM_STATE sysPhysMemDesc[] = {
{ 0x80000000, 0x80000000, 0x40000000, VM_STATE_CACHEABLE },
{ 0x10100000, 0x10100000, 0x00100000, VM_STATE_IO }
};
Step 7: Testing & Debugging #
-
Boot Test
Load VxWorks image via TFTP in U-Boot:=> tftpboot 0x80000000 vxWorks => go 0x80000000
-
Console Test
Verify UART output:VxWorks Bootrom 7.x >
-
Peripheral Test
- UART: send/receive test
- Ethernet:
ifconfig
,ping
- Timer: check system ticks
-
Debugging Tools
- Workbench Debugger: Load symbol tables, set breakpoints
- JTAG: Low-level debugging if boot fails
- Kernel Shell: Check memory/registers with
peek
&poke
Best Practices #
- Start with minimal features (UART + timer).
- Use reference BSPs (saves weeks of effort).
- Keep drivers modular and reusable.
- Document MMU attributes and device mappings.
- Automate build and test with scripts.
Frequently Asked Questions (FAQ) #
Q: Can I reuse Linux device trees in VxWorks?
A: Yes, as a reference. But you must adjust bindings for VxWorks drivers.
Q: Do I need a custom bootloader?
A: Usually not. U-Boot is widely supported, but you may need small patches.
Q: How do I debug a BSP hang during boot?
A: Use JTAG or add debug UART prints inside sysHwInit()
to trace execution.
BSP Architecture Overview #
Here’s a simple layered view of the BSP in VxWorks:
+--------------------------+
| Applications |
+--------------------------+
| VxWorks Kernel |
+--------------------------+
| BSP (drivers, init) |
+--------------------------+
| Bootloader (U-Boot) |
+--------------------------+
| Custom ARM Hardware |
+--------------------------+
Conclusion #
Porting a BSP for VxWorks 7 on a custom ARM board is a multi-step process that requires a solid understanding of hardware, bootloaders, memory management, and drivers. With the right approach—boot code, device tree setup, driver integration, and systematic debugging—you can bring your board up successfully and leverage the real-time power of VxWorks.
📌 In the next article, we’ll focus on VxBus Driver Development, where we’ll build a complete custom driver from scratch.