首先,我们跑去ARM,问它有没有现成的系统。ARM说有啊,A73/G71/视频/显示/ISP/总线/系统控制/内存控制器/Trustzone全都帮你集成好了,CPU和GPU后端也做了,还是16nm的,包你性能和功耗不出问题。然后我们再跑到Synopsys或者Cadence买EDA工具,把仿真平台也一起打包了,顺带捎上周边IP和PHY。至于基带,Wifi和蓝牙,先不做吧,毕竟是第一次攒,要求不能太高。
A73MP4@3Ghz,A53MP4@1.6Ghz,G71MP8@850Mhz,显示4K分辨率双路输出,视频4K,支持VR,支持Trustzone,内存带宽25.6GB/s(LPDDR4x64@3200Gbps)。
有了这张图,我们就可以对每个模块的性能,功耗,面积还有系统性能进行详细分析了。本文会涉及到一些基本概念,之前的文章(https://zhuanlan.zhihu.com/p/24878742?refer=c_70349842)介绍过,这篇里就不重复了。
工艺:TSMC16FFLL+
MP4静态功耗:251mW@TT/0.8V/85C
面积:6.7mm,MP4, 64KB L1D, 2MB L2, No ECC, with NEON&FP
也就是说,跑在极限3Ghz的时候,动态功耗是200x1.25x1.25x3=937mW,四核得上4W,而手机SoC最多也就能稳定跑在3W。跑在2.5G时候,动态功耗是200x2.5=500mW,MP4总功耗2.25W,可以接受。
工艺:TSMC16FFLL+
MP4静态功耗:63mW@TT/0.8V/85C
面积:3.9mm,MP4, 32KB L1D, 1MB L2, No ECC, with NEON&FP
四核跑在1.6Ghz时功耗463毫瓦,加上A73MP4,一共2.7瓦。
A53的:
作为CPU,集成到SoC中的时候,一个重要的参数是访存延迟。有个数据,在A73上,每减少5ns的访存时间,SPECINT2K分数就可以提高1%。总的延迟如下:
有的设计会把CCI550以及它上面的CPU,GPU作为一个子网挂在NoC下,由NoC连到内存控制器。这样的好处是可以把交织和调度交给NoC,坏处是凭空增加额外一层总线10-20ns的延迟,CPU的跑分就要低了2-4%。在ARM的方案中,交织由CCI直接完成,而调度交给内存控制器。既然所有主设备的访问都需要到DDR,那由DMC来调度更合适。再加上没有采用两层总线的链接方式,一共可以去掉两层异步桥,省掉12个cycle,已经快占到整个通路静态延迟的五分之一了。所以,现在我看到的主流总线拓扑,都是把CCI直接连内存控制器。那可不可以直接把CPU连内存控制器?只要DMC的端口够多,也没什么不可以,但是这样一来大小核就不能形成硬件支持的SMP了,功能上不允许。
静态延迟算完,我们来看带宽和动态延迟。
那为什么不通过增加总线的数据位宽来移除这个瓶颈呢?主要是是位宽增加,总线的最大频率相应的会降低,这是物理特性。同时我们可能会想,真的有需要做到更高的带宽吗,CPU那里发的出来吗?我们可以计算一下。先看单个A73核,A73是个乱序CPU,它的读来自于三类方式,一个是读指令本身,一个是PLD指令,一个是一级缓存的预取。A73的一级缓存支持8路预取,每路预取范围+/-32,深度是4,预取请求会送到PLD单元一起执行,如下图。所以他们同时受到BIU上Cacheable Linefill数目的限制,也就是8.此外还有一个隐含的瓶颈,就是A73的LSU有12个槽,读写共享,所有的读写请求(包括读写指令,PLD和反馈过来的预取)都挂在这12个槽中独立维护。
到了二级缓存这,预取可以有27次。当一级缓存的BIU发现自己到了4次的极限,它可以告诉二级缓存去抓取(Hint)。单个核的时候,4+27<48,瓶颈是在一级缓存的BIU;如果四个核同时发多路请求,那就是4x8=32个OT,再加上每路都可以请求二级缓存,其总和大于ACE的48个Cacheable读请求。按照带宽算,每核是10GB/s,然后CPU的ACE口的读是48*64B/50ns=60GB/s。虽然60GB/s大于4x10GB/s,看上去像是用不满,但是一级缓存会提示二级缓存自动去预取,所以,综合一级二级缓存,簇内的瓶颈还是在ACE接口上,也意味着总线和核之间的瓶颈在总线带宽上,并且60GB/s远高于10.8GB/s。
这里我简单计算下各个模块需要的带宽。
G71MP8在跑Manhattan时每一帧需要370M带宽(读加写未压缩时),850Mhz可以跑到45帧,接近17GB/s,压缩后需要12GB/s。
视频需求带宽少些,解压后是2GB/s,加上解压过程当中对参考帧的访问,压缩后算2GB/s。
当然,以上这些模块不会全都同时运行,复杂的场景下,视频播放,GPU渲染,CPU跑驱动,显示模块也在工作,带宽需求可以达到20GB/s。
而我们也不能无限制的增加内存控制器和内存通道,一个是内存颗粒成本高,还有一个原因是功耗会随之上升。光是内存控制和PHY,其功耗就可能超过1瓦。所以我们必须走提高带宽利用率的路线。
对于CPU,先要保证它的延迟,然后再是带宽。
对于视频和ISP,带宽相对来说不大,我们要保证它的实时性,间接的需要保证带宽。
其余的模块可以相对放在靠后的位置考虑。
它一共可以有7个ACE/ACE-Lite进口,读写通道分开,地址共用,并且会进行竞争检查,每cycle可以仲裁2个地址请求。之前我们只计算了独立的读写通道带宽,那共用的地址会是瓶颈吗?算一下就知道。对于CPU和GPU,所有从端口出来的传输,无论是不是Cacheable的,都不超过64字节,也就是16x4的突发。一拍地址对应四拍数据,那就是可以同时有八个端口发起传输,或者4个通道同时发起读和写。假设在最差情况下,CPU+GPU同时发起shareable读请求,并且,读回去的数据全都引起了eviction,造成同等数量的写。此时数据通道上不冲突,地址通道也正好符合。如果是CPU+GPU同时发起shareable写请求,并且全都命中别人的缓存,引起invalidate,读通道此时空闲,但是invalidate占用地址,造成双倍地址请求,也符合上限。到DMC的地址不会成为瓶颈,因为共四个出口,每周期可以出4个。这里,我们使用的都是shareable传输,每次都会去Snoop Filter查找,每次可能需要两个对TAG RAM的访问(一次判断,一次更新标志位,比如Invalidate),那就是每cycle四次(地址x2)。而我们的Tagram用的是2cycle访问延迟的2块ram,也就是说,需要同时支持8个OT。这一点,CCI550已经做到了。在以上的计算中,DMC是假设为固定延迟,并且OT足够,不成为瓶颈。实际情况中不会这么理想,光是带宽就不可能满足。
在出口,如果是shareable的传输,会需要去查表实现snooping,此时所有的请求会放在一起,它的大小按照四个出口,每个带宽2x10.8GB/s,共86.4GB/s。但是,由于我们DDR最多也就25.6GB/s,计算OT时候延迟按照读的75ns(85-10,CCI本身的延迟可以去掉了,写延迟小于读,按照读来计算),75x25.6/64=30。如果是non-shareable传输,那就还是使用入口的OT。
同样,增加缓存也可以减少总线动态延迟,下图一目了然:
关于CCI550的资源配置,还有最后一项,即tag ram的大小。CCI550的特点就是把各个主设备内部的缓存标志位记录下来提供给Snooping操作,那这个tag ram的大小该如何定。理论上,最大的配置是等同于各级exclusive缓存的tag ram总合。如果小于这个值,就需要一个操作,叫做back invalidation,通知相应的缓存,把它内部的标志位去掉,免得标志位不一致。这个是由于tag ram本身的大小限制引入的操作,在执行这个操作过程中,同地址是有竞争的,这个竞争是多余的,并且会阻塞所有缓存对这一地址的snooping操作,所以需要尽量避免。ARM定义了一个CCI550中的tag ram和原始大小的比例,使得back invalidation保持在1-2%以下,参考下图:
其他的配置参数还有地址线宽度,决定了物理地址的范围,这个很容易理解。还有传输ID的宽度,太小的话没法支持足够的OT。
在这里,读写比例是2:1,也就是说出口收到的请求最多64+32=96GB/s。但我们可以看到有时是可以做到超越100GB/s的,为什么呢?因为有相当部分是命中了内部的tag,转而从缓存进行读操作。我们之前算过,Snooping操作在CCI550内部访问tag ram是足够的,但是如果命中,就需要从别人的缓存读数据,而这就收到上级缓存访问窗口的限制。所以我们看到当命中率是100%,总带宽反而大大下降,此时的瓶颈在访问上级缓存。所幸的是,对于SMP的CPU,通常命中率都在10%以下,正处于带宽最高的那段。至于通用计算,是有可能超过10%的,等到GPU之后再讨论。
不过,一个没法避免的事实是,总线的入口带宽是86.4GB/s,出口却只能是25.6GB/s。我们需要一些方法来来达到理论带宽。在总线上,可以使用的方法有调度和交织。
而交织在之前的文章中也提过。 在CCI550上,只接了CPU和GPU,而他们发出的所有传输都不会超过64字节,也就是一个缓存行长度。只有一种可能的例外,就是exclusive传输,在spin lock中会用到。如果有一个变量跨缓存行,那么就有可能产生一个128字节的传输。而由于这类可能存在的传输,CCI550可以设置的最小粒度是128字节,来避免它被拆开到两个内存控制器,破坏原子性。不过到目前为止,CPU/GPU都不会发出128字节传输,如果有跨缓存行的变量,直接切断。这样虽然会造成软件逻辑上的错误,但这错误是软件不遵守规范引起的,ARM软件规范里明确禁止了这类问题。
这里就引出了一个问题,到底我们交织的粒度是多少合适?粒度越大,越不容易分散到各个DMC,而粒度越小,越不容易形成对某个DMC和DDR Bank的连续访问。
oChannel_Sel[0] = Addr[8] ^ Addr[10] ^ Addr [13] ^ Addr [21]
然后在DMC,使用了这样的变化来使访问分散到各个Bank:
经过哈希化后,在CPU的顺序地址上效果如下:
显然,在256字节的交织颗粒时效果最好,尤其是对于连续地址。对于随机地址,有个细节我们可以注意下,在颗粒度64/128字节的时候最好,这其实也符合预期。
调整完这么多参数,还有一个最根本的问题没有解决:DDR理论带宽也只有25.6GB/s,各个主设备一起访问,总会有拥挤的时候。这时该采取什么策略来最大程度上保证传输?答案是QoS。QoS的基本策略是设置优先级,其次还有一些辅助策略,比如动态优先级调整,整流等。
当中的表是DMC内部对QoS优先级的重映射。DMC500支持把某个模块的优先级提高,并动态调整。这里,CPU在每秒发送1.6GB之后,优先级会被降低,和外部总线一样。下图中,我们可以看到在没有QoS的时候,CPU的延迟最大(最左边noqos),有了QoS,由于CPU的优先级相对较高,延迟大大降低(qos_only)。而用了重映射之后,在1.6GB之下,延迟又进一步较大降低。
在上图,我们看最右边,由于显示和视频总带宽需求不高,所以都能满足。
当然,我们也可以通过别的机制来达到类似的效果,比如限流等。攒机是个细致活,功夫够了肯定能调好,就是需要耐心和积累。
R
eading
