蜗窝科技

问个关于ARM中System Counter的问题

蜗窝讨论区存档 · Linux kernel技术问答 · 楼主 metro · 2016-08-22 · 7 帖

本文是原「蜗窝讨论区」的历史存档(2016-08-22),来自版块「Linux kernel技术问答」,共 7 帖。讨论区已停止服务,此处仅供查阅。

metro · 2016-08-22 23:43

首先感谢网站,能够分享这么好的文章,让我学到了很多,谢谢。

最近我跟着相关文章的内容,自己在NanoPi M3(SoC为S5P6818,八核A53)上移植64位的U-boot。目前U-boot已经基本可用,但是遇到了一个问题:系统需要个定时器来记录时间,并且提供相应api以进行一些时间相关的功能,最终通过设备树的方式传递给Linux。通过查阅各种资料,我得到了以下信息: 1. 对系统定时器的初始化操作一般都在U-boot前期完成,包括System Counter的enable,基本是在lowlevel_init函数中完成,在进入C环境之前系统定时器可用。 2. 由于使用的是A53的SoC,所以参考了ARMv8和A53的技术手册的Generic Timers部分,但是看着看着就觉得有些奇怪。ARMv8中将Generic Timers分成了System Counter和Timers两部分,并且分别提供了Memory-mapped I/O和寄存器访问(AArch64 Mode)两种方式,其实在之前网站上的文章中也交待清楚了(Linux时间子系统之(十七):ARM generic timer驱动代码分析),这倒没什么问题。 但是,Cortex-A53的manuals则表示CPU中不存在System Counter部分(This resides in SoC),并且只提及了两个接口,即CNTCLKEN和CNTVALUEB,而没有提到通过bus访问的其他接口,而CNTCLKEN和CNTVALUEB在ARMv8的技术文档中完全没有提及。我做过测试,CNTVALUEB的值和AArch64中cntpct_el0寄存器的值一模一样,说明这确实是一个用作System Counter的接口。 那么问题来了,我查阅了SoC的datasheet,发现关于System Counter的部分确实只提供了CNTCLKEN和CNTVALUEB的接口,坑爹的是并没有详细说明这部分接口是否连到了SoC Timers部分,datasheet中提到的timers都是32位down-counter的,与System Counter的要求不符,感觉并不是用作System Counter的timers。(顺便吐槽下,这个SoC的datasheet写得挺糟糕,IP核似乎是来自几个平台拼凑而成的,相关资料都不大好找,虽然基本最后都能找得到,除了这个。) 3. 看了半天文档也没什么结果,于是我去翻了翻网上移植好的U-boot和Linux。不过蛋疼的是网上找到的都是32位的代码,移植的版本也比较早(14年),说白了就是把S5P4418(一个系列的前代产品,使用四核A9)的代码直接移植过来使用的。可能是ARMv7时代对于Generic Timers没有统一的规范,这些代码都是自己编写的timers驱动,没有用到后面抽象好的架构,而是使用一个SoC的定时器作为tick使用,看着实在是头大。。问过板子的技术支持,得到的答复是暂时没有提供64位的文档,噗。。。 不死心的我又看了几个同为A53的SoC的U-boot代码,但是却发现基本分成两种情况:要么压根没有初始化System Counter(可能在更早的时候就完成了初始化?),要么确实初始化了System Counter(典型的如freescale),但是访问了Memory-mapped I/O的接口(也就是CNTCR寄存器),而这个接口(当然也包括一个系列的其他接口)并没有出现在A53的技术文档和S5P6818的datasheet中。我猜测,System Counter的接口访问部分可能不是强制要求实现的,所以才会导致不一致的问题。

对于这个问题,我想到了以下几个解决方案: 1. 最好的方法当然是使用ARMv8和A53提供好的架构,但是在参考文档不大完善且有些矛盾的情况下似乎不大好搞。现在看来,要么等待官方更新64位部分的文档,要么就是自己使用定时器中断的方式更新CNTVALUEB寄存器来模拟,而前者遥遥无期,后者将要花费不少的系统资源来处理中断(DMA显然不适合,timer的类型都不一样),而且不符合power-down时的要求,更麻烦的是需要修改U-boot的exception vectors(U-boot默认将IRQ和FIQ中断引导到panic,不像ARMv7时可以自己处理异常),感觉不是一个合适的方法。 2. 参考旧的U-boot和linux代码,将其中一个普通定时器用作System Ticks,这样做的缺点也显而易见,那就是不符合ARMv8的规范,相关的驱动都要自己重新实现,这个只能说是下下之策。 3. 抛弃timer机制,使用一些其他函数替代system ticks的功能,但是只能在U-boot上凑合用用(U-boot基本上只在delay的时候用到),Linux上显然行不通。

