实验五 时钟Tick

Arm的中断系统

中断是一种硬件机制。借助于中断,CPU可以不必再采用轮询这种低效的方式访问外部设备。将所有的外部设备与CPU直接相连是不现实的,外部设备的中断请求一般经由中断控制器,由中断控制器仲裁后再转发给CPU。如下图所示Arm的中断系统。

../_images/ARMGIC.png

其中nIRQ是普通中断,nFIQ是快速中断。 Arm采用的中断控制器叫做GIC,即general interrupt controller。gic包括多个版本,如GICv1(已弃用),GICv2,GICv3,GICv4。简单起见,我们实验将选用GICv2版本。

为了配置好gicv2中断控制器,与pl011串口一样,我们需要阅读其技术参考手册。访问Arm官网在 这里 下载ARM Generic Interrupt Controller Architecture Specification - version 2.0 的pdf版本。

../_images/gicv2-logic.png

从上图(来源于ARM Generic Interrupt Controller Architecture Specification - version 2.0中的Chapter 2 GIC Partitioning)可以看出:

  • GICv2 最多支持8个核的中断管理。

  • GIC包括两大主要部分(由图中蓝色虚竖线分隔,Distributor和CPU Interface由蓝色虚矩形框标示),分别是:

    • Distributor,其通过GICD_开头的寄存器进行控制(蓝色实矩形框标示)

    • CPU Interface,其通过GICC_开头的寄存器进行控制(蓝色实矩形框标示)

  • 中断类型分为以下几类(由图中红色虚线椭圆标示):

    • SPI:(shared peripheral interrupt),共享外设中断。该中断来源于外设,通过Distributor分发给特定的core,其中断编号为32-1019。从图中可以看到所有核共享SPI。

    • PPI:(private peripheral interrupt),私有外设中断。该中断来源于外设,但只对指定的core有效,中断信号只会发送给指定的core,其中断编号为16-31。从图中可以看到每个core都有自己的PPI。

    • SGI:(software-generated interrupt),软中断。软件产生的中断,用于给其他的core发送中断信号,其中断编号为0-15。

    • virtual interrupt,虚拟中断,用于支持虚拟机。图中也可以看到,因为我们暂时不关心,所以没有标注。

    • 此外可以看到(FIQ, IRQ)可通过b进行旁路,我们也不关心。如感兴趣可以查看技术手册了解细节。

此外,由ARM Generic Interrupt Controller Architecture Specification - version 2.0 (section 1.4.2)可知,外设中断可由两种方式触发:

  • edge-triggered: 边沿触发,当检测到中断信号上升沿时中断有效。

  • level-sensitive:电平触发,当中断源为指定电平时中断有效。

因为soc中,中断有很多,为了方便对中断的管理,对每个中断,附加了中断优先级。在中断仲裁时,高优先级的中断,会优于低优先级的中断,发送给cpu处理。当cpu在响应低优先级中断时,如果此时来了高优先级中断,那么高优先级中断会抢占低优先级中断,而被处理器响应。

由ARM Generic Interrupt Controller Architecture Specification - version 2.0 (section 3.3)可知,GICv2最多支持256个中断优先级。GICv2中规定,所支持的中断优先级别数与GIC的具体实现有关,如果支持的中断优先级数比256少(最少为16),则8位优先级的低位为0,且遵循RAZ/WI(Read-As-Zero, Writes Ignored)原则。

GICv2初始化

由下图中virt.dts中intc和timer的部分

intc@8000000 {
        phandle = <0x8001>;
        reg = <0x00 0x8000000 0x00 0x10000 0x00 0x8010000 0x00 0x10000>;
        compatible = "arm,cortex-a15-gic";
        ranges;
        #size-cells = <0x02>;
        #address-cells = <0x02>;
        interrupt-controller;
        #interrupt-cells = <0x03>;

        v2m@8020000 {
                phandle = <0x8002>;
                reg = <0x00 0x8020000 0x00 0x1000>;
                msi-controller;
                compatible = "arm,gic-v2m-frame";
        };
};

timer {
        interrupts = <0x01 0x0d 0x104 0x01 0x0e 0x104 0x01 0x0b 0x104 0x01 0x0a 0x104>;
        always-on;
        compatible = "arm,armv8-timer\0arm,armv7-timer";
};

