mailbox原理

用于在片上处理器之间通信的一种mailbox队列中断机制,mailbox队列中断机制允许软件通过一组寄存器和关联的中断设置和得到信息在二个处理之间建立通信渠道。

mailbox寄存器解析

MAILBOX_SYSCONFIG寄存器

该寄存器控制通讯接口的各种参数,如下图所示可以控制复位的操作,空闲模式等
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

MAILBOX_MESSAGE_m寄存器

如下图所示MAILBOX_MESSAGE_m寄存器为消息寄存器,用于存储邮箱的下一条要读取的消息,读取完将从FIFO队列中删除该消息。
在这里插入图片描述

如下图所示,使用mailbox2 m= 0 to 11,所以一个mailbox有12个消息寄存器。,对应了也是12个可以存放消息数据的FIFO
在这里插入图片描述

MAILBOX_FIFOSTATUS_m寄存器

如上面所说的每个消息寄存器对应一个FIFO,FIFO是一种先进先出的缓存区,其有空间大小的限制,所以MAILBOX_FIFOSTATUS_m寄存器用于查看是否FIFO的状态已经装满数据了。
在这里插入图片描述

MAILBOX_MSGSTATUS_m寄存器

如下图所示MAILBOX_MSGSTATUS_m寄存器用于读取每个消息缓存区有多少个未读消息。
在这里插入图片描述

注意:每一个消息缓存区最多只能存放4个消息

MAILBOX_IRQSTATUS_CLR_u寄存器

MAILBOX_IRQSTATUS_CLR_u寄存器是中断状态寄存器,其中每个用户user都有一个中断状态寄存器,比如MAILBOX2 就有四个用户(user0 ~ user3)就有对应的四个中断状态寄存器,每一个中断状态寄存器最多可以表示16个消息队列的状态。(0~15 message)。
在这里插入图片描述

每个消息队列的状态分别又下面二位所代表,一个代表邮箱是否装满的状态位,一个代表邮箱是否有新的消息的状态位。当读取到状态位为1时表示中断状态位响应,然后写入1到对应的位置就可以关闭中断状态位。而是否装满的状态位是代表如果这个FIFO缓存区如果不是full就是触发中断。
在这里插入图片描述

MAILBOX_IRQENABLE_SET_u寄存器

MAILBOX_IRQENABLE_SET_u寄存器用于使能消息中断,跟MAILBOX_IRQSTATUS_CLR_u寄存器一样,每个用户都有对应一个寄存器。
在这里插入图片描述

对应上面有二种中断状态位的使能,当读出是1时表示这一位已经使能了,当写入1的时候将是对这个消息的中断进行使能。
在这里插入图片描述

MAILBOX_IRQENABLE_CLR_u寄存器

MAILBOX_IRQENABLE_CLR_u寄存器的作用与MAILBOX_IRQENABLE_SET_u寄存器相反,当写入1时是清除中断位。
在这里插入图片描述
在这里插入图片描述

核间通信方案

如下图所示ARM核和DSP核进行mailbox通信,ARM要发送数据给DSP,1 ARM核先往某个指定的共享内存空间buffer写入数据,然后MAILBOX触发中断和写入关于共享内存空间的地址信息给DSP。2 DSP通过得到mailbox中断的信息读取共享内存空间ARM核发送的buffer数据。3 DSP核读取完数据后将触发mailbox中断给ARM作为回应,告诉ARM核你发送的数据我已经接受完成了。
在这里插入图片描述

MAILBOX驱动代码

MAILBOX用户中断绑定

IRQCROSSBAR 与中断号ID进行绑定,这里将IRQCROSSBAR237 绑定了中断ID 58号。也就是user0。

//                dsp1_pads: dsp1pads
//                    {
//                    pin-set = <
//                               /* MPU_IRQ_26(ID58) 连接 MAILBOX2_IRQ_USER0 (IRQ_CROSSBAR_237) */
//                               /* MPU_IRQ_27(ID59) 连接 MAILBOX2_IRQ_USER2 (IRQ_CROSSBAR_239) */
                              0x0a70 0x00ed00ef

在这里插入图片描述

中断服务函数绑定

1.通过调用vxbResourceAlloc函数对中断号进行资源分配。

   pClkRegRes = vxbResourceAlloc(pDev, VXB_RES_MEMORY, 0);
    if(NULL == pClkRegRes)
    	goto errOut;
    
    pDrvCtrl->intRes = pResIrq; //中断资源
    interrupts = <58 0 4>;      //mailbox user0中断

