鸿蒙移植i.mx6ull(十) 系统时钟

Ps:这个鸿蒙系列是韦东山老师录制的视频和开发手册为基础,请大家支持韦老师。 这个专栏是:
1.学习的笔记记录。
2.整理和知识点汇总。
3.个人做的项目经验汇总。

1. Generic Timer介绍

参考资料:

1.1 硬件结构

在操作系统中,需要一个系统时钟,各类芯片都有自己的定时器,它们的编程方法互不相同,这给系统移植带来麻烦。 Generic TimerARM推荐的一种硬件实现实现,可以实现统一的编程方法。 Generic Timer分为两部分:共享的System Counter、各个Processor专有的Timer

  • System Counter:给所有Processor提供统一的时间
  • Timer:可以设置周期性的事件,给Processor提供中断信号

下图是Generic Timer的硬件框图,
红线表示时钟:System counter是时钟源,进入Porcessor中的Timer
蓝线表示中断:Porcessor中的Timer产生的中断进入GIC,作为PPI(Private Peripheral Interrupt)传给ProcessorSystem Counter是系统级别的(System Level),给整个系统提供时钟,可以使用Memeory Mapped的寄存器来访问。

每个Processor里都有一个Timer,这些Timer可以发出周期性的中断,给Processor提供系统时钟。Timer需要使用CP15协处理器命令来访问。

在这里插入图片描述

1.1.1 System Counter特性

规格描述
位宽(Width)至少56位,跟硬件实现。
读取时,可以得到64位的数值。
频率(Frequency)1M~50MHz,增加值可以调整:
比如时钟为8MHz时,每来一个时钟计数值增加1,
设置为4MHz时,每来一个时钟计数值增加2,
降低频率时可以降低功耗,同时增加步进值以维持时钟精度
溢出(Roll-over)不少于40年
精度(Accuracy)推荐:误差在24小时内不超过10秒
复位值(Start-up)从0开始
1. 两种访问方式

SystemCounter是给所有Processor使用的,它有两种访问方式:

  • CP15协处理器命令:某个Processor去访问它时可以使用CP15协处理器命令。

  • MemoryMapped寄存器:

    • 既然它是给所有Processor使用的,那么应该提供更高级的访问方法(System Level)

    • 而且有些Processor并没有实现CP15,所有也应该提供MemoryMapped的方法

2. CP15寄存器

下面这个表格列出了所有的寄存器,包括SystemCounterTimer,不仅仅是SystemCounter

在这里插入图片描述

3. MemoryMapped寄存器

这些寄存器在下列手册描述得比较清楚:

  • DM00327659.pdf的《46 System timer generator (STGEN)》
  • ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf的Table D5-1(下图):

在这里插入图片描述

u-boot代码中可以看到这样的结构体:

/* System Counter */
struct sctr_regs {
	u32 cntcr;   // control register, 启动/停止
	u32 cntsr;   // status register, 是否启动/停止, 使用哪个频率
	u32 cntcv1;  // count value lower register
	u32 cntcv2;  // count value upper register, cntcv1和cntcv2组成64位的计数值
	u32 resv1[4];
	u32 cntfid0; // base frequency register, 必须等于SystemCounter的输入频率
	u32 cntfid1; // cntfid1和cntfid2:其他频率
	u32 cntfid2;
	u32 resv2[1001];
	u32 counterid[1];
};

1.1.2 Timer特性

每个Processor都有一个Timer,它有3个寄存器,只能使用协处理器命令方位(CP15):

  • 64位的比较寄存器(CVAL):当SystemCounter的值等于它时,产生事件(中断)

    • SystemCounter总是增长的,所以Timer的64位比较寄存器也只能设置为大于SystemCounter的值
  • 被称为upcounter

  • 32位的TimerValue寄存器(TVAL)

    • 它是downconter
    • 比如设置为1000,表示再经过1000个时钟之后,就会产生事件(中断)
    • 实质是:设置64位的比较寄存器,让它等于SystemCounter+1000
  • 32位的控制寄存器(CTL)

    • 使能/禁止Timer
    • 使能输出:是否能产生事件(中断) * 状态:是否能产生了事件(中断)

在这里插入图片描述

1.2 SystemCounter时钟源

SystemCounter的时钟源,跟芯片设计相关。

STM32MP157为例:

在这里插入图片描述

1.3 使用方法

设置时钟源
设置/启动SystemCounter
设置Processor0的Timer: 设置比较值、使能中断、使能Timer
设置Processor1的Timer: 设置比较值、使能中断、使能Timer
  • 设置时钟源:芯片相关,一般u-boot里做好了
  • 设置/启动SystemCounter
  • 设置ProcessorTimer
  • 设置比较值、使能中断、使能Timer
  • 注册中断处理函数

2. GenericTimer源码分析

2.1 GenericTimer使用方法

设置时钟源
设置/启动SystemCounter
设置Processor0的Timer: 设置比较值、使能中断、使能Timer
设置Processor1的Timer: 设置比较值、使能中断、使能Timer
  • 设置时钟源:芯片相关,一般u-boot里做好了
  • 设置/启动SystemCounter:一般u-boot里做好了
  • 设置ProcessorTimer
    • 设置比较值、使能中断、使能Timer
    • 注册中断处理函数

2.2 源码分析

代码:kernel\liteos_a\platform\hw\arm\timer\arm_generic\arm_generic_timer.c

2.2.1 初始化

它做了2件事:

  • 读出SystemCounter的频率:以后设置中断周期时要用
  • 注册中断处理函数
LITE_OS_SEC_TEXT_INIT VOID HalClockInit(VOID)
{
    UINT32 ret;

    g_sysClock = HalClockFreqRead();
    ret = LOS_HwiCreate(OS_TICK_INT_NUM, MIN_INTERRUPT_PRIORITY, 0, OsTickEntry, 0);
    if (ret != LOS_OK) {
        PRINT_ERR("%s, %d create tick irq failed, ret:0x%x\n", __FUNCTION__, __LINE__, ret);
    }
}

2.2.2 启动Timer

它做了2件事:

  • 使能中断:中断号是29
  • 设置TimerValue寄存器:
  • OS_CYCLE_PER_TICK = g_sysClock / 100,也就是10MS之后产生中断
  • 设置TimerValue寄存器的实质,就是设置比较寄存器(CVAL) =当前SystemCounter+ OS_CYCLE_PER_TICK
LITE_OS_SEC_TEXT_INIT VOID HalClockStart(VOID)
{
    HalIrqUnmask(OS_TICK_INT_NUM);

    /* triggle the first tick */
    TimerCtlWrite(0);
    TimerTvalWrite(OS_CYCLE_PER_TICK);
    TimerCtlWrite(1);
}

2.2.3 中断处理

它做了2件事:

  • 调用OsTickHandler
  • 设置下一次中断时间:
  • 设置比较寄存器(CVAL) = 当前比较寄存器值 + OS_CYCLE_PER_TICK
  • 为什么不是当前SystemCounter+ OS_CYCLE_PER_TICK
  • 因为处理中断也是要时间的,SystemCounter值一直在增加
  • 要让两次中断的间隔非常精确的话,要使用发生中断时的SystemCounter值,也就是比较寄存器的当前值
LITE_OS_SEC_TEXT VOID OsTickEntry(VOID)
{
    TimerCtlWrite(0);

    OsTickHandler();

    /*
     * use last cval to generate the next tick's timing is
     * absolute and accurate. DO NOT use tval to drive the
     * generic time in which case tick will be slower.
     */
    TimerCvalWrite(TimerCvalRead() + OS_CYCLE_PER_TICK);
    TimerCtlWrite(1);
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值