General Interrupt Controller

Polling: keep waiting, waste of resources

Interrupt: keep doing other things until interruption


ICCICR: must be set to 1 so that interrupt can happen

ICCPMR: priority: 0 has the highest priority;
Only if the hardware has a priority level higher than ICCPMR, this interrupt would be triggered.

ICCIAR: stores interrupt ID (who triggers interrupt)

ICCEOIR: write the interrupt ID back to reset interrupt status

Push Button (KEY) manual:

Interval Timer

Interval Timer manual:

Q: Periodl(base+8) 和 Periodh(base+12) 怎样用
A: 譬如你想放十进制为25000000的数进去,红色框的数放 Periodl ;蓝色框的数放 Periodh ,也就是把0d381放 Periodh。


Masked = Disabled

[7] Interrupt Disable

Interrupt should be set to disabled(I=1) until all hardware you use(KEYs, Timers) has enabled interrupt-trigger.

0x0Exception not masked.
0b1Exception masked.

[6] Fast Interrupt Disable

0x0Exception not masked.
0b1Exception masked. (set to 1 in ECE243)

[5] Thumb Enable

0x0Thumb mode disabled. (set to 0 in ECE243)
0b1 Thumb mode enabled.

[4:0] Mode Bits

User10000Normal program execution, no privileges
FIQ10001Fast interrupt handling
IRQ10010Normal interrupt handling
Supervisor10011Privileged mode for the operating system
Abort10111For virtual memory and memory protection
Undefined11011Facilitates emulation of co-processors in hardware
System11111Runs programs with some privileges

Stack Pointer

You can modify SP at any time. You can even treat SP as a regular register if you don’t need a stack in your program(which is usually a bad programming style and thus not recommended).

Regular operations are shown below (assume each snippets are from independent programs and will not affect each other):

// backup and restore R0
POP {R0}

// backup R0, and restore its backup value into R1
POP {R1}

// backup LR, and restore its backup value into PC
// Think again. What exactly is this doing?
BL someSubroutine // LR gets the address of "here"
here: ...

BL anotherSubroutine // LR gets the address of "there"


// When the system starts, the default value of SP
//  on our DE1-SOC machine is 0x1 0000 0000 
// (Well you can say it is 0x0000 0000 if you want)
// What if I want to modify it? Try step into each of below instructions
LDR SP,=256 // 256=0x100
MOV R0,#1
PUSH {R0} // What is the content at 0x100-4
POP {R0} // What is the content at 0x100-4

// What is the value of R1, R2 and R3?
MOV R1, #1
MOV R2, #2
MOV R3, #3

PUSH {R1-R3}
POP {R2,R3,R1}

// 3 => R2
// 2 => R3
// 1 => R1
// initial stack pointer => end stack pointer

Banked Registers

