当前位置: 首页 > ai >正文

湖南大学-操作系统实验5

HUNAN  UNIVERSITY

操作系统实验报告

          

一、实验题目

实验五 时钟Tick

Arm的中断系统

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

其中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版本。

从上图(来源于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)原则。

  • 实验环境

ubuntu20

  • 实验步骤

GICv2初始化

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

  1. intc@8000000 {
  2.         phandle = <0x8001>;
  3.         reg = <0x00 0x8000000 0x00 0x10000 0x00 0x8010000 0x00 0x10000>;
  4.         compatible = "arm,cortex-a15-gic";
  5.         ranges;
  6.         #size-cells = <0x02>;
  7.         #address-cells = <0x02>;
  8.         interrupt-controller;
  9.         #interrupt-cells = <0x03>;
  10.         v2m@8020000 {
  11.                 phandle = <0x8002>;
  12.                 reg = <0x00 0x8020000 0x00 0x1000>;
  13.                 msi-controller;
  14.                 compatible = "arm,gic-v2m-frame";
  15.         };
  16. };
  17. timer {
  18.         interrupts = <0x01 0x0d 0x104 0x01 0x0e 0x104 0x01 0x0b 0x104 0x01 0x0a 0x104>;
  19.         always-on;
  20.         compatible = "arm,armv8-timer\0arm,armv7-timer";
  21. };

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

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

使能时钟中断

新建 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. #include "prt_typedef.h"
  4. // CurrentEl等级
  5. #define CURRENT_EL_2       0x8
  6. #define CURRENT_EL_1       0x4
  7. #define CURRENT_EL_0       0x0
  8. #define DAIF_DBG_BIT      (1U << 3)
  9. #define DAIF_ABT_BIT      (1U << 2)
  10. #define DAIF_IRQ_BIT      (1U << 1)
  11. #define DAIF_FIQ_BIT      (1U << 0)
  12. #define INT_MASK          (1U << 7)
  13. #define PRT_DSB() OS_EMBED_ASM("DSB sy" : : : "memory")
  14. #define PRT_DMB() OS_EMBED_ASM("DMB sy" : : : "memory")
  15. #define PRT_ISB() OS_EMBED_ASM("ISB" : : : "memory")
  16. #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. U64 g_timerFrequency;
  5. extern void OsGicIntSetConfig(uint32_t interrupt, uint32_t config);
  6. extern void OsGicIntSetPriority(uint32_t interrupt, uint32_t priority);
  7. extern void OsGicEnableInt(U32 intId);
  8. extern void OsGicClearInt(uint32_t interrupt);
  9. void CoreTimerInit(void)
  10. {
  11.     // 配置中断控制器
  12.     OsGicIntSetConfig(300); // 配置为电平触发
  13.     OsGicIntSetPriority(300); // 优先级为0
  14.     OsGicClearInt(30); // 清除中断
  15.     OsGicEnableInt(30); // 启用中断
  16.     // 配置定时器
  17.     OS_EMBED_ASM("MRS %0, CNTFRQ_EL0" : "=r"(g_timerFrequency) : : "memory""cc"); //读取时钟频率
  18.     U32 cfgMask = 0x0;
  19.     U64 cycle = g_timerFrequency / OS_TICK_PER_SECOND;
  20.     OS_EMBED_ASM("MSR CNTP_CTL_EL0, %0" : : "r"(cfgMask) : "memory");
  21.     PRT_ISB();
  22.     OS_EMBED_ASM("MSR CNTP_TVAL_EL0, %0" : : "r"(cycle) : "memory""cc"); //设置中断周期
  23.     cfgMask = 0x1;
  24.     OS_EMBED_ASM("MSR CNTP_CTL_EL0, %0" : : "r"(cfgMask) : "memory"); //启用定时器 enable=1, imask=0, istatus= 0,
  25.     OS_EMBED_ASM("MSR DAIFCLR, #2");
  26. }

时钟中断处理

将 prt_vector.S 中的 EXC_HANDLE 5 OsExcDispatch 改为 EXC_HANDLE 5 OsHwiDispatcher,表明我们将对 IRQ 类型的异常(即中断)使用 OsHwiDispatcher 处理。在 prt_vector.S 中加入 OsHwiDispatcher 处理代码,其类似于之前的 OsExcDispatch ,因此不再说明。

  1.     .globl OsHwiDispatcher
  2.     .type OsHwiDispatcher, @function
  3.     .align 4
  4. OsHwiDispatcher:
  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.     mov    x0, x1  // 异常类型0~15,参见异常向量表
  12.     mov    x1, sp  // 异常时寄存器信息,通过栈及其sp指针提供
  13.     bl     OsHwiDispatch
  14.     ldp    x2, x3, [sp],#16
  15.     add    sp, sp, #16        // 跳过far, esr, HCR_EL2.TRVM==1的时候,EL1不能写far, esr
  16.     msr    spsr_el1, x3
  17.     msr    elr_el1, x2
  18.     dsb    sy
  19.     isb
  20.     RESTORE_EXC_REGS // 恢复上下文
  21.     eret //从异常返回

在 prt_exc.c 中引用头文件 os_attr_armv8_external.h,os_cpu_armv8.h,OsHwiDispatch 处理 IRQ 类型的中断。

  1. extern void OsTickDispatcher(void);
  2. OS_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. extern  U32 OsGicIntAcknowledge(void);
  14. extern void OsGicIntClear(U32 value);
  15. // src/arch/cpu/armv8/common/hwi/prt_hwi.c  OsHwiDispatch(),OsHwiDispatchHandle()
  16. /*
  17. 描述中断处理入口调用处外部已关中断
  18. */
  19. OS_SEC_L0_TEXT void OsHwiDispatch( U32 excType, struct ExcRegInfo *excRegs) //src/arch/cpu/armv8/common/hwi/prt_hwi.c
  20. {
  21.     // 中断确认,相当于OsHwiNumGet()
  22.     U32 value = OsGicIntAcknowledge();
  23.     U32 irq_num = value & 0x1ff;
  24.     U32 core_num = value & 0xe00;
  25.     OsHwiHandleActive(irq_num);
  26.     // 清除中断,相当于 OsHwiClear(hwiNum);
  27.     OsGicIntClear(irq_num|core_num);
  28. }

新建 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. extern U64 g_timerFrequency;
  6. /* Tick计数 */
  7. OS_SEC_BSS U64 g_uniTicks; //src/core/kernel/sys/prt_sys.c
  8. /*
  9. 描述:Tick中断的处理函数。扫描任务超时链表、扫描超时软件定时器、扫描TSKMON等。
  10. */
  11. OS_SEC_TEXT void OsTickDispatcher(void)
  12. {
  13.     uintptr_t intSave;
  14.     intSave = OsIntLock();
  15.     g_uniTicks++;
  16.     OsIntRestore(intSave);
  17.     U64 cycle = g_timerFrequency / OS_TICK_PER_SECOND;
  18.     OS_EMBED_ASM("MSR CNTP_TVAL_EL0, %0" : : "r"(cycle) : "memory""cc"); //设置中断周期
  19. }
  20. /*
  21. 描述:获取当前的tick计数
  22. */
  23. OS_SEC_L2_TEXT U64 PRT_TickGetCount(void//src/core/kernel/sys/prt_sys_time.c
  24. {
  25.     return g_uniTicks;
  26. }

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

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

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

头文件 src/bsp/os_cpu_armv8_external.h

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

读取系统Tick值

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

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

   main.c 修改为

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

作业1

实现 hwi_init.c 中缺失的 OsGicEnableInt 和 OsGicClearInt 函数。根据老师提供的文档,找到对应函数的寄存器介绍。

GICD_ISENABLER 为 GIC 支持的每个中断提供一个 Set-enable 位。将 1 写入一个 "Set-enable "位可将相应的中断从分配器转发到 CPU 接口。读取该位可确定中断是否已启用。GICD_ICENABLER 为 GIC 支持的每个中断提供一个清除启用位。向 "清除启用 " 位写 1 将禁止从分配器向 CPU 接口转发相应的中断。读取该位可确定中断是否已 启用。具体作用如下:

函数的功能如下:

set-enable

clear-enable

根据老师的提示:

接下来查看OsGicIntSetPriority函数的实现

  1. OS_SEC_L4_TEXT void OsGicIntSetPriority(uint32_t interrupt, uint32_t priority) {
  2.     uint32_t shift = (interrupt % GICD_IPRIORITY_SIZE) * GICD_IPRIORITY_BITS;
  3.     volatile uint32_t* addr = ((volatile U32 *)(uintptr_t)(GICD_IPRIORITYn + (interrupt / GICD_IPRIORITY_SIZE) * sizeof(U32))) ;
  4.     uint32_t value = GIC_REG_READ(addr);
  5.     value &= ~(0xff << shift); // 每个中断占8位,所以掩码为 0xFF
  6.     value |= priority << shift;
  7.     GIC_REG_WRITE(addr, value);
  8. }

参考后写出如下函数:

  1. OS_SEC_L4_TEXT void OsGicDisableInt(U32 intId)
  2. {
  3.     // Interrupt Clear-Enable Registers
  4.     // 中断清除使能寄存器
  5.     uint32_t shift = intId % GICD_ICENABLER_SIZE;
  6.     // 计算位移量,用于确定需要操作的位
  7.     volatile uint32_t* addr = ((volatile U32 *)(uintptr_t)(GICD_ICENABLERn + (intId / GICD_ICENABLER_SIZE) * sizeof(U32)));
  8.     // 计算要操作的中断清除使能寄存器的地址
  9.     uint32_t value = GIC_REG_READ(addr);
  10.     // 从寄存器读取当前值
  11.     value |= (0x1) << shift;
  12.     // 设置对应位,使能中断
  13.     GIC_REG_WRITE(addr, value);
  14.     // 将更新后的值写回寄存器
  15. }
  16. OS_SEC_L4_TEXT void OsGicEnableInt(U32 intId)
  17. {
  18.     // Interrupt Set-Enable Registers
  19.     // 中断使能寄存器
  20.     uint32_t shift = intId % GICD_ISENABLER_SIZE;
  21.     // 计算位移量,用于确定需要操作的位
  22.     volatile uint32_t* addr = ((volatile U32 *)(uintptr_t)(GICD_ISENABLERn + (intId / GICD_ISENABLER_SIZE) * sizeof(U32)));
  23.     // 计算要操作的中断使能寄存器的地址
  24.     uint32_t value = GIC_REG_READ(addr);
  25.     // 从寄存器读取当前值
  26.     value |= (0x1) << shift;
  27.     // 设置对应位,使能中断
  28.     GIC_REG_WRITE(addr, value);
  29.     // 将更新后的值写回寄存器
  30. }

OS_SEC_L4_TEXT void OsGicDisableInt(U32 intId):声明一个函数,名为 OsGicDisableInt,用于禁用指定的中断。U32 是一个 32 位无符号整数类型,intId 是中断 ID。

uint32_t shift = intId % GICD_ICENABLER_SIZE;:计算中断 ID 在当前寄存器中的位移量。

volatile uint32_t* addr = ((volatile U32 *)(uintptr_t)(GICD_ICENABLERn + (intId / GICD_ICENABLER_SIZE) * sizeof(U32)));:计算对应的中断清除使能寄存器的地址。

uint32_t value = GIC_REG_READ(addr);:从寄存器读取当前值。

value |= (0x1) << shift;:将对应的位设置为 1,以禁用该中断。

GIC_REG_WRITE(addr, value);:将更新后的值写回寄存器。

OS_SEC_L4_TEXT void OsGicEnableInt(U32 intId):声明一个函数,名为 OsGicEnableInt,用于使能指定的中断。U32 是一个 32 位无符号整数类型,intId 是中断 ID。

uint32_t shift = intId % GICD_ISENABLER_SIZE;:计算中断 ID 在当前寄存器中的位移量。

volatile uint32_t* addr = ((volatile U32 *)(uintptr_t)(GICD_ISENABLERn + (intId / GICD_ISENABLER_SIZE) * sizeof(U32)));:计算对应的中断使能寄存器的地址。

uint32_t value = GIC_REG_READ(addr);:从寄存器读取当前值。

value |= (0x1) << shift;:将对应的位设置为 1,以使能该中断。

GIC_REG_WRITE(addr, value);:将更新后的值写回寄存器

可见正确运行。

http://www.xdnf.cn/news/4938.html

相关文章:

  • 几款适合Windows的工具,小巧而精致
  • 【软件设计师:多媒体】14.多媒体技术及其应用
  • Excel图表 vs 专业可视化工具:差距有多大?内容摘要
  • Navicat BI 数据分析功能上线 | 数据洞察新方法
  • 计算机网络笔记(十八)——3.5高速以太网
  • Python 打包时包含字库文件的方法
  • 浏览器自动化与网络爬虫实战:工具对比与选型指南
  • 基于springboot的海洋环保知识分享系统的设计与实现
  • 相机的方向和位置
  • 如何在 DataGridView 中加载大型数据集
  • 在一台CentOS服务器上开启多个MySQL服务
  • # 交换排序:从冒泡到快速排序的深度解析
  • WHAT - 简单服务发现
  • 【Bootstrap V4系列】学习入门教程之 组件-表单(Forms)
  • kuka, fanuc, abb机器人和移动相机的标定
  • 03 mysql 连接
  • 使用FastAPI微服务在AWS EKS中构建上下文增强型AI问答系统
  • Istio in action之Envoy Proxy详解
  • React 中二次封装组件时,实现 属性透传、事件透传、插槽透传和 ref 透传
  • iOS App 安全性探索:源码保护、混淆方案与逆向防护日常
  • 互联网大厂Java求职面试:基于RAG的智能问答系统设计与实现
  • 4.1【LLaMA-Factory 实战】医疗领域大模型:从数据到部署的全流程实践
  • clahe算法基本实现
  • Linux 环境通过 tar 多线程压缩和解压
  • 护城河理论——AI与思维模型【100】
  • 5级李克特量表:量化态度的黄金标准
  • 生信服务器如何安装cellranger|生信服务器安装软件|单细胞测序软件安装
  • ndarray数组掩码操作,True和False获取数据
  • vue3: pdf.js5.2.133 using typescript
  • Windows 10 无法启动或黑屏的修复指南(适用于更新失败或磁盘故障)