Mali硬件四大架构介绍:
Mali的四大架构之一:Utgard
第一代微架构Utgard(北欧神话人物:乌特加德)。这一代架构出来的比较早,主要是图形加速IP。可以追溯到2007年的mali-200。不过最让人惊讶的是mali-4xx系列,现在很多电视芯片都还在用这个IP。比如小米的智能电视,还有很多是mali-4xx系列的。
Utgard这一代vertex shader和fragment shader是分离的,arm官方支持的Opengl ES也只维护到2.0。所以Opengl ES 3.0及其以上要求的app是跑不了的。并且OpenCL在这一代中也是不支持的,因为这一代主打图形计算,不怎么支持通用计算。
移动端的GPU主要以基于tile的计算为主,mali的全系列(截止目前)都是基于tile的计算。基于tile的计算可以简单的认为,在进行计算的时候一张图会被划分成若干张小矩形,每个矩形可以认为是一个tile,然后对一个tile进行同时计算。
主要系列有:mali-200, mali-400, mali-450, mali-470
Mali的四大架构之二:Midgard
第二代微架构Midgard(北欧神话人物:米德加德)。Midgard这一代GPU开始属于同一着色器的架构,也就是上面说的vertex shader和fragment shader已经统一在一起了,相当于同一个shader计算单元可以处理多种着色器。当然也开始支持计算通用计算。特别是对OpenCL的支持,对通用计算有了很大的支持。OpenGLES 3.1虽然引入了compute shader,但是说起通用计算,OpenCL显然更加专业。
这个架构是基于128bit向量的,所以在编程的时候往往用4个float编程了能最大发挥其性能。当然,编译器也会把某些可以进行优化的计算合并成向量进行计算,不过最好在编码阶段自行优化。编译器编译的优化比较难以去把握。当然,也不建议用大于128bit的方式进行编程,最终需要编译器拆成多个数的运算,且每个数的位宽最大为128bit,如果编译器优化不好,反而会导致性能下降。
主要系列有:mali-t6xx, mali-t7xx, mali-t8xx
Mali的四大架构之三:Bifrost
第三代微架构Bifrost(北欧神话中连接天宫和大地的:彩虹桥)。由于这一代产品基本在2016年后发布的了,而OpenGLES在2016年后基本稳定了,所以相对于Midgard来说,在大方向上图形计算这块也没有多大的需要调整。
在Bifrost(Bifrost上更像是SIMT的模式,这里用SIMT表述也是我从多个文档资料推敲出来的)上会先把向量拆成标量,然后每个线程跑多维向量的第一维,因此对于三维向量 vec3向量最快只需要3个cycle,对于思维向量vec4最快只需要4个cycle。这里用了最快这个表述是因为并不是所有的指令都是单个cycle的。
当然,虽然bifrost架构是标量运算的,这是针对32bit的位宽来说的,如果是16bit位宽的计算,一个线程是可以在一个cycle内处理一个vec2的16bit数据的。因此在编程的时候,如果是8bit或者16bit的数据,用于应该考虑如何组织代码使得更有效的组合运算,例如16bit位宽的情况,尽量是用vec2,8bit位宽的尽量用vec4。
对于Bifrost,例如G76,一个shader core可以同时运行几十个线程,,从mali的资料显示,shader core一般由三个部分组成,ALU,L/S,TEXTURE三个主要模块。在G76上是8-wide wrap的,一般设置为3个ALU。(其余的型号可能不一样,例如G51/G72是4-wide wrap的,G72同样是3个ALU;G52跟G76一样,不过G52可配置成2个ALU的)
对于AI加速方面,部门系列也有一些指令修改,例如G52和G76都引入了int8 dot指令,该指令针对神经卷积网络的运算做了优化。
主要系列有:mali-g31, mali-g51, mali-g71, mali-g52, mali-g72, mali-g76
Mali的四大架构之四:Valhall
第四代微架构Valhall是2019年第二季度推出来的。该系列的是基于超标量实现的。对于G77,使用的时16-wide的wrap,单个shader core集成两个计算引擎。
主要系列有:mali-g57, mali-g77
GPU驱动简介
GPU 驱动一般分为两部分:一小部分在 Linux 内核中,另外一大部分在 userspace,在 usercapce 的部分向下操作内核中的驱动,向上对应用层提供标准的 OpenGL 接口,这样各种图形显示相关的应用才能通过标准的 OpenGL API 进行图形渲染加速。
但是 mali GPU IP 提供商 Arm 公司只开放了内核部分驱动,而且这部分驱动还没有按照 linux kernel 的规范以 Drm 的框架去实现,所以它无法被 linux mainline 接受,还有一个更重要的部分,usersapce 部分,Arm 没有开源,只是以库的形式提供给购买了 mali GPU 授权的 SOC 厂商,比如 Rockchip,Amlogic。而且这套代码主要是为 Android 系统设计的,对 Debian、Ubuntu 这种系统的兼容性也不好。
如果想跑 mainline 的内核,基本就没法使用 GPU 加速了,这也是为什么我们目前看到的大部分开发板如果搭载了 mainline 内核,基本都不会有 GPU 加速功能,或者直接就不开图形显示功能。
有黑客们逆向了 Arm 发布的二进制库,然后重写了针对 Arm mali gpu 的开源驱动,最终在 Linux 5.2 发布的时候合并到了 mainline 中:针对 Mali-400/Mali-450 的驱动叫做 lima,针对 Mali-T6xx / Mali-T7xx / Mali-T8xx GPU 和 GXX 系列的叫做 panfrost。Usersacpe 部分的开源库叫做 mesa,对 mali gpu 的支持从 mesa 19.2 开始。
Dts定义
gpu: gpu@2d000000 {
compatible = "arm,juno-mali", "arm,mali-t624", "arm,malit6xx", "arm,mali-midgard";
reg = <0 0x2d000000 0 0x10000>;
interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "job", "mmu", "gpu";
clocks = <&scmi_dvfs 2>;
power-domains = <&scmi_devpd 9>;
dma-coherent;
/* The SMMU is only really of interest to bare-metal hypervisors */
/* iommus = <&smmu_gpu 0>; */
//status = "disabled";
};
panfrost_driver
static struct platform_driver panfrost_driver = {
.probe = panfrost_probe,
.remove = panfrost_remove,
.driver = {
.name = “panfrost”,
.pm = &panfrost_pm_ops,
.of_match_table = dt_match,
},
};
module_platform_driver(panfrost_driver);
panfrost_probe
panfrost_probe
-》ddev = drm_dev_alloc(&panfrost_drm_driver, &pdev->dev); //分配一个 drm device,drm_device用于抽象一个完整的DRM设备
->drm_dev_init(dev, driver, parent)
->drm_minor_alloc
->idr_alloc(&drm_minors_idr, //从这里可以看出,/dev/dri/cardN,占用0-63次设备号,/dev/ dri /controlDN占用64-127次设备号,/dev/ dri /renderDN占用128到191
NULL,
64 * type,
64 * (type + 1),
GFP_NOWAIT)
->drm_sysfs_minor_alloc(struct drm_minor *minor)
->if (minor->type == DRM_MINOR_CONTROL)
minor_str = “controlD%d”;
else if (minor->type == DRM_MINOR_RENDER)
minor_str = “renderD%d”;
else
minor_str = “card%d”;
dev_set_name(kdev, minor_str, minor->index)
-> *drm_minor_get_slot(dev, type) = minor // drm_minor_register时,会取出调用device_add()
-》panfrost_device_init(pfdev)
-》panfrost_clk_init(pfdev)
-》panfrost_gpu_init //注册 “gpu” 硬件中断
-》panfrost_gpu_soft_reset(pfdev)
-》gpu_write(pfdev, GPU_CMD, GPU_CMD_SOFT_RESET)
-》devm_request_irq(pfdev->dev, irq, panfrost_gpu_irq_handler, IRQF_SHARED, KBUILD_MODNAME “-gpu”, pfdev) //注册”gpu”中断处理函数
-》panfrost_mmu_init(pfdev) //注册 “mmu” 硬件中断
-》panfrost_job_init(pfdev) //注册 “job” 硬件中断
-》panfrost_perfcnt_init(pfdev)
-》drm_dev_register(ddev, 0) //调用 DRM core接口,注册一个DRM device
->drm_minor_register(dev, DRM_MINOR_CONTROL);
->device_add(minor->kdev)
->drm_minor_register(dev, DRM_MINOR_RENDER);
->drm_minor_register(dev, DRM_MINOR_PRIMARY);
->dev->driver->load(dev, flags) // panfrost driver中没有定义load()接口,所以这里为空
->drm_modeset_register_all(dev)
->drm_plane_register_all(dev); //调用各自的 late_register()接口
->drm_crtc_register_all(dev);
->drm_encoder_register_all(dev); //遍历dev->mode_config.encoder_list
->drm_connector_register_all(dev); //遍历dev->mode_config.connector_list
-》panfrost_gem_shrinker_init(ddev)
panfrost_drm_driver
static struct drm_driver panfrost_drm_driver = {
.driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ,
.open = panfrost_open,
.postclose = panfrost_postclose,
.ioctls = panfrost_drm_driver_ioctls,
.num_ioctls = ARRAY_SIZE(panfrost_drm_driver_ioctls),
.fops = &panfrost_drm_driver_fops, //对/dev/dri/cardX 进行open/dri/ioctl时,会调用到这里的操作集合
.name = “panfrost”,
.desc = “panfrost DRM”,
.date = “20180908”,
.major = 1,
.minor = 1,
.gem_create_object = panfrost_gem_create_object,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_import_sg_table = panfrost_gem_prime_import_sg_table,
.gem_prime_mmap = drm_gem_prime_mmap,
};
drm_encoder_init
drm_encoder_init(drm, &priv->encoder, &xxx_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL);
->list_add_tail(&encoder->head, &dev->mode_config.encoder_list) //将 encoder加到链表dev->mode_config.encoder_list 中
drm_connector_init
drm_connector_init(drm, &priv->connector, xxx_connector_funcs, _MODE_CONNECTOR_HDMIA)
-> list_add_tail(&connector->head, &config->connector_list); //将 connector加到链表dev->mode_config. connector_list 中
提交JOB给GPU的具体实现
static const struct drm_ioctl_desc panfrost_drm_driver_ioctls[] = {
#define PANFROST_IOCTL(n, func, flags)
DRM_IOCTL_DEF_DRV(PANFROST_##n, panfrost_ioctl_##func, flags)
PANFROST_IOCTL(SUBMIT, submit, DRM_RENDER_ALLOW), //提交一个JOB 到硬件
PANFROST_IOCTL(WAIT_BO, wait_bo, DRM_RENDER_ALLOW),
PANFROST_IOCTL(CREATE_BO, create_bo, DRM_RENDER_ALLOW),
PANFROST_IOCTL(MMAP_BO, mmap_bo, DRM_RENDER_ALLOW),
panfrost_drm_driver_fops的定义
DEFINE_DRM_GEM_FOPS(panfrost_drm_driver_fops);
#define DEFINE_DRM_GEM_FOPS(name)
static const struct file_operations name = {
.owner = THIS_MODULE,
DRM_GEM_FOPS,
}
#define DRM_GEM_FOPS
.open = drm_open,
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
.compat_ioctl = drm_compat_ioctl,
.poll = drm_poll,
.read = drm_read,
.llseek = noop_llseek,
.mmap = drm_gem_mmap
drm_ioctl
上层的libdrm将会通过ioctl()调用到drm_ioctl,进而再调用到drm_ioctls[] 中定义的一系列API
drm_ioctl
-》drm_ioctls[]
panfrost_drm_driver_ioctls 定义
所有的上层应用,都是通过这个ioctl 数组中定义的各个接口操作GPU,这个里面的函数特别多,这里只列出几个:
static const struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_RENDER_ALLOW),
DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0),
DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0),
DRM_LEGACY_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_legacy_irq_by_busid,
DRM_MASTER|DRM_ROOT_ONLY),
PANFROST_IOCTL(GET_PARAM, get_param, DRM_RENDER_ALLOW),
PANFROST_IOCTL(GET_BO_OFFSET, get_bo_offset, DRM_RENDER_ALLOW),
PANFROST_IOCTL(PERFCNT_ENABLE, perfcnt_enable, DRM_RENDER_ALLOW),
PANFROST_IOCTL(PERFCNT_DUMP, perfcnt_dump, DRM_RENDER_ALLOW),
PANFROST_IOCTL(MADVISE, madvise, DRM_RENDER_ALLOW),
};
原文链接:https://blog.csdn.net/weixin_43082062/article/details/139595195