在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 来访问设备的寄存器