目前卡在这个问题上,不知道各位对此有什么见解吗?谢谢!

wowo · 2016-08-23 09:18

System counter是ARM体系结构规定的,不是具体的CPU规定的,怎么会没有呢?你可以参考“DDI0487A_d_armv8_arm.pdf”,查看里面有关CNTFRQ_EL0的定义。 另外,在u-boot中,要使用system counter是非常容易的: 1)在配置头文件中,定义system counter的时钟频率


[== Undefined ==]
COUNTER_FREQUENCY

2)arch/arm/cpu/armv8/start.S中会根据时钟频率,配置CNTFRQ_EL0


[== Undefined ==]
#ifdef COUNTER_FREQUENCY                                                        
        ldr     x0, =COUNTER_FREQUENCY                                          
        msr     cntfrq_el0, x0                  /* Initialize CNTFRQ */         
#endif

3)arch/arm/cpu/armv8/generic_timer.c中有关的API就可以使用了

metro · 2016-08-23 13:59

wowo 写道: System counter是ARM体系结构规定的,不是具体的CPU规定的,怎么会没有呢?你可以参考“DDI0487A_d_armv8_arm.pdf”,查看里面有关CNTFRQ_EL0的定义。 另外,在u-boot中,要使用system counter是非常容易的: 1)在配置头文件中,定义system counter的时钟频率


[== Undefined ==]
COUNTER_FREQUENCY

2)arch/arm/cpu/armv8/start.S中会根据时钟频率,配置CNTFRQ_EL0


[== Undefined ==]
#ifdef COUNTER_FREQUENCY                                                        
        ldr     x0, =COUNTER_FREQUENCY                                          
        msr     cntfrq_el0, x0                  /* Initialize CNTFRQ */         
#endif

3)arch/arm/cpu/armv8/generic_timer.c中有关的API就可以使用了

抱歉之前可能没说清楚。按理来说应该是配置好COUNTER_FREQUENCY(也就是cntfrq_el0)之后应该就可以用了,但实际上配置好后调用timer_read_counter函数时cntpct_el0的读数始终为0没有变化,所以怀疑是System Counter不工作导致的。有些SoC的timer的驱动包括了操作cntcr的部分(bit0控制System Counter是否运行),但有些并没有。我也觉得应该是需要打开某个开关来启动System Counter,但SoC的datasheet没有交代,而是提到了CNTCLKEN和CLKVALUEB这两个接口,而这两个接口只在Cortex-A53的manuals中提及,在ARMv8中却没有出现过(完整版5000多页的pdf同样没有),所以这才是让我疑惑的地方。

wowo · 2016-08-24 09:36

metro 写道:

wowo 写道: System counter是ARM体系结构规定的,不是具体的CPU规定的,怎么会没有呢?你可以参考“DDI0487A_d_armv8_arm.pdf”,查看里面有关CNTFRQ_EL0的定义。 另外,在u-boot中,要使用system counter是非常容易的: 1)在配置头文件中,定义system counter的时钟频率


[== Undefined ==]
COUNTER_FREQUENCY

2)arch/arm/cpu/armv8/start.S中会根据时钟频率,配置CNTFRQ_EL0


[== Undefined ==]
#ifdef COUNTER_FREQUENCY                                                        
        ldr     x0, =COUNTER_FREQUENCY                                          
        msr     cntfrq_el0, x0                  /* Initialize CNTFRQ */         
#endif

3)arch/arm/cpu/armv8/generic_timer.c中有关的API就可以使用了

抱歉之前可能没说清楚。按理来说应该是配置好COUNTER_FREQUENCY(也就是cntfrq_el0)之后应该就可以用了,但实际上配置好后调用timer_read_counter函数时cntpct_el0的读数始终为0没有变化,所以怀疑是System Counter不工作导致的。有些SoC的timer的驱动包括了操作cntcr的部分(bit0控制System Counter是否运行),但有些并没有。我也觉得应该是需要打开某个开关来启动System Counter,但SoC的datasheet没有交代,而是提到了CNTCLKEN和CLKVALUEB这两个接口,而这两个接口只在Cortex-A53的manuals中提及,在ARMv8中却没有出现过(完整版5000多页的pdf同样没有),所以这才是让我疑惑的地方。