并结合kernel.org中关于 ARM Generic Interrupt ControllerARM architected timer 的devicetree的说明可知:

  • intc中的 reg 指明GICD寄存器映射到内存的位置为0x8000000,长度为0x10000, GICC寄存器映射到内存的位置为0x8010000,长度为0x10000

  • intc中的 #interrupt-cells 指明 interrupts 包括3个cells。第一个文档 指明:第一个cell为中断类型,0表示SPI,1表示PPI;第二个cell为中断号,SPI范围为[0-987],PPI为[0-15];第三个cell为flags,其中[3:0]位表示触发类型,4表示高电平触发,[15:8]为PPI的cpu中断掩码,每1位对应一个cpu,为1表示该中断会连接到对应的cpu。

  • 以timer设备为例,其中包括4个中断。以第二个中断的参数 0x01 0x0e 0x104 为例,其指明该中断为PPI类型的中断,中断号14, 路由到第一个cpu,且高电平触发。但注意到PPI的起始中断号为16,所以实际上该中断在GICv2中的中断号应为16 + 14 = 30。

阅读ARM Generic Interrupt Controller Architecture Specification - version 2.0,在其Chapter 4 Programmers’ Model部分有关于GICD和GICC寄存器的描述,以及如何使能Distributor和CPU Interfaces的方法。

新建 src/bsp/hwi_init.c 文件,初始化 GIC

  1#include "prt_typedef.h"
  2#include "os_attr_armv8_external.h"
  3
  4#define OS_GIC_VER                 2
  5
  6#define GIC_DIST_BASE              0x08000000
  7#define GIC_CPU_BASE               0x08010000
  8
  9#define GICD_CTLR                  (GIC_DIST_BASE + 0x0000U)
 10#define GICD_TYPER                 (GIC_DIST_BASE + 0x0004U)
 11#define GICD_IIDR                  (GIC_DIST_BASE + 0x0008U)
 12#define GICD_IGROUPRn              (GIC_DIST_BASE + 0x0080U)
 13#define GICD_ISENABLERn            (GIC_DIST_BASE + 0x0100U)
 14#define GICD_ICENABLERn            (GIC_DIST_BASE + 0x0180U)
 15#define GICD_ISPENDRn              (GIC_DIST_BASE + 0x0200U)
 16#define GICD_ICPENDRn              (GIC_DIST_BASE + 0x0280U)
 17#define GICD_ISACTIVERn            (GIC_DIST_BASE + 0x0300U)
 18#define GICD_ICACTIVERn            (GIC_DIST_BASE + 0x0380U)
 19#define GICD_IPRIORITYn            (GIC_DIST_BASE + 0x0400U)
 20#define GICD_ICFGR                 (GIC_DIST_BASE + 0x0C00U)
 21
 22
 23#define GICD_CTLR_ENABLE                1  /* Enable GICD */
 24#define GICD_CTLR_DISABLE               0     /* Disable GICD */
 25#define GICD_ISENABLER_SIZE             32
 26#define GICD_ICENABLER_SIZE             32
 27#define GICD_ICPENDR_SIZE               32
 28#define GICD_IPRIORITY_SIZE             4
 29#define GICD_IPRIORITY_BITS             8
 30#define GICD_ICFGR_SIZE                 16
 31#define GICD_ICFGR_BITS                 2
 32
 33#define GICC_CTLR                  (GIC_CPU_BASE  + 0x0000U)
 34#define GICC_PMR                   (GIC_CPU_BASE  + 0x0004U)
 35#define GICC_BPR                   (GIC_CPU_BASE  + 0x0008U)
 36#define IAR_MASK        0x3FFU
 37#define GICC_IAR            (GIC_CPU_BASE + 0xc)
 38#define GICC_EOIR           (GIC_CPU_BASE + 0x10)
 39#define     GICD_SGIR               (GIC_DIST_BASE + 0xf00)
 40
 41#define BIT(n)                     (1 << (n))
 42
 43#define GICC_CTLR_ENABLEGRP0       BIT(0)
 44#define GICC_CTLR_ENABLEGRP1       BIT(1)
 45#define GICC_CTLR_FIQBYPDISGRP0    BIT(5)
 46#define GICC_CTLR_IRQBYPDISGRP0    BIT(6)
 47#define GICC_CTLR_FIQBYPDISGRP1    BIT(7)
 48#define GICC_CTLR_IRQBYPDISGRP1    BIT(8)
 49
 50#define GICC_CTLR_ENABLE_MASK      (GICC_CTLR_ENABLEGRP0 | \
 51                                    GICC_CTLR_ENABLEGRP1)
 52
 53#define GICC_CTLR_BYPASS_MASK      (GICC_CTLR_FIQBYPDISGRP0 | \
 54                                    GICC_CTLR_IRQBYPDISGRP0 | \
 55                                    GICC_CTLR_FIQBYPDISGRP1 | \
 56                                    GICC_CTLR_IRQBYPDISGRP1)
 57
 58#define GICC_CTLR_ENABLE            1
 59#define GICC_CTLR_DISABLE           0
 60// Priority Mask Register. interrupt priority filter, Higher priority corresponds to a lower Priority field value.
 61#define GICC_PMR_PRIO_LOW           0xff
 62// The register defines the point at which the priority value fields split into two parts,
 63// the group priority field and the subpriority field. The group priority field is used to
 64// determine interrupt preemption. NO GROUP.
 65#define GICC_BPR_NO_GROUP           0x00
 66
 67#define GIC_REG_READ(addr)         (*(volatile U32 *)((uintptr_t)(addr)))
 68#define GIC_REG_WRITE(addr, data)  (*(volatile U32 *)((uintptr_t)(addr)) = (U32)(data))
 69
 70
 71// src/arch/drv/gic/prt_gic_init.c
 72/*
 73* 描述: 去使能(禁用)指定中断
 74*/
 75OS_SEC_L4_TEXT void OsGicDisableInt(U32 intId)
 76{
 77    // Interrupt Clear-Enable Registers
 78}
 79
 80/*
 81* 描述: 使能指定中断
 82*/
 83OS_SEC_L4_TEXT void OsGicEnableInt(U32 intId)
 84{
 85    // Interrupt Set-Enable Registers
 86}
 87
 88// 直接清除中断的pending状态
 89OS_SEC_L4_TEXT void OsGicClearIntPending(uint32_t interrupt)
 90{
 91    // Interrupt Clear-Pending state of an interrupt Registers
 92    GIC_REG_WRITE(GICD_ICPENDRn + (interrupt / GICD_ICPENDR_SIZE)*sizeof(U32), 1 << (interrupt % GICD_ICPENDR_SIZE));
 93}
 94
 95// 设置中断号为interrupt的中断的优先级为priority
 96OS_SEC_L4_TEXT void OsGicIntSetPriority(uint32_t interrupt, uint32_t priority) {
 97    uint32_t shift = (interrupt % GICD_IPRIORITY_SIZE) * GICD_IPRIORITY_BITS;
 98    volatile uint32_t* addr = ((volatile U32 *)(uintptr_t)(GICD_IPRIORITYn + (interrupt / GICD_IPRIORITY_SIZE) * sizeof(U32))) ;
 99    uint32_t value = GIC_REG_READ(addr);
100    value &= ~(0xff << shift); // 每个中断占8位,所以掩码为 0xFF
101    value |= priority << shift;
102    GIC_REG_WRITE(addr, value);
103}
104
105// 设置中断号为interrupt的中断的属性为config
106OS_SEC_L4_TEXT void OsGicIntSetConfig(uint32_t interrupt, uint32_t config) {
107    uint32_t shift = (interrupt % GICD_ICFGR_SIZE) * GICD_ICFGR_BITS;
108    volatile uint32_t* addr = ((volatile U32 *)(uintptr_t)(GICD_ICFGR + (interrupt / GICD_ICFGR_SIZE)*sizeof(U32)));
109    uint32_t value = GIC_REG_READ(addr);
110    value &= ~(0x03 << shift);
111    value |= config << shift;
112    GIC_REG_WRITE(addr, value);
113}
114
115/*
116* 描述: 中断确认
117*/
118OS_SEC_L4_TEXT U32 OsGicIntAcknowledge(void)
119{
120    // reads this register to obtain the interrupt ID of the signaled interrupt.
121    // This read acts as an acknowledge for the interrupt.
122    U32 value = GIC_REG_READ(GICC_IAR);
123    return value;
124}
125
126/*
127* 描述: 标记中断完成,清除相应中断位
128*/
129OS_SEC_L4_TEXT void OsGicIntClear(U32 value)
130{
131    // A processor writes to this register to inform the CPU interface either:
132    // • that it has completed the processing of the specified interrupt
133    // • in a GICv2 implementation, when the appropriate GICC_CTLR.EOImode bit is set to 1, to indicate that the interface should perform priority drop for the specified interrupt.
134    GIC_REG_WRITE(GICC_EOIR, value);
135}
136
137U32 OsHwiInit(void)
138{
139
140    // 初始化Gicv2的distributor和cpu interface
141    // 禁用distributor和cpu interface后进行相应配置
142    GIC_REG_WRITE(GICD_CTLR, GICD_CTLR_DISABLE);
143    GIC_REG_WRITE(GICC_CTLR, GICC_CTLR_DISABLE);
144    GIC_REG_WRITE(GICC_PMR, GICC_PMR_PRIO_LOW);
145    GIC_REG_WRITE(GICC_BPR, GICC_BPR_NO_GROUP);
146
147
148    // 启用distributor和cpu interface
149    GIC_REG_WRITE(GICD_CTLR, GICD_CTLR_ENABLE);
150    GIC_REG_WRITE(GICC_CTLR, GICC_CTLR_ENABLE);
151
152    return OS_OK;
153}

在 hwi_init.c 中 OsHwiInit 函数实现 GIC 的初始化,此外还提供了其他函数实现开关指定中断、设置中断属性、确认中断和标记中断完成等功能。

注意

你需要参照 OsGicIntSetPriority 等函数实现 OsGicEnableInt 和 OsGicDisableInt 函数。

使能时钟中断

新建 src/include/prt_config.h

1/* Tick中断时间间隔,tick处理时间不能超过1/OS_TICK_PER_SECOND(s) */
2#define OS_TICK_PER_SECOND                              1000

新建 src/include/os_cpu_armv8.h。

 1#ifndef OS_CPU_ARMV8_H
 2#define OS_CPU_ARMV8_H
 3
 4#include "prt_typedef.h"
 5
 6// CurrentEl等级
 7#define CURRENT_EL_2       0x8
 8#define CURRENT_EL_1       0x4
 9#define CURRENT_EL_0       0x0
10
11#define DAIF_DBG_BIT      (1U << 3)
12#define DAIF_ABT_BIT      (1U << 2)
13#define DAIF_IRQ_BIT      (1U << 1)
14#define DAIF_FIQ_BIT      (1U << 0)
15
16#define INT_MASK          (1U << 7)
17
18#define PRT_DSB() OS_EMBED_ASM("DSB sy" : : : "memory")
19#define PRT_DMB() OS_EMBED_ASM("DMB sy" : : : "memory")
20#define PRT_ISB() OS_EMBED_ASM("ISB" : : : "memory")
21
22#endif /* OS_CPU_ARMV8_H */

新建 src/bsp/timer.c 文件,对定时器和对应的中断进行配置

 1#include "prt_typedef.h"
 2#include "prt_config.h"
 3#include "os_cpu_armv8.h"
 4
 5U64 g_timerFrequency;
 6extern void OsGicIntSetConfig(uint32_t interrupt, uint32_t config);
 7extern void OsGicIntSetPriority(uint32_t interrupt, uint32_t priority);
 8extern void OsGicEnableInt(U32 intId);
 9extern void OsGicClearIntPending(uint32_t interrupt);