Helps you to switch between modes.


  1. 什么是SPSR?
  2. 下图有多少个SPSR?谁没有SPSR?
  3. 为什么需要这么多个SPSR?


  1. (Saved CPSR): Holds the saved process state for the current mode.
  2. 5个SPSR,user mode并不需要SPSR
  3. 将SPSR复制到CPSR即可还原到切换前的mode。 如果User mode切换到IRQ mode后,又被切换到SVC mode;那SVC mode下可将SPSR_svc复制到CPSR来还原到IRQ mode,IRQ mode下可将SPSR_irq 复制到CPSR来还原到user mode。


  1. 从user mode切换到IRQ mode后, 当前执行程序的 SP还是user mode的SP吗?
  2. 从user mode切换到IRQ mode后,当前执行程序的LR还是user mode的LR吗?
  3. 从user mode切换到IRQ mode后, 当前执行程序的 PC还是user mode的PC吗?
  4. 从user mode切换到IRQ mode后, 当前执行程序的 General Registers(R0-12) 还是user mode的 General Registers 吗?
  5. 事实上CPULator并不支持user mode,所以前面的题把user mode换成SVC mode再答一遍


  1. 不是,这时是IRQ mode的SP
  2. 不是,这时是IRQ mode的LR,刚被切换到IRQ mode时LR_irq会获得user mode里还没执行的指令的地址+4
  3. 是的,每个(单核心)处理器PC只有一个,指向下一个还未执行的指令的地址
  4. 是的,每个 (单核心) 处理器只有一套General Registers,任意mode都可以更改他们的值
  5. 事实上,把以上答案中user mode换成SVC mode,答案一样(只是考试可能不问user mode,所以改题大家试着做

Interrupt Procedure


  1. 程序中有.vector 这个section(也称”exception table”)
  2. .vector section中 0x18 这行地址的指令为 “B SERVICE_IRQ”,确保能在interrupt发生时,能正确进入Interrupt ReQuest handler
  3. GIC有正确设置,且该硬件设置了会触发interrupt
  4. SERVICE_IRQ的subroutine里,各硬件有对应的ISR handler
  5. Interrupt Service Routine里做想做的事,并能够正确返回到 Interrupt ReQuest handler


  1. 处理器会马上停下现在做的事,GIC会保存好当前mode的特殊register(SP, PC, CPSR),切换mode为IRQ_MODE,PC值变为0x18
  2. 0x18正正是”B SERVICE_IRQ” ,我们会进入 SERVICE_IRQ 这个label所对应的subroutine
  3. 在 SERVICE_IRQ 里,先备份好数据register(R0-R10,其实是用多少备份多少,教授的码里推荐R0-R7),读取 ICCIAR到R5(或其他register,此处仅用R5作例) ,并与一系列interrupt id对比
  4. 如果R5的值为73,则进入KEY_ISR这个subroutine
  5. KEY_ISR做完想做的事之后,返回 SERVICE_IRQ 里,还原之前备份的数据register(R0-R10),通过将interrupt id(R5)写回ICCIAR的方式,重置interrupt status(如果不这样做,退出IRQ之后GIC马上又会激活interrupt)
  6. 退出interrupt,GIC帮忙还原SP, PC, CPSR

Program Counter



  • PC作为register存储的是下一个要跑的instruction(要执行但还未执行的instruction)的地址;
  • PC作为op2在instruction里出现时(MOV R0, PC),则PC代表一个地址,为(这一行instruction的地址+8)


  • 在执行BL someLabel时,在branch into someLabel之前,BL someLabel这一行opcode的地址+4被储存在LR中
  • 在执行普通程序时,如果发生interrupt,第一个未执行的指令的地址+4被储存在IRQ mode的LR中

Special thanks to Dr. Henry Wong( who provided this perspective on Piazza in Winter 2019, even when he was not a TA at that time. He motivates me to post those review materials for free.


This is not the official position of ECE243, but I disagree sufficiently with the way PC is taught that I think an alternative view would be useful.
What you’ve learned so far is

  1. The CPU has a set of registers,
  2. at any instantaneous moment in time, each register has one value, and
  3. the register values change when you execute an instruction or interrupt (or a few other things).

PC isn’t really a register by the above definition, and cannot be treated as one (it violates #2). ECE243 tries really hard to explain PC as if it were a register, but when you dig deep
enough you’re left with a bunch of inconsistencies in behaviour that are very difficult to explain away, and it’s these inconsistencies that lead you to ask questions like this one and
@457. Congrats on detecting the lie.

What’s a better explanation of PC?

Each instruction is an opcode in me mory, and PC is the location of the opcode. PC is not a physical object. It is just the concept of the location of an instruction. This means that questions like “What is the value of PC” don’t make sense, but questions like “What is the PC of this instruction?” do make sense.
The correct question to ask is “What value do I observe when I look at PC from some perspective?” What you observe depends on the method you use to observe it:

  • From the debugger (or at instruction fetch): PC points to the instruction that has not yet executed. This is what the debugger shows you.
  • Reading r15 as an operand of an instruction (e.g., mov r0, pc or ldr r0, [pc]): The observed value is the PC of the instruction itself + 8.
  • Writing r15 as the destination operand of an instruction (e.g., mov pc, lr): PC points to the next instruction to execute (same as the first definition above)
  • Branch-and-link instructions (function call): The observed value (the value written to LR) is the PC of the instruction itself + 4.
  • Interrupts: The observed value (the one written to LR) is the PC of the first unexecuted instruction + 4 (so that returning to LR-4 is correct). Note that this is not the same as “PC of the last completed instruction + 8”
  • Software exception instruction: The observed value (written to LR) is the PC of the SVC instruction + 4.

(I might be missing a few cases)
The point is: PC is not a register, and does not have a single value. What matters is what you get when you look at “PC”, and the ISA specifies all the ways you can observe “PC”,
the meaning of each, and offsets that depend on the method you use to observe.

There are a few other imaginary things invented to fit the view of PC being a register. One of the most confusing is the concept of a “currently-executing instruction” and whether this “current instruction” is completed or aborted at an interrupt. (The logic behind it is: If we suppose that PC has a single global value, then there must be a “current” instruction
related to this value, and thus we must define whether the “current” instruction is completed or aborted at an interrupt to make it consistent with the observations about LR during an interrupt). These explanations need to exist only if you try to explain PC as if it were a register.
At a precise interrupt, all instructions are completed up to some point, and all instructions after that point have not executed. Then the LR is set to the PC of the first non-executed instruction + 4. I think the clearest way to express this idea is to think of interrupts as occurring in between instructions, not at an instruction that is then completed/aborted.

So to directly answer your questions using the above interpretation:

  1. If you are looking at PC from the debugger, yes. PC points to the instruction that hasn’t yet executed, which is at 0x0.
  2. If you’re still looking at PC from the debugger, then PC would be 0x4 (the location of the instruction that hasn’t yet exe cuted) unless the instruction was a taken branch. What you
    were probably trying to ask was “If the instruction at 0x0 used PC as a source operand, would it see 0x8?”, and the answer is yes: When reading pc as a source operand, the
    observed value is the PC of the instruction itself + 8.
  3. BL sets LR to (location of the BL instruction + 4). Interrupts set LR to (location of first unexecuted instruction + 4).


.global _start
	// using this address to write directly to the buffer
	ldr r7,=0xc8000000
	ldr r0,=0b11111
	LDR R1,xOffset
	ADD R1, #1
	STR R1,xOffset
	CMP R1, #320
	LSL R1, #1
	LDR R2,yOffset
	LSL R2,#10
	ORR R1,R2
	STRH R0, [r7,R1]
	MOV R1, #0
	STR R1,xOffset
	LDR R2, yOffset
	ADD R2,#1
	STR R2, yOffset
	CMP R2, #240
	.word 0
	.word 0


Located in Markham, I am working as an Automotive Display Driver Engineer at Qualcomm Canada Inc. Previously, I graduated as a Computer Engineering undergraduate student at the University of Toronto and worked as an ECE297 TA there. As I once tutored ECE243 and APS105 at EngFastlane, now I am also providing tutoring service at TopLogic Inc.. I am proficient with C, C++, JavaScript and Python and familiar with PSQL, Java, Intel FPGA Verilog and ARM Assembly(V7). My interest is in Software Design and Development.

1 Comment

Final Review - Junhao · 19 April 2020 at 10:39 PM

[…] Protected: DE1-SoC Hardware […]

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *