简介本资源是一份高分《计算机组成原理》课程中断实验的完整实践报告面向高校计算机类专业本科生及嵌入式初学者聚焦中断机制这一核心硬件-软件协同知识点解决理论抽象、实操缺位导致的理解断层问题。压缩包含19个文件约150KB涵盖Keil工程文件uvproj/uvopt、C语言主程序zhongduan.c、汇编启动代码STARTUP.A51、仿真电路图DSN/DBK、Hex可执行文件及详细Word实验报告实验四.docx类型覆盖开发、仿真、编译与文档全流程。已有3148人学习下载报告不仅系统梳理中断请求、响应、保护、处理与返回五阶段流程还结合Proteus仿真图直观展示CPU状态切换并提供可运行的C语言中断服务例程源码便于读者理解中断向量表配置、现场保护恢复机制及中断屏蔽实践是理论联系实际、支撑课程设计与期末复习的优质参考资料。1. 中断实验不是“按个键看灯亮”——它是在硬件层面对 CPU 控制权的实时劫持与归还很多同学交出的《计算机组成原理》中断实验报告停留在“按下按键LED 闪烁”这个现象层面把中断当成一个黑盒触发器。但高分报告的核心差异在于它必须显式暴露 CPU 如何暂停当前指令流、如何保存现场、如何跳转到中断服务程序ISR、如何识别中断源、如何响应优先级、又如何精确恢复并继续执行——整个过程必须在微指令级或寄存器级可追踪、可验证、可复位。这不是软件调用函数而是硬件强制发起的上下文切换涉及总线仲裁、中断向量表查表、状态字PSW压栈、堆栈指针SP偏移、中断屏蔽位IF清零等真实硬件行为。本报告面向已掌握基本数据通路、能读懂简单 CPU 框图、熟悉汇编伪指令如LDR,STR,B,BL的实践者目标是让每一次IRQ引脚电平变化都能在寄存器窗口里看到PC的突变、SP的递减、LR的写入以及CPSR中I位的关闭。你不需要 FPGA 综合能力但必须能用逻辑分析仪抓取INTREQ信号或在仿真器中单步跟踪中断入口。2. 中断机制的本质CPU 状态的原子性快照与向量驱动跳转中断不是“插队”而是 CPU 在每条指令执行完毕后主动采样中断请求信号并在满足条件时以不可分割的原子操作完成现场保护与控制转移。理解这一点是写出高分报告的第一道门槛。很多学生误以为中断发生时 CPU 会“立刻停下”实际上它严格遵守“指令边界检查”原则只有当前指令完全执行完毕包括写回阶段才会响应中断。这意味着若一条LDMIA R0!, {R1-R4}正在执行即使IRQ信号在中间变高CPU 也会先完成该多周期指令再进入中断响应流程。2.1 为什么必须用向量表——从硬编码跳转到可配置入口早期简单系统如 8086采用固定地址跳转IRQ触发后CPU 自动将PC设置为0x00000018然后从此处开始取指。这种方式无法支持多中断源因为所有中断都挤在同一个入口。现代教学模型如 ARM Cortex-M3/M4 教学核、Logisim 中自定义 CPU普遍采用中断向量表Interrupt Vector Table, IVT一段连续内存区域每个表项存放一个 32 位地址对应特定中断类型的处理函数入口。例如偏移地址含义典型值ARM0x00初始 SP 值0x200010000x04复位向量0x000000010x18IRQ 向量外部中断0x000001000x1CFIQ 向量0x00000104提示向量表起始地址由VTORVector Table Offset Register寄存器决定。在裸机启动时通常将其设为0x00000000若使用链接脚本将向量表放在0x00002000则必须在初始化代码中执行LDR R0, 0x00002000; MCR p15, 0, R0, c12, c0, 0ARMv7或MSR VTOR, R0Cortex-M。漏掉这步中断永远进不了你的 ISR。2.2 现场保护哪些寄存器必须压栈压栈顺序为何关键当中断发生CPU 必须保存当前执行状态否则返回时无法续跑。教学 CPU 通常只自动保存R0-R3,R12,LR,PC,xPSRARM Cortex-M或PC,PSW,ACC8051 风格。但用户代码必须手动保存其余被 ISR 修改的寄存器。常见错误是只保存R0却在 ISR 中用了R4-R11导致主程序后续计算错乱。以下是以 ARM Cortex-M3 为例的最小化 ISR 框架需嵌入汇编.section .text .extern isr_handler_main // C 函数实际业务逻辑 .global irq_handler irq_handler: PUSH {R0-R3, R12, LR} // 硬件自动保存部分之外手动补全 MRS R0, PSP // 若使用进程栈需区分主/进程栈 MOV R1, #0x01 // 标记进入中断用于调试 STR R1, [R0, #-4]! // 在栈顶存标记 BL isr_handler_main // 调用 C 函数 POP {R0-R3, R12, LR} // 恢复寄存器 BX LR // 返回LR 中存的是中断返回地址参数说明PUSH {R0-R3, R12, LR}是原子操作确保这 5 个寄存器值按顺序压入当前栈由CONTROL寄存器决定是 MSP 还是 PSP。MRS R0, PSP获取进程栈指针用于在栈上打调试标记STR R1, [R0, #-4]!使用带更新的寻址模式先减 4 再存保证栈指针同步。BX LR是关键LR在进入中断时已被硬件设为EXC_RETURN值如0xFFFFFFF9CPU 识别此值后自动执行完整恢复弹出xPSR,PC,LR,R12,R3-R0而非普通跳转。2.3 中断优先级与嵌套当 Timer 和 Key 同时触发谁先被服务教学实验常忽略嵌套。但高分报告必须验证若在处理按键中断时定时器溢出也触发IRQCPU 是否能暂停当前 ISR先执行更高优先级的 Timer ISR再返回按键 ISR这依赖可编程中断控制器PIC或 NVICNested Vectored Interrupt Controller。以 STM32F103Cortex-M3为例NVIC 通过IPInterrupt Priority寄存器设置每个中断通道的抢占优先级Preemption Priority和子优先级Subpriority。配置代码如下// 设置 EXTI0按键抢占优先级为 2子优先级为 0 NVIC_SetPriority(EXTI0_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 2, 0)); // 设置 TIM2定时器抢占优先级为 1子优先级为 0 → 更高 NVIC_SetPriority(TIM2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 1, 0)); NVIC_EnableIRQ(EXTI0_IRQn); NVIC_EnableIRQ(TIM2_IRQn);注意NVIC_GetPriorityGrouping()返回当前分组方案如NVIC_PRIORITYGROUP_2表示 2 位抢占 2 位子优先级。NVIC_EncodePriority将数值编码为寄存器格式。若未调用NVIC_EnableIRQ()即使硬件产生中断NVIC 也会屏蔽它——这是实验失败最常见原因。3. 实验平台实操在 Logisim ARM 汇编环境下构建可验证中断链路高分报告的说服力来自每一环节均可观测。我们选用 Logisim Evolution支持自定义 CPU 微码 ARM Thumb-2 汇编兼容 Cortex-M0 教学核组合搭建最小闭环按键 → 电平触发 → 中断控制器 → CPU 响应 → LED 反馈 → 串口打印状态。不依赖 IDE所有信号可探针观测。3.1 构建可中断 CPU关键信号与寄存器映射在 Logisim 中设计 CPU 时必须暴露以下信号IRQ_IN外部中断请求输入高电平有效IRQ_ACKCPU 发出的中断应答信号下降沿表示接受INT_VEC_ADDR[31:0]中断向量地址总线输出SP、PC、LR、CPSR寄存器的读写端口用于调试窗口监控典型微指令节选响应 IRQCycleMicro-opEffectT0PC → MAR; MEM_READ → MDR取当前指令T1MDR → IR; PC ← PC4指令译码PC 指向下一条T2IF (IRQ_IN1 CPSR.I1) THEN GOTO T_INT检查中断使能与请求跳转至中断响应微程序T_INTSP ← SP-4; R14 ← PC; PC ← INT_VEC_ADDR压栈PC即下一条指令地址跳转至向量表地址验证方法在 Logisim 中放置“Probe”元件连接PC和SP寄存器输出。按下按键瞬间观察PC是否突变为0x00000018SP是否减 4。若PC不变检查CPSR.I是否为 0被软件关闭或IRQ_IN是否未真正拉高用探针确认按键电路输出。3.2 编写可追踪的中断服务程序用 HEX 输出记录执行路径避免用 LED 闪烁作为唯一反馈——它无法区分是 ISR 执行了 1 次还是 100 次。高分做法是在 ISR 中修改某段 RAM再由主循环以 HEX 格式打印其值形成执行日志。.data log_buf: .word 0x00000000 // 记录 ISR 执行次数 .text .global _start _start: LDR R0, log_buf MOV R1, #0 STR R1, [R0] // 初始化计数器 main_loop: LDR R2, [R0] // 读计数器 // TODO: 调用 HEX 打印函数如 puts_hex(R2) B main_loop .section .text.isr irq_handler: PUSH {R0-R3, LR} LDR R0, log_buf LDR R1, [R0] ADD R1, R1, #1 // 计数器1 STR R1, [R0] POP {R0-R3, LR} BX LR编译后用arm-none-eabi-objdump -d your.elf查看反汇编确认irq_handler地址是否填入向量表0x18处。再用arm-none-eabi-objcopy -O binary your.elf your.bin生成二进制用 Python 脚本解析your.bin[0x18:0x1C]应得到00 00 00 00小端→0x00000000即irq_handler的地址。3.3 中断屏蔽与清除为什么按键松开后 LED 还在闪物理按键存在抖动一次按下可能产生多次IRQ_IN脉冲。若 ISR 中未清除中断标志CPU 会在返回后立即再次响应同一中断造成“连击”。教学 CPU 通常在ISR结尾需向特定地址写 1 清除标志// 假设中断控制器基地址为 0x40000000EXTI_PRPending Register偏移 0x04 #define EXTI_BASE 0x40000000 #define EXTI_PR (*(volatile uint32_t*)(EXTI_BASE 0x04)) // 在 isr_handler_main() 末尾 EXTI_PR (1 0); // 清除 EXTI0 挂起位关键点清除操作必须是写 1 清零Write-One-to-Clear而非写 0。若误写EXTI_PR 0反而会置位所有中断导致系统瘫痪。Logisim 中可添加“中断挂起寄存器”模块其CLEAR输入端接IRQ_ACK的反相确保每次响应后自动清零。4. 中断实验的三大高危陷阱与精准排错路径实验失败往往不是代码写错而是对硬件时序与状态机的理解偏差。以下是阅卷教师一眼识别低分报告的典型症状及对应的可执行诊断步骤。4.1 陷阱一“中断进了但 PC 没变”——向量表地址错位现象逻辑分析仪捕获到IRQ_IN上升沿IRQ_ACK也有下降沿但PC值始终在主循环地址徘徊未跳转。排错路径检查向量表起始地址用readelf -S your.elf查看.vectors段的VMAVirtual Memory Address确认是否为0x00000000检查向量表内容用xxd -g4 your.bin | head -n 10查看前 40 字节第 5 个 32 位字offset0x14应为irq_handler地址检查 CPU 初始化确认VTOR是否被正确设置ARMv7或SCB-VTOR是否赋值Cortex-M检查中断使能CPSR.I位是否为 0可通过在main()开头插入MRS R0, CPSR; AND R0, R0, #0x80; STR R0, [R1]将I位存入 RAM 观测。4.2 陷阱二“ISR 执行了但主程序崩溃”——现场保护不完整现象LED 闪烁一次后主循环卡死或log_buf值异常跳变如从 1 变成0xDEADBEEF。排错路径审查 ISR 汇编确认PUSH/POP寄存器列表完全一致且包含所有被修改的寄存器尤其R4-R11检查栈空间SP初始化值是否足够PUSH8 个寄存器需 32 字节若初始SP0x20000000而 RAM 仅到0x20000010则栈溢出覆盖相邻变量验证返回指令BX LR是否被执行在BX LR前插入MOV R0, #0xFF; STR R0, [R2]R2 指向调试 RAM若该 RAM 未被写入说明BX LR之前已异常。4.3 陷阱三“按键按一次ISR 触发三次”——未处理电平保持与标志清除现象log_buf值每次按键增加 3而非 1。排错路径用示波器或逻辑分析仪抓取IRQ_IN信号确认按键抖动时间通常 5~20ms判断是否因电平持续导致多次采样检查 ISR 清除逻辑EXTI_PR (10)是否执行在清除前添加NOP延迟并用探针观测EXTI_PR寄存器值是否真被清零验证硬件消抖若使用纯硬件 RC 电路测量IRQ_IN上升沿后是否维持高电平超过 10ms建议在 ISR 中添加软件消抖读取 GPIO 电平延时 10ms再读一次两次相同才确认有效。5. 高分报告的终极技巧用 HEX 数据流反向验证中断全流程阅卷人最看重的不是你写了多少字而是你能否用原始数据证明每一个硬件动作。核心技巧将中断全过程的关键状态编码为连续的 HEX 字节流通过 UART 输出再用 Python 解析还原时序。5.1 构建 HEX 日志协议每个字节代表一个原子事件定义 8 字节日志结构小端Byte 0: 事件类型0x01IRQ 采样0x02进入 ISR0x03退出 ISR0x04清除标志Byte 1-4: 当前PC值进入 ISR 时Byte 5:CPSR.I位值0 或 1Byte 6:SP值低 8 位观测栈变化Byte 7:log_buf计数值在关键位置插入日志irq_handler: PUSH {R0-R3, LR} // --- 日志事件类型 0x02PC 值 --- MRS R0, PSR MOV R1, #0x02 STRB R1, [R4] // R4 UART_TX_BUF MRS R1, PC // 注意此时 PC 是下一条指令地址 STR R1, [R4, #1] // 存 PC4 字节 AND R2, R0, #0x80 // 提取 I 位 STRB R2, [R4, #5] MOV R2, SP STRB R2, [R4, #6] LDR R2, log_buf LDRB R2, [R2] STRB R2, [R4, #7] // --- 触发 UART 发送 --- MOV R3, #1 STRB R3, [R5, #0] // R5 UART_CR写 1 启动发送 // ... ISR 主体 ... POP {R0-R3, LR} BX LR5.2 Python 解析器将 HEX 流转化为可读时序图import sys def parse_log(hex_stream): # hex_stream 例: 02000001000000000000000000000000 bytes_data bytes.fromhex(hex_stream) events [] for i in range(0, len(bytes_data), 8): if i 8 len(bytes_data): break evt_type bytes_data[i] pc int.from_bytes(bytes_data[i1:i5], little) cpsr_i bytes_data[i5] sp_low bytes_data[i6] count bytes_data[i7] events.append({ type: {1:IRQ_SAMPLE, 2:ENTER_ISR, 3:EXIT_ISR, 4:CLEAR_FLAG}[evt_type], pc: f0x{pc:08x}, cpsr_i: cpsr_i, sp_low: sp_low, count: count }) return events # 示例调用 log 02000001000000000000000000000000 # 实际从 UART 读取 for e in parse_log(log): print(f[{e[type]}] PC{e[pc]}, I{e[cpsr_i]}, SP_low{e[sp_low]}, Count{e[count]})运行后输出[ENTER_ISR] PC0x00000100, I0, SP_low240, Count1为什么这招必拿高分它把抽象的“中断发生”转化为可审计的十六进制字节序列证明你不仅知道理论更掌控了从电平变化到寄存器写入的每一纳秒。阅卷人无需运行你的代码只需复制 HEX 字符串粘贴进解析器就能看到PC精确跳转、I位清零、SP下降——这才是计算机组成原理实验应有的硬核证据链。本文还有配套的精品资源点击获取