> For the complete documentation index, see [llms.txt](https://jizhuoran.gitbook.io/mali-gpu/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://jizhuoran.gitbook.io/mali-gpu/mali-gpu-driver/dao-sheng-yi-yi-sheng-er-er-sheng-san-san-sheng-wan-wu.md).

# 道生一，一生二，二生三，三生万物

《道德经》第四十二章：道生一，一生二，二生三，三生万物。万物负阴而抱阳，冲气以为和。

许久没有更新这本书了，实在不是因为我不勤快，而是另有原因：因为我懒。。。这篇文章我给他分到了一个全新的章节下，这个章节的名字叫做“实事儿”，也是勉励自己少说空话，多干实事儿吧。

这个题目起的也颇为讲究，取自道德经的第四十二章，讲的是宇宙的生成观，万物由道起，道生一，一边能生二，有二便有三，有三则有万物。那么我是不是放弃CS转行去干老子（这里没有任何意思占大辈，你们都xiao坏了）研究了，我仍然在搞着计算机科学，虽然形象日益趋紧于一个三十二岁的工地上的项目经理。

跑题了，当然我不可能改了讲干货先兑水的习惯。之所以把这章的题目定为道，是因为我最近写了一个GPU的simulator，思来想去，我决定把这个simulator起名为DAO，希望能由此生一，生二，乃至生万物。当然，希望“一”不是一厢情愿，“二”不是二了吧唧，“三”不是san跑。。。

在此我要做个声明啊，这个simulator是用来simulate移动端GPU的，他只是恰好能和Mali的driver进行交互，又恰好表现的和Mali，主要是G72这个型号表现的比较类似。但是没有任何意思这个是模仿了G72的一些东西，毕竟我的肉眼观察大发不能观察到芯片的构成，ARM方面也没有开放任何资料供大家参考。所以一切的一切都是巧合，你说巧不巧？另外，我临时并没有公开代码，主要原因还是怕叫人告了，你说人家是大公司，我就是poor student，小混混碰上大流氓，搞不过搞不过。当然我会适时的open-source代码，而且这些文章会讲的比较细致，所以照着葫芦画~~嫖~~瓢，自己实现也不是很惨。这一段是正文哦，别以为还在谈论闲白，我也是有原则的，我会定义一部分内容为干货。。

首先，别看现在把GPU吹得这么牛逼，无论是AI还是科学计算都离不开GPU。说白了GPU就是个外设设备，跟打印机，甚至是一个LED灯没有什么本质的区别。对GPU的控制是通过写寄存器完成的，我们想知道GPU的信息也要去读寄存器，无非是支持了DMA（其实在Mali里，CPU和GPU可以读同一块块儿内存，所以并不是完全意义上的DMA），和虚拟地址翻译（IOMMU）。

既然我们看清楚了GPU了本质，那么就能想到一个事情，在初始化的时候，我们需要从GPU的一些寄存器里读取出一些数值，这些数值代表了这个GPU的一些信息，我们也要写一些寄存器，才表示我们想如何去config这个GPU（主要是是电源管理（PM）策略），以及我们想要何种中断等等。

正如之前所说的，驱动在启动的时候一般要probe设备，读取一些值来setup一些变量，例如GPU的型号，核心的数量，MMU的features。这些变量在之后的运行中至关重要，所以想要驱动能顺利运行，我们必须正确的setup这些变量。这些变量以及他们的值如下所示，可以看到里面有GPU的ID，有MEM的features。这些值可以让Mali的driver误以为自己真的在和一个Mali的GPU接上头了，没想到被我给骗了，哈哈哈。

```
GPU_ID = 0x62210001; //(RO)
L2_FEATURES = 0x07130206; //(RO)
SUSPEND_SIZE = 0x00000000; //(RO)
TILER_FEATURES = 0x00000809; //(RO)
MEM_FEATURES = 0x00000101; //(RO)
MMU_FEATURES = 0x00002830; //(RO)
AS_PRESENT = 0x000000ff; //(RO)
JS_PRESENT = 0x00000007; //(RO)

JS0_FEATURES = 0x0000020e; //(RO)
JS1_FEATURES = 0x000004fe; //(RO)
JS2_FEATURES = 0x0000007e; //(RO)
JS3_FEATURES = 0x00000000; //(RO)
JS4_FEATURES = 0x00000000; //(RO)
JS5_FEATURES = 0x00000000; //(RO)
JS6_FEATURES = 0x00000000; //(RO)
JS7_FEATURES = 0x00000000; //(RO)
JS8_FEATURES = 0x00000000; //(RO)
JS9_FEATURES = 0x00000000; //(RO)
JS10_FEATURES = 0x00000000; //(RO)
JS11_FEATURES = 0x00000000; //(RO)
JS12_FEATURES = 0x00000000; //(RO)
JS13_FEATURES = 0x00000000; //(RO)
JS14_FEATURES = 0x00000000; //(RO)
JS15_FEATURES = 0x00000000; //(RO)
/*
    这里必须要着重说明一下，ARM真的是连自己都骗，
    明明只有三个JS，还是要咬着牙弄16个寄存器，
    然后读一堆0出来。
*/
TEXTURE_FEATURES_0 = 0xffffffff; //(RO)
TEXTURE_FEATURES_1 = 0x00ff0fff; //(RO)
TEXTURE_FEATURES_2 = 0xfe3fffff; //(RO)
TEXTURE_FEATURES_3 = 0x00000000; //(RO)

THREAD_MAX_THREADS = 0x00000180; //(RO)
THREAD_MAX_WORKGROUP_SIZE = 0x00000180; //(RO)
THREAD_MAX_BARRIER_SIZE = 0x00000180; //(RO)
THREAD_FEATURES = 0x0a046000; //(RO)

SHADER_PRESENT_LO = 0x00000fff; //(RO)
SHADER_PRESENT_HI = 0x00000000; //(RO)
TILER_PRESENT_LO = 0x00000001; //(RO)
TILER_PRESENT_HI = 0x00000000; //(RO)

L2_PRESENT_LO = 0x00000001; //(RO)
L2_PRESENT_HI = 0x00000000; //(RO)

STACK_PRESENT_LO = 0x0000000f; //(RO)
STACK_PRESENT_HI = 0x00000000; //(RO)

COHERENCY_FEATURES = 0x00000002; //(RO) 
```

当然大家都能看出来每一行后面的（RO）代表的是READ ONLY，但是这个READ ONLY是指这个寄存器不能被CPU写，而不是这个值不会改变，GPU本身还是能改变这些寄存器的值。所以这些值是给驱动开发者看的，我一个写simulator的，是要去更改某些只读寄存器的，毕竟我在假装自己是一个~~蘑菇~~GPU。。。其实我根本也没有啥寄存器，弄一个长一点的数组，起个名字叫寄存器，谁不信就打谁。

这些值都是恰好和 麒麟970上面搭载的Mali　G72上对应的寄存器上的值相等。获得这些值真的是艰难而困苦，那么多数，怎么能都猜对，这概率多么低，还愣是叫我都猜对了。。。好吧，谁也没有这样的运气，这些数值是通过自己修改并编译的Linux内核，在执行的时候强行打印出来的，由于这个内核会打印很多不应该被打印出来的东西（每读一个寄存器就log一下），我给他起了个名字叫“话唠”。。。

下一期应该会讲我是怎么假装中断的。应该。。。