2.使用vxbIntConnect函数将中断号与中断服务函数vxbDspIpcInt进行绑定,然后传递的参数为pDev,然后使用vxbIntEnable函数进行使能

    /* 连接中断服务函数 */
    rtn = vxbIntConnect(pDev, pDrvCtrl->intRes, vxbDspIpcInt, pDev);

    /* 使能中断 */
    rtn = vxbIntEnable(pDev, pDrvCtrl->intRes);

二进制信号量解析

这是在attach函数中初始化二进制信号量,然后将获得的信号量ID填充到pDrvCtrl结构体的accessSemId成员

    accessSemId = semBCreate (SEM_Q_FIFO, SEM_FULL);
    if (accessSemId == SEM_ID_NULL)
        goto errOut;

    pDrvCtrl->accessSemId = accessSemId;

如下面代码所示创建二进制信号量对应有二个参数一个是指信号量模式选项,一个是信号量初始化状态

信号量模式选项

1 SEM_Q_PRIORITY (0x1) 信号量优先级模式

2 SEM_Q_FIFO (0x0) FIFO先进先出模式

信号量初始状态

1.SEM_FULL (1) 信号量空表示第一次调用时将可以获取信号量

2.SEM_EMPTY (0) 信号量满表示第一次调用需要释放信号量才可以获取信号量

SEM_ID semBCreate
    (
    int         options,       /* semaphore options */
    SEM_B_STATE initialState   /* initial semaphore state */
    )  

获得信号量,如果获取成功返回OK,获取失败返回ERROR。其中获取信号量有二种模式一种是NO_WAIT不等待模式,一种是永恒等待模式 WAIT_FOREVER 。如果是NO_WAIT模式当调用时没有获得信号量将会马上返回。如果是WAIT_FOREVER模式没有获得信号量就将会一直等待不返回直到等到有人释放信号量,才会返回。

semTake (pDrvCtrl->accessSemId, NO_WAIT)

当有任务已经调用semTake函数获得了信号量,需要使用semGive函数对信号量进行释放,才能使用semTake函数再次获取信号量。

semGive (pDrvCtrl->accessSemId)

调用访问

当调用驱动的open函数时将会对使能中断寄存器进行操作,对用户1(user1)对应的中断使能寄存器进行操作,写入对应的消息邮件中的新消息事件中断使能。

    CSR_WRITE_4(pDrvCtrl,
                MAILBOX_IRQENABLE_SET(pDrvCtrl->localUser), 
                MAILBOX_IRQ_NEWMSG(pDrvCtrl->mbMsgRx));
#define MAILBOX_IRQENABLE_SET(u)    (0x108 + 0x10 * (u))    //FIFO未满/新消息事件中断使能寄存器
#define MAILBOX_IRQ_NEWMSG(m)        (1 << (2 * (m)))        //新消息事件中断标志位是2m

vxbDSPIosWrite函数是往某个消息邮箱写入32bit数据,这样就会触发上面open函数建立的user1中断,因为其使能新消息中断的消息邮箱收到了消息将会触发user1的中断。

//写邮箱消息,产生中断
LOCAL ssize_t vxbDSPIosWrite
    (
    DSP_DRVCTRL * pDrvCtrl,
    char * buffer,
    int num
    )
	{
    CSR_WRITE_4(pDrvCtrl, MAILBOX_MESSAGE(pDrvCtrl->mbMsgTx), *(UINT32*)buffer);
    return num;
	}
中断触发

当消息邮箱得到新消息时会得到触发中断进入中断服务函数。在中断服务函数中先读取中断的状态,然后读取消息里的新消息,接着再将中断进行清除。


LOCAL void vxbDspIpcInt 
	(
			VXB_DEV_ID pDev
	)
	{
    UINT32 reg;
    DSP_DRVCTRL *pDrvCtrl;

    //获得设备上下文
    pDrvCtrl = (DSP_DRVCTRL *)vxbDevSoftcGet(pDev);
    //读取中断状态
    reg = CSR_READ_4(pDrvCtrl, MAILBOX_IRQSTATUS_CLR(pDrvCtrl->localUser));

    //检查是否在对应的位置发生了中断
    if(reg & (1 << pDrvCtrl->mbMsgRx))
    {
        //读消息
        reg = CSR_READ_4(pDrvCtrl, MAILBOX_MESSAGE(pDrvCtrl->mbMsgRx));
        pDrvCtrl->mbMsg = reg;
        //测试使用,搬数据

        //存在未处理消息标志置1
        pDrvCtrl->unhandledMsg = 1;
    
        //清除新消息中断标志位
        CSR_WRITE_4(pDrvCtrl,
                    MAILBOX_IRQSTATUS_CLR(pDrvCtrl->localUser),
                    MAILBOX_IRQ_NEWMSG(pDrvCtrl->mbMsgRx));
    }
    
	}
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