移植 u-boot-2020.07 到 iTOP-4412(四)支持中断

文章目录

  • 一、Interrupt principle
    • 1. arm
      • 1.1 registers
      • 1.2 mode
      • 1.3 CPSR register
      • 1.4 CPSR & CPSR_c difference
    • 2. exynos-4412
  • 二、Button interrupt
    • 1. Peripherals level
    • 2. GIC
    • 3. ARM core (cpu0)
  • 三、u-boot
    • 1. relocate_vector()
    • 2. irq init
    • 3. jump to do_irq()
    • 4. cpsr register
    • 5. verification
  • Reference

一、Interrupt principle

1. arm

1.1 registers

ARM 处理器一般共有 37 个寄存器,其中包括:

  1. 31 个通用寄存器,包括 PC(程序计数器)在内,都是 32 位的寄存器。

  2. 6 个状态寄存器,都是 32 位的寄存器。

1.2 mode

ARM 处理器共有 7 种不同的处理器模式:

模式 Mode
用户模式 User
快速中断模式 FIQ
普通中断模式 IRQ
管理模式 Svc
数据访问中止模式 Abort
未定义指令中止模式 Und
系统模式 Sys

   在每一种处理器模式中有一组相应的寄存器。在任意一种处理器模式下,可见的寄存器包括 15 个通用寄存器(R0~R14)、一个或者二个状态寄存器以及程序计数器(PC)。在所有的寄存器中,有些是各模式共用同一个物理寄存器,有些寄存器是各个模式自己拥有独立的物理寄存器

移植 u-boot-2020.07 到 iTOP-4412(四)支持中断_第1张图片
   其中 r0~r3 主要用于子程序间传递参数, r4~r11 主要用于保存局部变量,但在 Thumb 程序中,通常只能使用 r4~r7 来保存局部变量; r12 用作子程序间scratch 寄存器,即 ip 寄存器; r13 通常用做栈指针,即 sp; r14 寄存器又被称为连接寄存器(lr),用于保存子程序以及中断的返回地址; r15 用作程序计数器(pc),由于 ARM 采用了流水线机制,当正确读取了 PC 的值后,该值为当前指令地址加 8 个字节,即 PC 指向当前指令的下两条指令地址。

1.3 CPSR register

   CPSR和SPSR都是程序状态寄存器,其中SPSR是用来保存中断前的CPSR中的值,以便在中断返回之后恢复处理器程序状态。

移植 u-boot-2020.07 到 iTOP-4412(四)支持中断_第2张图片
   所有处理器模式下都可访问当前程序状态寄存器CPSR。CPSR中包含条件码标志、中断禁止位、当前处理器模式以及其他状态和控制信息。在每种异常模式下都有一个对用的程序状态寄存器SPSR。当异常出现时,SPSR用于保存CPSR的状态,以便异常返回后恢复异常发生时的工作状态。

(1)条件码标志

N、Z、C、V,最高4位称为条件码标志。ARM的大多数指令可以条件执行的,即通过检测这些条件码标志来决定程序指令如何执行。

各个条件码的含义如下:

N:在结果是有符号的二进制补码情况下,如果结果为负数,则N=1;如果结果为非负数,则N=0。

Z:如果结果为0,则Z=1;如果结果为非零,则Z=0。

C:其设置分一下几种情况:

  • 对于加法指令(包含比较指令CMN),如果产生进位,则C=1;否则C=0。
  • 对于减法指令(包括比较指令CMP),如果产生借位,则C=0;否则C=1。
  • 对于有移位操作的非法指令,C为移位操作中最后移出位的值。
  • 对于其他指令,C通常不变。

V:对于加减法指令,在操作数和结果是有符号的整数时,如果发生溢出,则V=1;如果无溢出发生,则V=0;对于其他指令,V通常不发生变化。

(2)控制位的作用在图1中可以看出,在这里就不阐述了。