我明白你的意思了,这里有一份数据手册: http://wiki.friendlyarm.com/wiki/images/8/8b/SEC_S5P6818X_Users_Manual_preliminary_Ver_0.00.pdf 你可以参考“4.9.2.86”章节,其中有一个“counter clock enable”的bit位,可能就是控制system counter的,你可以试试。 另外我没有找到有关system counter Frequency的信息,你找找,然后给大家分享一下。

PS:你用的这个板子的数据手册已经挺详细的了,我觉得x project都可以考虑也在这个板子上玩玩~~~

metro · 2016-08-24 10:31

wowo 写道:

metro 写道:

wowo 写道: System counter是ARM体系结构规定的,不是具体的CPU规定的,怎么会没有呢?你可以参考“DDI0487A_d_armv8_arm.pdf”,查看里面有关CNTFRQ_EL0的定义。 另外,在u-boot中,要使用system counter是非常容易的: 1)在配置头文件中,定义system counter的时钟频率


[== Undefined ==]
COUNTER_FREQUENCY

2)arch/arm/cpu/armv8/start.S中会根据时钟频率,配置CNTFRQ_EL0


[== Undefined ==]
#ifdef COUNTER_FREQUENCY                                                        
        ldr     x0, =COUNTER_FREQUENCY                                          
        msr     cntfrq_el0, x0                  /* Initialize CNTFRQ */         
#endif

3)arch/arm/cpu/armv8/generic_timer.c中有关的API就可以使用了

抱歉之前可能没说清楚。按理来说应该是配置好COUNTER_FREQUENCY(也就是cntfrq_el0)之后应该就可以用了,但实际上配置好后调用timer_read_counter函数时cntpct_el0的读数始终为0没有变化,所以怀疑是System Counter不工作导致的。有些SoC的timer的驱动包括了操作cntcr的部分(bit0控制System Counter是否运行),但有些并没有。我也觉得应该是需要打开某个开关来启动System Counter,但SoC的datasheet没有交代,而是提到了CNTCLKEN和CLKVALUEB这两个接口,而这两个接口只在Cortex-A53的manuals中提及,在ARMv8中却没有出现过(完整版5000多页的pdf同样没有),所以这才是让我疑惑的地方。

我明白你的意思了,这里有一份数据手册: http://wiki.friendlyarm.com/wiki/images/8/8b/SEC_S5P6818X_Users_Manual_preliminary_Ver_0.00.pdf 你可以参考“4.9.2.86”章节,其中有一个“counter clock enable”的bit位,可能就是控制system counter的,你可以试试。 另外我没有找到有关system counter Frequency的信息,你找找,然后给大家分享一下。

PS:你用的这个板子的数据手册已经挺详细的了,我觉得x project都可以考虑也在这个板子上玩玩~~~

感谢回复,这个位我试过了,还是不工作,所以也有些郁闷。。打算把网上能找到的u-boot都试一遍(可惜暂时只能找到32位的,要不可以连移植都省了,哈哈)。 当时选开发板时就是找了这么一个资料比较丰富的SoC,现在做下来除了DRAM的初始化部分只能反汇编得到源代码之外(按照datasheet的方式初始化不工作),其他都没遇到什么太大的问题。要是解决了这个问题,一定会来分享的,哈哈。

wowo · 2016-08-26 16:30

有机会可以把做的东西传到x project上面,和大家分享分享哈

metro · 2017-01-15 20:55

不好意思挖个自己的坟。 暑假后因为太忙暂时搁置了,这个月有空后又回来看了眼,发现手中的这块板子还没更新Linux 4.x 64位的OS。失望之余,偶然在网上看到三星搞了个开发平台ARTIK,里面的ARTIK710用的就是这个SoC。于是到github上找找,竟然有了收获:(http://github.com/SamsungArtik),发现从2ndboot(相当于SPL)到U-boot和Linux 4.1的源码一应俱全,美中不足的是还是没有这个SoC的最新Datasheet(之前的Datasheet有很多错误)。 然后这几天就试着移植到NanoPi M3上了。两个开发板之间的区别不算太大,主要在于PMIC、eMMC、WiFi等等。目前2ndboot移植完毕,U-boot也可以使用(除了有些驱动例如PMIC还没移植)。但是很奇怪的是System Counter依然是之前未启用的状态(表现为cntpct_el0恒为零),U-boot的dts也没有generic timers的影子,不知道是什么情况,等移植到Linux的时候再说吧。 最后给个自己的github地址:(https://github.com/metro94),有兴趣的可以试试看。不过最近可能又没时间了,等到彻底闲下来的时候可能会考虑自己编写和移植2ndboot和U-boot,感觉官方写得不能忍 :mad: