道生一,一生二,二生三,三生万物

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

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

这个题目起的也颇为讲究,取自道德经的第四十二章,讲的是宇宙的生成观,万物由道起,道生一,一边能生二,有二便有三,有三则有万物。那么我是不是放弃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一下),我给他起了个名字叫“话唠”。。。

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