树莓派的cpu与gpu通信设计浅析

时间:2022-07-22
本文章向大家介绍树莓派的cpu与gpu通信设计浅析,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

树莓派的cpu与gpu通信设计浅析

  • 1.本文介绍
  • 2.树莓派的videocoreiv
  • 3.访问策略
  • 4.framebuff图像访问
  • 5.注意事项

1.本文介绍

异构设计在嵌入式开发过程中非常的重要,比如mcu与mpu的异构,还有两个不同架构的cpu或者两个不同架构的mpu等等。本文主要介绍树莓派的cpu与gpu通信的设计思想。并且通过在树莓派4上进行测试,测试访问gpu所提供的功能。

2.树莓派的videocoreiv

树莓派上电启动时,首先启动的是GPU,然后从sd卡中加载启动文件,紧接着启动CPU,所以GPU在学习使用树莓派时非常重要。可以通过下面的仓库看到底层的GPU的使用。

https://github.com/hermanhermitage/videocoreiv

要想CPU与GPU之间访问,首先需要了解两个设计的架构,下面从树莓派3b摄像头传输图像的角度去理解一下这个架构的设计。

BCM2835 SOC是芯片的设计架构,里面集成了一个ARM Cortex A53的CPU与VideoCore IV GPU。摄像头的MIPI数据传输线连接在GPU上,其摄像头SCCB连接在CPU上。

GPU上运行着一个RTOS,就是VCOS其实是基于ThreadX系统实现的。CPU与GPU共享RAM。当启动图像传输的时候,实际上就是首先由GPU出来图像时序,然后将图像放到RAM中,CPU与GPU通过VCHI管道进行通信,启动DMA将图像传递到CPU可以访问的内存区域。

那么GPU有哪些功能呢?

具体可以看下面的信息:

https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface

1.得到固件信息

2.电源管理

3.频率管理

4.内存管理

5.framebuff

6.摄像头

7.触摸屏

整体来看GPU的功能比较齐全。所以访问这些信息是如何进行的呢?

3.访问策略

如果要实现CPU与GPU的通信,树莓派做了一个控制器,就是一个独立的外设接口,叫做Mailbox Peripheral。这个Mailbox的外设的寄存器布局如下:

 0       4       8      12      16      20      24      28      32
       +-------+-------------------------------------------------------+
0x00   |rd chn |                     read data                         |
       +-------+-------------------------------------------------------+
0x04   |                             Unused                            |
...    \                                                             \
0x14   |                             Unused                            |
       +-----------------------------------------------------------+-+-+
0x18   |      status reserved                                      |E|F|
       +-----------------------------------------------------------+-+-+
0x1C   |                             Unused                            |
       +-----------------------------------------------------------+-+-+
0x20   |wt chn |                    write data                         |
       +-----------------------------------------------------------+-+-+

寄存器的起始地址为外设起始地址加速0xB880的偏移量。例如在树莓派4上其外设的地址为0xFE000000。上述就是CPU核操作GPU时的寄存器的布局。寄存器并不多,只需要判断状态即可。

当进行通信时,要往寄存器写的数据是什么?

#define MBOX_CH_POWER   0
#define MBOX_CH_FB      1
#define MBOX_CH_VUART   2
#define MBOX_CH_VCHIQ   3
#define MBOX_CH_LEDS    4
#define MBOX_CH_BTNS    5
#define MBOX_CH_TOUCH   6
#define MBOX_CH_COUNT   7
#define MBOX_CH_PROP    8

一般来说是有9个通道可以指定,每个通道有着特定的用途,如上定义所示。写数据(write data)实际上写的是一个消息列表的地址,这个消息列表可以是一个数组。叫做msgbox。这个地址一般都是要求4字节对齐的,因为上图寄存器中前面4字节是用于存放通道信息的。

一般一个消息的空间布局如下:

       0       4       8      12      16      20      24      28      32
       +---------------------------------------------------------------+
0x00   |                         Buffer Size                           |
       +---------------------------------------------------------------+
0x04   |                   Request/Response Code                       |
       +---------------------------------------------------------------+
0x08   |                             Tags                              |
...    \                                                             \
0xXX   |                             Tags                              |
       +---------------------------------------------------------------+
0xXX+4 |                           End Tag (0)                         |
       +---------------------------------------------------------------+
0xXX+8 |                           Padding                             |
...    \                                                             \
0xXX+16|                           Padding                             |
       +---------------------------------------------------------------+

获取版本信息,传递消息实际实现代码如下:

int bcm283x_mbox_hardware_get_revison(void)
{
    mbox[0] = 8*4;                          // length of the message
    mbox[1] = MBOX_REQUEST;                 // this is a request message
    
    mbox[2] = MBOX_TAG_HARDWARE_GET_REV;   
    mbox[3] = 4;                            // buffer size
    mbox[4] = 0;                            // len

    mbox[5] = 0;                    
    mbox[6] = 0;

    mbox[7] = MBOX_TAG_LAST;
    mbox_call(MBOX_CH_PROP, MMU_DISABLE);

    return mbox[5];
}

这样就将消息传递给CPU的寄存器,寄存器通过访问状态,并且将消息传递给GPU,GPU得到信息后,将消息填充,然后通过DMA将返回结果的消息包传递到原来的地址中,这样就可以实现基本的通信逻辑了。

具体的完整的实现细节可以参考rt-thread/bsp/raspberry-pi/raspi3-64/driver/mbox.c。

4.framebuff图像访问

上述基本上讲述了cpu和gpu的访问流程,那么如果想使用树莓派的hdmi接口进行图像显示,该如何进行设计呢?首先树莓派在设计的时候,并未在CPU集成图像控制接口,那只能通过GPU来实现了。访问其实就是利用mbox的通信进行实现,利用TAG的消息进行区分。

下面是framebuff相关TAG的宏定义

#define TAG_ALLOCATE_BUFFER         0x00040001
#define TAG_SET_PHYS_WIDTH_HEIGHT   0x00048003
#define TAG_SET_VIRT_WIDTH_HEIGHT   0x00048004
#define TAG_SET_DEPTH               0x00048005
#define TAG_SET_PIXEL_ORDER         0x00048006
#define TAG_GET_PITCH               0x00040008
#define TAG_SET_VIRT_OFFSET         0x00048009
#define TAG_END                     0x00000000

通过mbox的数组传递消息,其tag为TAG_ALLOCATE_BUFFER,并且指定图像的depth、width、height等参数。

当传递消息后GPU会将申请到的framebuff的地址通过mbox[5]传递回来,当访问这个地址的时候,实际上就是访问这个framebuff。我们不用去关心具体的时序逻辑问题,当然,可能需要注意的是图像传输完成之后的中断。

5.注意事项

在访问GPU的时候,需要注意的是寄存器的地址一定需要通过MMU映射成非cache的模式,否则可能会出现内存一致性问题,导致实际上通道的数据没有写到寄存器中。访问图像的时候,也需要注意这个问题,因为framebuff也需要非cache访问,这些都是在实际项目设计中需要注意的问题。总之在使用树莓派GPU和CPU的通信过程中,弄清楚BCM的SOC的设计思想,注意几个寄存器,并且注意消息的传输格式,那么访问GPU时就不是什么很大的问题了。其中比较有借鉴意义的是共享内存的方式访问,还有就是采用不同的tag进行消息机制的传递。