10
11void CoreTimerInit(void)
12{
13    // 配置中断控制器
14    OsGicIntSetConfig(30, 0); // 配置为电平触发
15    OsGicIntSetPriority(30, 0); // 优先级为0
16    OsGicClearIntPending(30); // 清除中断pending
17    OsGicEnableInt(30); // 启用中断
18
19    // 配置定时器
20    OS_EMBED_ASM("MRS %0, CNTFRQ_EL0" : "=r"(g_timerFrequency) : : "memory", "cc"); //读取时钟频率
21
22    U32 cfgMask = 0x0;
23    U64 cycle = g_timerFrequency / OS_TICK_PER_SECOND;
24
25    OS_EMBED_ASM("MSR CNTP_CTL_EL0, %0" : : "r"(cfgMask) : "memory");
26    PRT_ISB();
27    OS_EMBED_ASM("MSR CNTP_TVAL_EL0, %0" : : "r"(cycle) : "memory", "cc"); //设置中断周期
28
29    cfgMask = 0x1;
30    OS_EMBED_ASM("MSR CNTP_CTL_EL0, %0" : : "r"(cfgMask) : "memory"); //启用定时器 enable=1, imask=0, istatus= 0,
31    OS_EMBED_ASM("MSR DAIFCLR, #2");
32}

时钟中断处理

  • 将 prt_vector.S 中的 EXC_HANDLE 5 OsExcDispatch 改为 EXC_HANDLE 5 OsHwiDispatcher,表明我们将对 IRQ 类型的异常(即中断)使用 OsHwiDispatcher 处理。

    提示

    需修改为 EXC_HANDLE 5 OsHwiDispatcher ,否则还是 OsExcDispatch 函数处理,仅会输出 “Catch a exception.” 信息

  • 在 prt_vector.S 中加入 OsHwiDispatcher 处理代码,其类似于之前的 OsExcDispatch ,因此不再说明。

     1    .globl OsHwiDispatcher
     2    .type OsHwiDispatcher, @function
     3    .align 4
     4OsHwiDispatcher:
     5    mrs    x5, esr_el1
     6    mrs    x4, far_el1
     7    mrs    x3, spsr_el1
     8    mrs    x2, elr_el1
     9    stp    x4, x5, [sp,#-16]!
    10    stp    x2, x3, [sp,#-16]!
    11
    12    mov    x0, x1  // 异常类型0~15,参见异常向量表
    13    mov    x1, sp  // 异常时寄存器信息,通过栈及其sp指针提供
    14    bl     OsHwiDispatch
    15
    16    ldp    x2, x3, [sp],#16
    17    add    sp, sp, #16        // 跳过far, esr, HCR_EL2.TRVM==1的时候,EL1不能写far, esr
    18    msr    spsr_el1, x3
    19    msr    elr_el1, x2
    20    dsb    sy
    21    isb
    22
    23    RESTORE_EXC_REGS // 恢复上下文
    24
    25    eret //从异常返回
    
  • 在 prt_exc.c 中引用头文件 os_attr_armv8_external.h , os_cpu_armv8.h , OsHwiDispatch 处理 IRQ 类型的中断。

     1extern void OsTickDispatcher(void);
     2OS_SEC_ALW_INLINE INLINE void OsHwiHandleActive(U32 irqNum)
     3{
     4    switch(irqNum){
     5        case 30:
     6            OsTickDispatcher();
     7            // PRT_Printf(".");
     8            break;
     9        default:
    10            break;
    11    }
    12}
    13
    14extern  U32 OsGicIntAcknowledge(void);
    15extern void OsGicIntClear(U32 value);
    16// src/arch/cpu/armv8/common/hwi/prt_hwi.c  OsHwiDispatch(),OsHwiDispatchHandle()
    17/*
    18* 描述: 中断处理入口, 调用处外部已关中断
    19*/
    20OS_SEC_L0_TEXT void OsHwiDispatch( U32 excType, struct ExcRegInfo *excRegs) //src/arch/cpu/armv8/common/hwi/prt_hwi.c
    21{
    22    // 中断确认,相当于OsHwiNumGet()
    23    U32 value = OsGicIntAcknowledge();
    24    U32 irq_num = value & 0x1ff;
    25    U32 core_num = value & 0xe00;
    26
    27    OsHwiHandleActive(irq_num);
    28
    29    // 清除中断,相当于 OsHwiClear(hwiNum);
    30    OsGicIntClear(irq_num|core_num);
    31}
    

    src/bsp/os_attr_armv8_external.h 头文件可以在 此处 下载。

  • 新建 src/kernel/tick/prt_tick.c 文件,提供 OsTickDispatcher 时钟中断处理函数。

     1#include "os_attr_armv8_external.h"
     2#include "prt_typedef.h"
     3#include "prt_config.h"
     4#include "os_cpu_armv8_external.h"
     5
     6extern U64 g_timerFrequency;
     7
     8/* Tick计数 */
     9OS_SEC_BSS U64 g_uniTicks; //src/core/kernel/sys/prt_sys.c
    10
    11/*
    12* 描述:Tick中断的处理函数。扫描任务超时链表、扫描超时软件定时器、扫描TSKMON等。
    13*/
    14OS_SEC_TEXT void OsTickDispatcher(void)
    15{
    16    uintptr_t intSave;
    17
    18    intSave = OsIntLock();
    19    g_uniTicks++;
    20    OsIntRestore(intSave);
    21
    22    U64 cycle = g_timerFrequency / OS_TICK_PER_SECOND;
    23    OS_EMBED_ASM("MSR CNTP_TVAL_EL0, %0" : : "r"(cycle) : "memory", "cc"); //设置中断周期
    24
    25}
    26
    27/*
    28* 描述:获取当前的tick计数
    29*/
    30OS_SEC_L2_TEXT U64 PRT_TickGetCount(void) //src/core/kernel/sys/prt_sys_time.c
    31{
    32    return g_uniTicks;
    33}
    

注意需将 hwi_init.c timer.c prt_tick.c 等文件加入构建系统。

提示

src/kernel, src/kernel/tick 目录下均需加入 CMakeLists.txt, src/ 和 src/bsp/ 下的 CMakeLists.txt 需修改。其中,

src/kernel/tick/CMakeLists.txt 类似 src/bsp/CMakeLists.txt

src/kernel/CMakeLists.txt 内容为: add_subdirectory(tick)

src/CMakeLists.txt 需修改增加include目录、包含子目录和编译目标:

... ...
include_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}/include   # 增加 src/include 目录
    ${CMAKE_CURRENT_SOURCE_DIR}/bsp
)

add_subdirectory(bsp)
add_subdirectory(kernel) # 增加 kernel 子目录

list(APPEND OBJS $<TARGET_OBJECTS:bsp> $<TARGET_OBJECTS:tick>) # 增加 $<TARGET_OBJECTS:tick> 目标
add_executable(${APP} main.c ${OBJS})

后续实验中若新增文件加入构建系统不再赘述,请参照此处。

  • 在 OsTickDispatcher 中调用了 OsIntLock 和 OsIntRestore 函数,这两个函数用于关中断和开中断。简单起见,将其放入 prt_exc.c 中。

     1/*
     2* 描述: 开启全局可屏蔽中断。
     3*/
     4OS_SEC_L0_TEXT uintptr_t PRT_HwiUnLock(void) //src/arch/cpu/armv8/common/hwi/prt_hwi.c
     5{
     6    uintptr_t state = 0;
     7
     8    OS_EMBED_ASM(
     9        "mrs %0, DAIF      \n"
    10        "msr DAIFClr, %1   \n"
    11        : "=r"(state)
    12        : "i"(DAIF_IRQ_BIT)
    13        : "memory", "cc");
    14
    15    return state & INT_MASK;
    16}
    17
    18/*
    19* 描述: 关闭全局可屏蔽中断。
    20*/
    21OS_SEC_L0_TEXT uintptr_t PRT_HwiLock(void) //src/arch/cpu/armv8/common/hwi/prt_hwi.c
    22{
    23    uintptr_t state = 0;
    24    OS_EMBED_ASM(
    25        "mrs %0, DAIF      \n"
    26        "msr DAIFSet, %1   \n"
    27        : "=r"(state)
    28        : "i"(DAIF_IRQ_BIT)
    29        : "memory", "cc");
    30    return state & INT_MASK;
    31}
    32
    33/*
    34* 描述: 恢复原中断状态寄存器。
    35*/
    36OS_SEC_L0_TEXT void PRT_HwiRestore(uintptr_t intSave) //src/arch/cpu/armv8/common/hwi/prt_hwi.c
    37{
    38    if ((intSave & INT_MASK) == 0) {
    39        OS_EMBED_ASM(
    40            "msr DAIFClr, %0\n"
    41            :
    42            : "i"(DAIF_IRQ_BIT)
    43            : "memory", "cc");
    44    } else {
    45        OS_EMBED_ASM(
    46            "msr DAIFSet, %0\n"
    47            :
    48            : "i"(DAIF_IRQ_BIT)
    49            : "memory", "cc");
    50    }
    51    return;
    52}
    