1.4 CPSR & CPSR_c difference

   CPSR_c指的是CPSR的低8位控制位

   CPSR有4个8位区域:标志域(F)、状态域(S)、扩展域(X)、控制域(C)

   MSR - Load specified fields of the CPSR or SPSR with an immediate constant, or from the contents of a general-purpose register.

   Syntax:

   MSR{cond} , #immed_8r MSR{cond} , Rm where: cond is an optional condition code. is either CPSR or SPSR. specifies the field or fields to be moved. can be one or more of:

   c control field mask byte (PSR[7:0]) x extension field mask byte (PSR[15:8]) s status field mask byte (PSR[23:16) f flags field mask byte (PSR[31:24]). immed_8r is an expression evaluating to a numeric constant. The constant must correspond to an 8-bit pattern rotated by an even number of bits within a 32-bit word. Rm is the source register.

  • C 控制域屏蔽字节(psr[7:0])
  • X 扩展域屏蔽字节(psr[15:8])
  • S 状态域屏蔽字节(psr[23:16])
  • F 标志域屏蔽字节(psr[31:24])

   常用于MRS或MSR指令,用于psr中的值转移到寄存器或把寄存器的内容加载到psr中.

如:

  • MSR CPSR_c,#0xd3

2. exynos-4412

移植 u-boot-2020.07 到 iTOP-4412(四)支持中断_第3张图片
中断处理过程,详细的寄存器操作在后面实现中介绍。

注:

   Exynos4412中断控制器包括160个中断控制源,这些中断源来自软中断(SGI),私有外部中断(PPI),公共外部中断(SPI)

   Exynos4412采用GIC中断控制器,主要是因为Contex-A9 是多核处理器,GIC(Generic Interrupt Controller)通用中断控制器用来选择使用哪个CPU接口,具体主要有两个功能:

  1. 分配器:设置一个开关,是否接收外部中断源;为该中断源选择CPU接口;

  2. CPU接口:设置一个开发,是否接受该中断源请求;

二、Button interrupt

iTOP-4412 板子原理图:
移植 u-boot-2020.07 到 iTOP-4412(四)支持中断_第4张图片
   我们就以 HOME 键作为中断源。

1. Peripherals level

1-- 将GPX1_1引脚的上拉和下拉禁止

   GPX1PUD[3:2] = 0;

2 – 将GPX1_1引脚功能设置为中断功能 WAKEUP_INT1[1] — EXT_INT41[1]

   GPX1CON[7:4] = 0xF

3 – EXT_INT41CON 配置触发电平

   当前配置成下降沿触发:

   EXT_INT41CON[6:4] = 0x2

4 – EXT_INT41_FLTCON0 配置中断引脚滤波

   默认就是打开的,不需要配置

5 – EXT_INT41_MASK 中断使能寄存器

   使能INT41[1]

   EXT_INT41_MASK[1] = 0b0

6 – EXT_INT41_PEND 中断状态寄存器
   当GPX1_1引脚接收到中断信号,中断发生,中断状态寄存器EXT_INT41_PEND 相应位会自动置1
   注意:中断处理完成的时候,需要清除相应状态位。置1清0.
   EXT_INT41_PEND[1] = 0b1

2. GIC

1-- 找到外设中断名称和GIC中断控制器对应的名称

   查看芯片手册(本例:Exynos_4412 – 9.2表)
   WAKEUP_INT1[1] — EXT_INT41[1] — INT[9] — SPI[25]/ID[57]

   其对应INT[9],中断ID为57,这是非常重要的,在后面的寄存器设置中起很大作用;

下面是外设与中断控制器处理具体流程:
移植 u-boot-2020.07 到 iTOP-4412(四)支持中断_第5张图片
2 – GIC使能

   ICDDCR =1;

   使能分配器。

3 – 使能相应中断到分配器

   ICDISER.ICDISER1 |= (0x1 << 25); //57/32 =1…25 取整数(那个寄存器) 和余数(哪位)

   ICDISER用于使能相应中断到分配器,一个bit控制一个中断源,一个ICDISER可以控制32个中断源,这里INT[9] 对应的中断ID为57,所以在ICDSER1中进行设置,57/32 =1余25,所以这里在ICDISER1第25位置一。

4 – 选择CPU接口

   设置SPI[25]/ID[57]由那个cpu处理,当前设置为cpu0的irq中断

   ICDIPTR.ICDIPTR14 |= 0x01<<8; //SPI25 interrupts are sent to processor 0 //57/4 = 14…1 14号寄存器的[15:8]

   ICDIPTR寄存器每8个bit 控制一个中断源

5 – 全局使能cpu0中断处理

   CPU0.ICCICR |= 0x1;

   使能中断到CPU。

6 – 优先级屏蔽寄存器,设置cpu0能处理所有的中断。

   CPU0.ICCPMR = 0xFF;

3. ARM core (cpu0)

前面两步设置好,就可以等待中断的发生了,当中断发生时,ARM内核的处理过程如下:

1-- 四大步三小步 — 硬件
移植 u-boot-2020.07 到 iTOP-4412(四)支持中断_第6张图片

  1. 拷贝 CPSR 到 SPSR_
  2. 设置适当的 CPSR 位:
    1. 改变处理器状态进入 ARM 态
    2. 改变处理器模式进入相应的异常模式
    3. 设置中断禁止位禁止相应中断 (如果需要)
  3. 保存返回地址到 LR_
  4. 设置 PC 为相应的异常向量

2 – 中断服务程序 — start.S 汇编

.text
.global _start
_start:
		b		reset
		ldr		pc,_undefined_instruction
		ldr		pc,_software_interrupt
		ldr		pc,_prefetch_abort
		ldr		pc,_data_abort
		ldr		pc,_not_used
		ldr		pc,_irq
		ldr		pc,_fiq
 
_undefined_instruction: .word  _undefined_instruction
_software_interrupt:	.word  _software_interrupt
_prefetch_abort:		.word  _prefetch_abort
_data_abort:			.word  _data_abort
_not_used:				.word  _not_used
_irq:					.word  irq_handler
_fiq:					.word  _fiq
 
 
reset:
 
	ldr	r0,=0x40008000
	mcr	p15,0,r0,c12,c0,0		@ Vector Base Address Register
 
 
init_stack:
		ldr		r0,stacktop         /*get stack top pointer*/
 
	/********svc mode stack********/
		mov		sp,r0
		sub		r0,#128*4          /*512 byte  for irq mode of stack*/
	/****irq mode stack**/
		msr		cpsr,#0xd2
		mov		sp,r0
		sub		r0,#128*4          /*512 byte  for irq mode of stack*/
	/***fiq mode stack***/
		msr 	cpsr,#0xd1
		mov		sp,r0
		sub		r0,#0
	/***abort mode stack***/
		msr		cpsr,#0xd7
		mov		sp,r0
		sub		r0,#0
	/***undefine mode stack***/
		msr		cpsr,#0xdb
		mov		sp,r0
		sub		r0,#0
   /*** sys mode and usr mode stack ***/
		msr		cpsr,#0x10
		mov		sp,r0             /*1024 byte  for user mode of stack*/
 
		b		main
 
	.align	4
 
	/****  swi_interrupt handler  ****/
 
 
	/****  irq_handler  ****/
irq_handler:
 
	sub  lr,lr,#4
	stmfd sp!,{r0-r12,lr}
	.weak do_irq
	bl	do_irq
	ldmfd sp!,{r0-r12,pc}^
 
stacktop:    .word 		stack+4*512
.data
 
stack:	 .space  4*512

3–中断处理程序 — do_irq函数 c语言(函数原型void name(void))

(1) 读取正在处理的中断ID寄存器(ICCIAR)

   irq_num = (CPU0.ICCIAR & 0x3FF);

(2)根据irq_num,分支处理中断

switch(irq_num)
{
	case 57:
		break;
}

(3)清除中断状态位

   (3-1)i.外设级,EXT_INT41_PEND |= 0x1 << 1;
   (3-2)ii.GIC级,ICDICPR.ICDICPR1 |= 0x1 << 25;
   (3-3)iii.CPU0级 CPU0.ICCEOIR = (CPU0.ICCEOIR & ~(0x1FF)) | irq_num;

下面是C 程序:

#include "exynos_4412.h"
#include "led.h"
 
void  delay_ms(unsigned int num)
{
    int i,j;
    for(i=num; i>0;i--)
		for(j=1000;j>0;j--)
			;
}
void do_irq(void)
{
	static int a = 1;
	int irq_num;
	irq_num = CPU0.ICCIAR&0x3ff;  //获取中断号
	switch(irq_num)
	{
	case 57:
		printf("in the irq_handler\n");
			if(a)
				led_on(1);
			else
				led_off(1);
			a = !a;
			EXT_INT41_PEND = EXT_INT41_PEND |((0x1 << 1)); //清GPIO中断标志位
			ICDICPR.ICDICPR1 = ICDICPR.ICDICPR1 | (0x1 << 25); //清GIC中断标志位
		break;
	}
	CPU0.ICCEOIR = CPU0.ICCEOIR&(~(0x3ff))|irq_num; //清cpu中断标志位
}
/*
 *  裸机代码,不同于LINUX 应用层, 一定加循环控制
 */
int main (void)
{
	GPX1.CON =GPX1.CON & (~(0xf << 4)) |(0xf << 4); //配置引脚功能为外部中断
	GPX1.PUD = GPX1.PUD & (~(0x3 << 2));  //关闭上下拉电阻
	EXT_INT41_CON = EXT_INT41_CON &(~(0xf << 4))|(0x2 << 4); //外部中断触发方式
	EXT_INT41_MASK = EXT_INT41_MASK & (~(0x1 << 1));  //使能中断
	ICDDCR = 1;  //使能分配器
	ICDISER.ICDISER1 = ICDISER.ICDISER1 | (0x1 << 25); //使能相应中断到分配器
	ICDIPTR.ICDIPTR14 = ICDIPTR.ICDIPTR14 & (~(0xff << 8))|(0x1 << 8); //选择CPU接口
	CPU0.ICCPMR = 255; //中断屏蔽优先级
	CPU0.ICCICR = 1;   //使能中断到CPU
	led_init();
	while(1) {}
   return 0;
}

三、u-boot

   u-boot 默认没有打开中断的功能,也没有找到相关的配置选项,不过阅读源码可以发现,基本的异常向量表、relocate_vector 还是支持的,所以稍加修改应该就可以支持中断,这里也使用前文提到的 HOME 按键的中断进行验证。

1. relocate_vector()

   在 u-boot(并非 spl)调用 relocate_code() 重定位 RAM 中的 u-boot.bin 后,会返回 here ,第一个执行的程序就是 relocate_vector(),该函数主要作用是设置 VBAR(Vector Base Address Register),VBAR 的细节还没有了解过,不过肯定就是指示异常向量的基地址了,暂时不深究了。

去掉无关的宏后,实际函数:

/*
 * If the ARM processor has the security extensions,
 * use VBAR to relocate the exception vectors.
 */
ldr	r0, [r9, #GD_RELOCADDR]	/* r0 = gd->relocaddr */
mcr     p15, 0, r0, c12, c0, 0  /* Set VBAR */

第一行获得重定位后的基地址,同时也是异常向量表的基地址。
第二行将该基地址存放在 VBAR 中,以后发生异常后就会来该地址寻找异常向量。

2. irq init

   中断寄存器的初始化就按照第二章的描述进行就可以了,相比以前学习的 2440 确实复杂很多,但是实际上搞清楚后其实也就那样,无非是对应的关系变复杂了,需要配置和检查的寄存器变多了。

3. jump to do_irq()

函数 do_irq() 定义在:

  • arch/arm/lib/interrupts.c

根据 vectors.S 里的定义:

	b	reset
	ldr	pc, _undefined_instruction
	ldr	pc, _software_interrupt
	ldr	pc, _prefetch_abort
	ldr	pc, _data_abort
	ldr	pc, _not_used
	ldr	pc, _irq
	ldr	pc, _fiq

_undefined_instruction:	.word undefined_instruction
_software_interrupt:	.word software_interrupt
_prefetch_abort:	.word prefetch_abort
_data_abort:		.word data_abort
_not_used:		.word not_used
_irq:			.word irq
_fiq:			.word fiq

发生 interrupt 后,会跳转到 irq 执行。
仔细分析原有的 irq 函数使用的设置堆栈和保存现场的方法不对,需要稍作修改,SP 的地址可以比较随意,这里就设置 iRAM 的最高地址吧:

irq:
	get_irq_stack
	irq_save_user_regs
	bl	do_irq
	irq_restore_user_regs

.globl IRQ_STACK_START
IRQ_STACK_START:
	.word	0x02060000 // irq stack

4. cpsr register

   这里非常重要,虽然前面打开了很多寄存器的样子,但是实际上中断并不能到达 arm core,因为 u-boot 中并没有打开 cpsr 的 irq enable,在任意位置打开就好了,我为了方便打印该值,就在 C 函数里做了。

	unsigned long i;
	__asm__ __volatile__ ( 
		"mrs %0, cpsr\n\t"
		"bic %0, %0, #0x80\n\t"
		"msr cpsr,%0\n\t" : "=r" (i):);
	printf("cpsr = 0x%08lX!\n", i);

5. verification

中断处理函数:

void do_irq (struct pt_regs *pt_regs)
{
	static int irq_count = 0;
	(*((volatile unsigned long*)(0x11000104)) ^= (1)); // led
	printf("good irq irq_count = %d!\n", irq_count++);
	(*((volatile unsigned long*)(0x11000F44)) |= (1 << 1)); // clear interrupt
}

效果:
移植 u-boot-2020.07 到 iTOP-4412(四)支持中断_第7张图片
   可以看出中断发生后正确恢复现场,可以通过回车等和 u-boot 交互,支持中断成功。

   当然支持中断并不是为了点个灯,主要还是为了改善 u-boot 输入时一卡一卡的手感,并且明明输入了很多很多字符却不显示,但是实际却起了作用的 bug。

   关于 UART 的优化:移植 u-boot-2020.07 到 iTOP-4412(三)重定位、UART

   源码下载:

Reference

  • ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition 不知道为啥下载下来打不开。。

  • Exynos4412裸机开发——中断处理

  • ARM寄存器及功能介绍

你可能感兴趣的:(移植 u-boot-2020.07 到 iTOP-4412(四)支持中断)