树莓派的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进行消息机制的传递。