头文件 src/bsp/os_cpu_armv8_external.h

 1#ifndef OS_CPU_ARMV8_EXTERNAL_H
 2#define OS_CPU_ARMV8_EXTERNAL_H
 3
 4extern uintptr_t PRT_HwiUnLock(void);
 5extern uintptr_t PRT_HwiLock(void);
 6extern void PRT_HwiRestore(uintptr_t intSave);
 7
 8#define OsIntUnLock() PRT_HwiUnLock()
 9#define OsIntLock()   PRT_HwiLock()
10#define OsIntRestore(intSave) PRT_HwiRestore(intSave)
11
12#endif

读取系统Tick值

新建 prt_tick.h,声明 Tick 相关的接口函数.

1#ifndef PRT_TICK_H
2#define PRT_TICK_H
3
4#include "prt_typedef.h"
5
6extern U64 PRT_TickGetCount(void);
7
8#endif /* PRT_TICK_H */

main.c 修改为:

 1#include "prt_typedef.h"
 2#include "prt_tick.h"
 3
 4extern U32 PRT_Printf(const char *format, ...);
 5extern void PRT_UartInit(void);
 6extern void CoreTimerInit(void);
 7extern U32 OsHwiInit(void);
 8
 9U64 delay_time = 10000;
10
11S32 main(void)
12{
13    // 初始化GIC
14    OsHwiInit();
15    // 启用Timer
16    CoreTimerInit();
17
18    PRT_UartInit();
19
20    PRT_Printf("            _       _ _____      _             _             _   _ _   _ _   _           \n");
21    PRT_Printf("  _ __ ___ (_)_ __ (_) ____|   _| | ___ _ __  | |__  _   _  | | | | \\ | | | | | ___ _ __ \n");
22    PRT_Printf(" | '_ ` _ \\| | '_ \\| |  _|| | | | |/ _ \\ '__| | '_ \\| | | | | |_| |  \\| | | | |/ _ \\ '__|\n");
23    PRT_Printf(" | | | | | | | | | | | |__| |_| | |  __/ |    | |_) | |_| | |  _  | |\\  | |_| |  __/ |   \n");
24    PRT_Printf(" |_| |_| |_|_|_| |_|_|_____\\__,_|_|\\___|_|    |_.__/ \\__, | |_| |_|_| \\_|\\___/ \\___|_|   \n");
25    PRT_Printf("                                                     |___/                               \n");
26
27    PRT_Printf("ctr-a h: print help of qemu emulator. ctr-a x: quit emulator.\n\n");
28
29    for(int i = 0; i < 10; i++)
30    {
31
32        U32 tick = PRT_TickGetCount();
33        PRT_Printf("[%d] current tick: %u\n", i, tick);
34
35        //delay
36        int delay_time = 10000000;  // 根据自己机器计算能力不同调整该值
37        while(delay_time>0){
38            PRT_TickGetCount();  //消耗时间,防止延时代码被编译器优化
39            delay_time--;
40        }
41
42    }
43
44    while(1);
45    return 0;
46
47}

lab5 作业

作业1

实现 hwi_init.c 中缺失的 OsGicEnableInt 和 OsGicDisableInt 函数。

1

https://developer.arm.com/documentation/den0024/a/Fundamentals-of-ARMv8/Execution-states

2

https://developer.arm.com/documentation/den0024/a/AArch64-Exception-Handling/Synchronous-and-asynchronous-exceptions

3

https://developer.arm.com/documentation/den0024/a/AArch64-Exception-Handling/AArch64-exception-table

4

https://developer.arm.com/documentation/den0024/a/ARMv8-Registers/AArch64-special-registers/Stack-pointer

5

https://www.ic.unicamp.br/~celio/mc404-2014/docs/gnu-arm-directives.pdf

6

https://developer.arm.com/documentation/ddi0487/gb

7

https://doc.rust-lang.org/reference/inline-assembly.html#register-operands

8

https://cloud.tencent.com/developer/article/1520799