在VxWork 7.0里,如果直接访问某个寄存器地址,往一个寄存器里写入数据时,系统会报错,原因是VxWorks 7往后的版本都采用了虚拟地址访问的机制,不能直接进行寄存器的读写。因此VxWorks 7.0往后的版本都提供了很多访问寄存器的接口函数:
#define CSR_READ_4(pDev, addr) \
vxbRead32 (FEI_HANDLE(pDev), (UINT32 *)((char *)FEI_BAR(pDev) + addr))
#define CSR_WRITE_4(pDev, addr, data) \
vxbWrite32 (FEI_HANDLE(pDev), \
(UINT32 *)((char *)FEI_BAR(pDev) + addr), data)
#define CSR_READ_2(pDev, addr) \
vxbRead16 (FEI_HANDLE(pDev), (UINT16 *)((char *)FEI_BAR(pDev) + addr))
#define CSR_WRITE_2(pDev, addr, data) \
vxbWrite16 (FEI_HANDLE(pDev), \
(UINT16 *)((char *)FEI_BAR(pDev) + addr), data)
#define CSR_READ_1(pDev, addr) \
vxbRead8 (FEI_HANDLE(pDev), (UINT8 *)((char *)FEI_BAR(pDev) + addr))
#define CSR_WRITE_1(pDev, addr, data) \
vxbWrite8 (FEI_HANDLE(pDev), \
(UINT8 *)((char *)FEI_BAR(pDev) + addr), data)
但是这些函数接口都不能直接使用,需要传递一个pDev的句柄,如果需要直接修改该如何做呢,我们顺藤摸瓜查看这些接口的原型,以vxbWrite8 为例。
vxbWrite8 ->vxbRegMap
STATUS vxbRegMap(VXB_RESOURCE * pRes /* description of register to map */
) {
VXB_RESOURCE_ADR *pResAdr = NULL;
... ... ... ... ...
if (VXB_RES_TYPE(pRes->id) == VXB_RES_IO) {
pResAdr->virtual = VXB_ARCH_IO_MAP(pResAdr->start, pResAdr->size);
pResAdr->pHandle = (void *)VXB_ARCH_IO_HANDLE;
} else {
pResAdr->virtual = VXB_ARCH_MEM_MAP(pResAdr->start, pResAdr->size);
pResAdr->pHandle = (void *)VXB_ARCH_MEM_HANDLE;
}
if (pResAdr->virtual == (VIRT_ADDR) PMAP_FAILED) {
return ERROR;
}
pRes->id |= VXB_RES_FLAG_MAPPED;
return (OK);
}
vxbRegMap 函数调用了VXB_ARCH_IO_MAP 和 VXB_ARCH_MEM_MAP 宏,这两个宏的定义如下:
#define VXB_ARCH_IO_MAP(paddr, len) \
((VIRT_ADDR) pmapGlobalMap(paddr, len, VXB_REG_MAP_MMU_ATTR))
#define VXB_ARCH_MEM_MAP(paddr, len) \
((VIRT_ADDR) pmapGlobalMap(paddr, len, VXB_REG_MAP_MMU_ATTR))
这个pmapGlobalMap函数进行最终的虚实地址转换,VXB_REG_MAP_MMU_ATTR的属性为关闭CACHE。所以应用程序直接调用pmapGlobalMap函数其实就可以访问寄存器的地址了。
debugSioBase = pmapGlobalMap(0x21C0500, 0x30, VXB_REG_MAP_MMU_ATTR);
debugSioBase[0] = 0XFFF;
LOCAL VIRT_ADDR _pmapGlobalMap(PHYS_ADDR physAddr, /* physical address */
size_t len, /* number of bytes to map */
UINT attr /* MMU attributes */
) {
VIRT_ADDR virtAddr;
size_t pageSize = vmPageSizeGet();
UINT mapAttr;
RTP_ID rtpId = MY_CTX_ID();
/* check if the function is being called in the kernel context. */
if (rtpId != kernelId) {
errno = S_adrSpaceLib_INVALID_CONTEXT;
return ((VIRT_ADDR) NONE);
}
mapAttr = attr | ADR_SPACE_OPT_RGN_KERNEL | ADR_SPACE_OPT_MAPPED | ADR_SPACE_OPT_NOPHYSALLOC | VM_MAP_GLOBAL;
virtAddr = adrSpacePageAlloc(0, (VIRT_ADDR) NULL, physAddr, len / pageSize, mapAttr);
return (virtAddr);
}
pmapGlobalMap函数还可以往下继续追溯,pmapGlobalMap->adrSpacePageAlloc->adrSpacePageMap->vmPgMap->MMU_GLOBAL_PAGE_MAP->mmuArchPageMap
还有另一篇相关的讲解文章:使用VxWorks 7内核 shell 来访问设备的寄存器