When working with memory-mapped hardware, the ability to read and write registers from the kernel shell is a powerful tool-especially useful during device driver development and hardware bring-up. In this post, we’ll walk through how to access device registers from the VxWorks 7 kernel shell, and how this process differs from earlier versions like VxWorks 6.9.
Accessing Registers in VxWorks 6.9 #
If you’ve used VxWorks 6.9 before, you might remember using the d
and m
commands in the kernel shell to inspect and modify device registers directly:
-> d 0xffd02000, 8, 4
NOTE: memory values are displayed in hexadecimal.
0xffd02000: 00000001 000000ee 3fe449ea 00000000 *.........I.?....*
0xffd02010: 00000000 00000000 3130362a 00000000 *........*601....*
value = 0 = 0x0
->
This dumps eight 4-byte registers starting at the physical address 0xffd02000.
To modify register values:
-> m ffd02000, 4
0xffd02000: 00000001-
0xffd02004: 000000ee-ff
0xffd02008: 3e63054e-.
value = 0 = 0x0
Or, using pointer syntax:
-> *0xffd02000 = 0
value = 0 = 0x0
These examples access the L4 Watchdog Timer on the Cyclone V HPS.
What’s Different in VxWorks 7 #
Trying the same approach in VxWorks 7 often results in an exception:
-> d 0xffd02000, 32, 4
Data abort
Exception address: 0x003736a8
Data Fault Address Register: 0xffd02000
...
Shell task 'tShell0' restarted...
Why? Because VxWorks 7 handles memory management very differently. Specifically, address translation between virtual and physical addresses is no longer guaranteed to be one-to-one.
Understanding Virtual vs. Physical Addresses #
Modern processors use Memory Management Units (MMUs) to manage memory. VxWorks leverages this to define separate virtual
and physical
address spaces:
Virtual addresses
: Used by software (e.g., pointers in C)Physical addresses
: Used by hardware to route memory accesses
In VxWorks 7, only regions explicitly mapped from physical to virtual memory are accessible. Attempting to use an unmapped physical address as a virtual address will trigger a data abort.
Inspecting the Address Map #
To inspect the current memory mappings, use vmContextShow
in the shell:
-> vmContextShow
VIRTUAL ADDR BLOCK LENGTH PHYSICAL ADDR PROT CACHE SPECIAL
0x22000000 0x00004000 0xffd08000 RW- OFF/CO/G ...
0x22008000 0x00001000 0xffd05000 RW- OFF/CO/G ...
...
The physical address 0xffd02000
doesn’t appear—hence, attempts to access it cause an exception.
Why Are Some Devices Mapped, But Not Others #
The answer lies in the device tree. When VxWorks boots, it parses the device tree, initializes device drivers, and maps required physical regions to virtual memory. Devices not claimed by drivers may remain unmapped.
Manually Mapping a Device Register #
If a device isn’t mapped automatically, you can manually map it using the pmapGlobalMap()
function:
void* pmapGlobalMap (PHYS_ADDR addr, size_t len, UINT attrs);
addr
: Physical addresslen
: Length in bytes (rounded up to at least a memory page)attrs
: Memory attributes (e.g., cache settings, access permissions)
Example: Mapping the L4 Watchdog Timer #
-> l4wd0 = pmapGlobalMap (0xffd02000ULL, 0x1000, 0x483)
New symbol "l4wd0" added to kernel symbol table.
l4wd0 = 0x228f7000
The 0x483
attribute enables read/write access (0x3
), disables caching (0x80
), and ensures guarded access (0x400
). These are derived from MMU_ATTR_*
constants in vmLibCommon.h
.
You can now access the device via the mapped virtual address:
-> d l4wd0, 32, 4
0x228f7000: 00000001 000000ff 7b02c5be 00000000 ...
To confirm the mapping:
-> vmContextShow
...
0x228f7000 0x00001000 0xffd02000 RW- OFF/CO/G ...
Conclusion #
To access hardware registers in VxWorks 7:
- Map the register’s physical address using
pmapGlobalMap()
. - Use the virtual address returned to read or write registers using the usual shell commands.
While this is a bit more involved than in VxWorks 6.9, it provides much better control and memory protection.