feat(board): 中断综合测试程序上板 + 可变分频/刷新率;README 大幅扩写
板级顶层(srcs/board/cpu21_board_top.v) - ROM 增加 RISC_V_INTRUPT_CCAB_ROM 分支,中断入口 0x0AC/0x1E4/0x308; 与 SAOLEI_ROM / BOARD_ROM_FAST / 默认跑马灯构成 ifdef 链 - 新增参数 CLK_DIV_INTRUPT:跑中断测试程序时用更慢的 CPU 时钟(默认 32 = 3.125 MHz),让数码管上每个数值停留约 160 ms 便于观察 - 数码管扫描分频随 CPU 分频缩放(SEG_REFRESH_DIV = 100000/CPU_DIV), 保证每位数码管仍是 125 Hz(不闪);数码管改显示 MMIO 的 led_data; LED[2:0] 改为锁存式中断指示(请求上升沿点亮、对应 ISR 执行 uret 后熄灭) - 复位同步器/led_we 指示等注释同步更新;srcs/rtl/cpu21_seg7.v 分频计数 加宽到 20 位以容纳随主频变化的 REFRESH_DIV 工具与程序 - tools/adapt_intrupt_ccab.py:把课程中断测试镜像适配成上板 ROM。六处适配 (ISR 停机 ecall、显示改 MMIO+DISP_DLY、ISR2/ISR3 出栈顺序、ISR3 SB 测试 用未保存寄存器、ISR 破坏 ra 的 DISP_DLY_ISR),全部 2 字换 2 字, 不移动任何指令地址;带断言与 jal 目标自检 - programs/risc-v-intrupt-ccab.asm/.hex:课程中断综合测试程序(含六处适配说明) - create_project.tcl:新增 use_intrupt_ccab / use_saolei 开关,自动把对应 ROM 头文件按 Verilog Header 加入 sources_1 并定义宏 - tools/rars_f0c874c.jar:课程 RARS 汇编器移到 tools/ 文档(README.md) - 新增"各模块的实现方式":按流水线主体 / 外设 / 板级逐模块讲实现要点 - 新增"内置程序与宏的用法":ROM 宏表(含各自的中断入口)、GUI/Tcl/命令行 三种定义方式、中断程序的适配流程与仿真验证方法 - 新增"分频与刷新率一览":CLK_DIV / CLK_DIV_INTRUPT / SEG_REFRESH_DIV / led_we 时长 / DISP_DLY 延时常量分别在哪里改、怎么换算 - 修正引脚表(LED[2:0] 中断指示、led_we 0.34 s、CPU_DIV)与 ROM/RAM 说明 (ROM 8192 字 = 32 KB),补充中断程序上板现象与 ROM 头文件清单 验证:不带宏 / 带 RISC_V_INTRUPT_CCAB_ROM 两种配置 xvlog 编译均无错误; 三路嵌套与 IRQ3 抢占 ISR1 的仿真通过(level 逐级升降、uepc 正确恢复、 halted=0);tools/check_rtl_text.ps1 体检 OK。
This commit is contained in:
@@ -0,0 +1,498 @@
|
||||
# =============================================================================
|
||||
# 注意:上板/仿真真正使用的镜像是 programs/risc-v-intrupt-ccab.hex(270 字),
|
||||
# 本文件是课程原始清单。由 tools/adapt_intrupt_ccab.py 生成 ROM 时会做六处适配:
|
||||
# 1) 三个 ISR 里的 "addi a7,zero,10 + ecall" 会让这颗 CPU 停机(a7 != 34 的
|
||||
# ecall 是"停机等 go_i"的约定),中断服务程序跑到那里就卡住、后面的中断显示
|
||||
# 循环永远不执行 -> ROM 里换成两条空操作;
|
||||
# 2) 原来的 "addi a7,zero,34 + ecall" 显示改成写 MMIO 的 LED 寄存器(0x1000),
|
||||
# 并且每个显示点都改为调用 DISP_DLY 加约 10 ms 延时 —— 否则 50 MHz 下刷新
|
||||
# 太快,数码管的动态扫描根本分辨不出来,只会看到一片亮;
|
||||
# 3) DISP_DLY 追加在程序末尾(0x438),因此所有指令地址、特别是三个中断入口
|
||||
# 0x0AC / 0x1E4 / 0x308 都保持不变;
|
||||
# 4) **hex 里 ISR2 / ISR3 的现场恢复(出栈)顺序被写乱了**(本清单是正确的,
|
||||
# ISR1 的 hex 也是正确的)。ISR3 把 s1 从 s3 的槽位弹出,于是 s1 被写成
|
||||
# s3 保存的 0xFFFFF111;ISR3 返回后,正在跑 BLT 测试的 ISR2 的循环计数 s1
|
||||
# 就变成 -3823,循环要跑约 170 万周期才退出,表现就是"二级/三级中断返回
|
||||
# 之后卡住";ISR2 的 s0/s2/a0/a7/s1/t3/t1 也全部错位。适配脚本按本清单的
|
||||
# 正确 LIFO 顺序改回 12 条 lw 的目标寄存器(字数不变、地址不动)。
|
||||
# 5) **ISR3 的 SB 测试用了它自己没有保存的寄存器**(t1 当地址、t3 当计数、
|
||||
# s2 当增量)。ISR3 的现场只保存了 s1/a7/a0/s0/s3/s4/s5/s6/uepc,所以:
|
||||
# 若 IRQ3 直接抢占正在跑 DIVU 测试的 ISR1(ISR1 用 t3 递减计数),ISR1 的
|
||||
# 计数会被改掉;ISR1 恢复后 `addi t3,t3,-1` 得到负数,`bne t3,zero` 永远
|
||||
# 成立,DIVU 循环再也回不来 —— 表现就是“拨了 SW0 再拨 SW2 之后卡住”;
|
||||
# 主程序的跑马灯步长 t1 也会被改掉。适配脚本把这 14 处寄存器域改成 ISR3
|
||||
# 自己保存的 s4/s5/s6(SB 测试结束后紧接着的 IntLoop3 会重新给它们赋值,
|
||||
# 所以这样换是安全的),字数与地址不变。
|
||||
# 6) **ISR 不能破坏被打断代码的 ra**:三个 ISR 都保存 s1/a7/a0/s0/s3/s4/s5/s6,
|
||||
# 但都没有保存 ra,而它们要调用显示例程,"jal ra,DISP_DLY" 会把 ra 写成 ISR
|
||||
# 内部的返回地址。如果中断恰好落在被打断代码依赖 ra 的位置(典型就是主程序
|
||||
# 正停在 DISP_DLY 末尾的 jalr zero,0(ra) 那一条上),中断返回后就会跳进 ISR
|
||||
# 代码里,一路执行到 ISR 结尾的 csrrci zero,0x4,1,把 MIE 永久清零:表现
|
||||
# 就是“跑一阵子之后拨开关再也不响应,只能复位”。窗口只有 1~2 拍,平时很
|
||||
# 难遇到(仿真里把延时改到极小才会碰上)。适配脚本给 ISR 另加一份显示例程
|
||||
# DISP_DLY_ISR(0x458),链接寄存器改用 a0、显示值走 a7(这两个寄存器三个
|
||||
# ISR 都保存/恢复),于是 ISR 完全不碰 ra;ISR 的显示点由 "jal ra,DISP_DLY +
|
||||
# nop" 换成 "add a7,a0,zero + jal a0,DISP_DLY_ISR",仍是 2 字换 2 字,
|
||||
# 字数与地址不变。
|
||||
# =============================================================================
|
||||
|
||||
.text
|
||||
addi sp, zero, 2000 #内存堆栈初始化
|
||||
|
||||
#############################################################
|
||||
#走马灯测?,测试addi,andi,slli,srli,srai,or,ori,nor,ecall LED按走马灯方式来回显示数据
|
||||
#############################################################
|
||||
|
||||
.text
|
||||
START:
|
||||
|
||||
addi s0,zero,1
|
||||
slli s3, s0, 31 # s3=0x80000000
|
||||
srai s3, s3, 31 # s3=0xFFFFFFFF
|
||||
add s0,zero,zero # s0=0
|
||||
addi s2,zero,12
|
||||
|
||||
addi s6,zero,8 #走马灯计数
|
||||
zmd_loop:
|
||||
|
||||
addi s0, s0, 1 #计算下一个走马灯的数
|
||||
andi s0, s0, 15
|
||||
|
||||
#######################################
|
||||
addi t0,zero,8
|
||||
addi t1,zero,1
|
||||
left:
|
||||
|
||||
slli s3, s3, 4 #走马灯左移
|
||||
or s3, s3, s0
|
||||
|
||||
add a0,zero,s3 # display s3
|
||||
jal ra, DISP_DLY # 显示 a0(写 LED 寄存器 0x1000)并延时约 10 ms
|
||||
nop # 占位:保持每个显示点仍是 2 个字,指令地址不变
|
||||
|
||||
sub t0,t0,t1
|
||||
bne t0,zero,left
|
||||
#######################################
|
||||
|
||||
addi s0, s0, 1 #计算下一个走马灯的数
|
||||
addi t6,zero,15
|
||||
and s0, s0, t6
|
||||
slli s0, s0, 28
|
||||
|
||||
addi t0,zero,8
|
||||
addi t1,zero,1
|
||||
|
||||
zmd_right:
|
||||
|
||||
srli s3, s3, 4 #走马灯右移
|
||||
or s3, s3, s0
|
||||
|
||||
add a0,zero,s3 # display s3
|
||||
jal ra, DISP_DLY # 显示 a0(写 LED 寄存器 0x1000)并延时约 10 ms
|
||||
nop # 占位:保持每个显示点仍是 2 个字,指令地址不变
|
||||
|
||||
sub t0,t0,t1
|
||||
bne t0,zero,zmd_right
|
||||
srli s0, s0, 28
|
||||
#######################################
|
||||
|
||||
sub s6,s6,t1
|
||||
beq s6,zero, exit
|
||||
j zmd_loop
|
||||
|
||||
exit:
|
||||
|
||||
add t0,zero,zero
|
||||
xori t0,t0,-1 #test nor ori
|
||||
slli t0,t0,16
|
||||
ori t0,t0,-1
|
||||
|
||||
add a0,zero,t0 # display t0
|
||||
jal ra, DISP_DLY # 显示 a0(写 LED 寄存器 0x1000)并延时约 10 ms
|
||||
nop # 占位:保持每个显示点仍是 2 个字,指令地址不变
|
||||
|
||||
j START # loop forever
|
||||
|
||||
|
||||
#InteruptProgram1:
|
||||
#############################################################################################
|
||||
# exceptoin 1
|
||||
# 使用?s6? s5?s4?s3?s0?a0?a7
|
||||
#############################################################################################
|
||||
|
||||
################# 保护现场
|
||||
InteruptProgram1:
|
||||
sw t0, 0(sp) #保护t0现场
|
||||
addi sp, sp, 4
|
||||
sw t3, 0(sp) #保护t3现场
|
||||
addi sp, sp, 4
|
||||
sw s1, 0(sp) #保护s1现场
|
||||
addi sp, sp, 4
|
||||
sw a7, 0(sp)
|
||||
addi sp, sp, 4
|
||||
sw a0, 0(sp)
|
||||
addi sp, sp, 4
|
||||
sw s0, 0(sp)
|
||||
addi sp, sp, 4
|
||||
sw s3, 0(sp)
|
||||
addi sp, sp, 4
|
||||
sw s4, 0(sp)
|
||||
addi sp, sp, 4
|
||||
sw s5, 0(sp)
|
||||
addi sp, sp, 4
|
||||
sw s6, 0(sp)
|
||||
addi sp, sp, 4
|
||||
|
||||
csrrsi s6,0x41,0 #s6=uepc
|
||||
sw s6, 0(sp) #uepc压栈保护
|
||||
addi sp, sp, 4
|
||||
|
||||
|
||||
################# 开中断
|
||||
csrrsi zero,0x4,1 #ustatus.MIE=1
|
||||
|
||||
################# DIVU测试
|
||||
addi t0,zero,2 # /2
|
||||
addi s1,zero, 0x11
|
||||
slli s1,s1,8
|
||||
addi s1,s1,0x11
|
||||
slli s1,s1,16
|
||||
|
||||
add a0,zero,s1
|
||||
jal ra, DISP_DLY # 显示 a0(写 LED 寄存器 0x1000)并延时约 10 ms
|
||||
nop # 占位:保持每个显示点仍是 2 个字,指令地址不变
|
||||
addi t3,zero,28 #ѭ������
|
||||
|
||||
divu_branch:
|
||||
divu s1,s1,t0 #����ָ��
|
||||
add a0,zero,s1
|
||||
jal ra, DISP_DLY # 显示 a0(写 LED 寄存器 0x1000)并延时约 10 ms
|
||||
nop # 占位:保持每个显示点仍是 2 个字,指令地址不变
|
||||
addi t3,t3, -1
|
||||
bne t3,zero,divu_branch #ѭ��
|
||||
|
||||
|
||||
nop # 原为 addi a7,zero,10 + ecall:这颗 CPU 会停机,改成空操作
|
||||
|
||||
################# 中断服务
|
||||
addi s6,zero,1 #中断?1,2,3 不同中断号显示值不一样
|
||||
addi s4,zero,3 #循环次数初始值
|
||||
addi s5,zero,1 #计数器累加值
|
||||
|
||||
IntLoop1:
|
||||
add s0,zero,s6
|
||||
|
||||
IntLeftShift1:
|
||||
|
||||
slli s0, s0, 4
|
||||
or s3,s0,s4
|
||||
add a0,zero,s3 #display s0
|
||||
jal ra, DISP_DLY # 显示 a0(写 LED 寄存器 0x1000)并延时约 10 ms
|
||||
nop # 占位:保持每个显示点仍是 2 个字,指令地址不变
|
||||
|
||||
bne s0, zero, IntLeftShift1
|
||||
sub s4,s4,s5 #循环次数递减
|
||||
bne s4, zero, IntLoop1
|
||||
|
||||
################# 关中断
|
||||
csrrci zero,0x4,1 #ustatus.MIE=0
|
||||
|
||||
################# 恢复现场
|
||||
addi sp, sp, -4
|
||||
lw s6, 0(sp)
|
||||
|
||||
csrrw zero,0x41,s6 #uepc=s6
|
||||
|
||||
addi sp, sp, -4
|
||||
lw s6, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw s5, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw s4, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw s3, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw s0, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw a0, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw a7, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw s1, 0(sp) #恢复s1现场
|
||||
addi sp, sp, -4
|
||||
lw t3, 0(sp) #恢复t3现场
|
||||
addi sp, sp, -4
|
||||
lw t0, 0(sp) #恢复t0现场
|
||||
|
||||
################ 中断返回
|
||||
uret #同步开中断,uepc-->pc
|
||||
|
||||
|
||||
#InteruptProgram2:
|
||||
#############################################################################################
|
||||
# exceptoin 2
|
||||
# 使用?s6? s5?s4?s3?s0?a0?a7
|
||||
#############################################################################################
|
||||
|
||||
################# 保护现场
|
||||
InteruptProgram2:
|
||||
sw t1, 0(sp) #保护t1现场
|
||||
addi sp, sp, 4
|
||||
sw t3, 0(sp) #保护t3现场
|
||||
addi sp, sp, 4
|
||||
sw s1, 0(sp) #保护s1现场
|
||||
addi sp, sp, 4
|
||||
sw s2, 0(sp) #保护s2现场
|
||||
addi sp, sp, 4
|
||||
sw a7, 0(sp)
|
||||
addi sp, sp, 4
|
||||
sw a0, 0(sp)
|
||||
addi sp, sp, 4
|
||||
sw s0, 0(sp)
|
||||
addi sp, sp, 4
|
||||
sw s3, 0(sp)
|
||||
addi sp, sp, 4
|
||||
sw s4, 0(sp)
|
||||
addi sp, sp, 4
|
||||
sw s5, 0(sp)
|
||||
addi sp, sp, 4
|
||||
sw s6, 0(sp)
|
||||
addi sp, sp, 4
|
||||
|
||||
csrrsi s6,0x41,0 #s6=uepc
|
||||
sw s6, 0(sp) #uepc压栈保护
|
||||
addi sp, sp, 4
|
||||
|
||||
|
||||
################# 开中断
|
||||
csrrsi zero,0x4,1 #ustatus.MIE=1
|
||||
|
||||
################# BLT测试
|
||||
addi s1,zero,-15 #初始值
|
||||
blt_branch:
|
||||
add a0,zero,s1
|
||||
jal ra, DISP_DLY # 显示 a0(写 LED 寄存器 0x1000)并延时约 10 ms
|
||||
nop # 占位:保持每个显示点仍是 2 个字,指令地址不变
|
||||
addi s1,s1,1
|
||||
blt s1,zero,blt_branch #当前指令
|
||||
|
||||
|
||||
nop # 原为 addi a7,zero,10 + ecall:这颗 CPU 会停机,改成空操作
|
||||
|
||||
################# 中断服务
|
||||
|
||||
addi s6,zero,2 #中断?1,2,3 不同中断号显示值不一样
|
||||
|
||||
addi s4,zero,3 #循环次数初始值
|
||||
addi s5,zero,1 #计数器累加值
|
||||
|
||||
IntLoop2:
|
||||
add s0,zero,s6
|
||||
|
||||
IntLeftShift2:
|
||||
|
||||
|
||||
slli s0, s0, 4
|
||||
or s3,s0,s4
|
||||
add a0,zero,s3 #display s0
|
||||
jal ra, DISP_DLY # 显示 a0(写 LED 寄存器 0x1000)并延时约 10 ms
|
||||
nop # 占位:保持每个显示点仍是 2 个字,指令地址不变
|
||||
|
||||
bne s0, zero, IntLeftShift2
|
||||
sub s4,s4,s5 #循环次数递减
|
||||
bne s4, zero, IntLoop2
|
||||
|
||||
################# 关中断
|
||||
csrrci zero,0x4,1 #ustatus.MIE=0
|
||||
|
||||
################# 恢复现场
|
||||
|
||||
|
||||
addi sp, sp, -4
|
||||
lw s6, 0(sp)
|
||||
csrrw zero,0x41,s6 #uepc=s6
|
||||
|
||||
addi sp, sp, -4
|
||||
lw s6, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw s5, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw s4, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw s3, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw s0, 0(sp) #恢复s0现场
|
||||
addi sp, sp, -4
|
||||
lw a0, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw a7, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw s2, 0(sp) #恢复s2现场
|
||||
addi sp, sp, -4
|
||||
lw s1, 0(sp) #恢复s1现场
|
||||
addi sp, sp, -4
|
||||
lw t3, 0(sp) #恢复t3现场
|
||||
addi sp, sp, -4
|
||||
lw t1, 0(sp) #恢复t1现场
|
||||
|
||||
################ 中断返回
|
||||
uret #同步开中断,uepc-->pc
|
||||
|
||||
|
||||
#InteruptProgram3:
|
||||
#############################################################################################
|
||||
# exceptoin 3
|
||||
# 使用?s6? s5?s4?s3?s0?a0?a7
|
||||
#############################################################################################
|
||||
|
||||
|
||||
################# 保护现场
|
||||
InteruptProgram3:
|
||||
sw s1, 0(sp) #保护s1现场
|
||||
addi sp, sp, 4
|
||||
sw a7, 0(sp)
|
||||
addi sp, sp, 4
|
||||
sw a0, 0(sp)
|
||||
addi sp, sp, 4
|
||||
sw s0, 0(sp)
|
||||
addi sp, sp, 4
|
||||
sw s3, 0(sp)
|
||||
addi sp, sp, 4
|
||||
sw s4, 0(sp)
|
||||
addi sp, sp, 4
|
||||
sw s5, 0(sp)
|
||||
addi sp, sp, 4
|
||||
sw s6, 0(sp)
|
||||
addi sp, sp, 4
|
||||
|
||||
csrrsi s6,0x41,0 #s6=uepc
|
||||
sw s6, 0(sp) #uepc压栈保护
|
||||
addi sp, sp, 4
|
||||
|
||||
|
||||
################# 开中断
|
||||
csrrsi zero,0x4,1 #ustatus.MIE=1
|
||||
|
||||
################# SB测试
|
||||
# 注:ROM 镜像里下面这段的 t1/t3/s2 已改成 ISR3 自己保存的 s4/s5/s6
|
||||
# (t1 当地址 -> s4,t3 当计数 -> s5,s2 当增量 -> s6),避免抢占 ISR1
|
||||
# 时破坏 ISR1 的 DIVU 循环计数,详见文件头部第 5 项。
|
||||
addi t1,zero,0 #init_addr
|
||||
addi t3,zero,32 #counter
|
||||
|
||||
#sb写入 01,02,03,04
|
||||
addi s1,zero, 0x00 #
|
||||
addi s2,zero, 0x01 #
|
||||
|
||||
sb_store:
|
||||
sb s1,(t1)
|
||||
add a0,zero,s1
|
||||
jal ra, DISP_DLY # 显示 a0(写 LED 寄存器 0x1000)并延时约 10 ms
|
||||
nop # 占位:保持每个显示点仍是 2 个字,指令地址不变
|
||||
|
||||
add s1,s1,s2 #data +1
|
||||
addi t1,t1,1 # addr ++
|
||||
addi t3,t3,-1 #counter
|
||||
bne t3,zero,sb_store
|
||||
|
||||
addi t3,zero,8
|
||||
addi t1,zero,0 # addr
|
||||
sb_branch:
|
||||
lw s1,(t1) #读出数据
|
||||
add a0,zero,s1
|
||||
jal ra, DISP_DLY # 显示 a0(写 LED 寄存器 0x1000)并延时约 10 ms
|
||||
nop # 占位:保持每个显示点仍是 2 个字,指令地址不变
|
||||
addi t1,t1,4
|
||||
addi t3,t3, -1
|
||||
bne t3,zero,sb_branch
|
||||
|
||||
nop # 原为 addi a7,zero,10 + ecall:这颗 CPU 会停机,改成空操作
|
||||
|
||||
|
||||
################# 中断服务
|
||||
addi s6,zero,3 #中断?1,2,3 不同中断号显示值不一样
|
||||
addi s4,zero,3 #循环次数初始值
|
||||
addi s5,zero,1 #计数器累加值
|
||||
|
||||
IntLoop3:
|
||||
add s0,zero,s6
|
||||
|
||||
IntLeftShift3:
|
||||
|
||||
slli s0, s0, 4
|
||||
or s3,s0,s4
|
||||
add a0,zero,s3 #display s0
|
||||
jal ra, DISP_DLY # 显示 a0(写 LED 寄存器 0x1000)并延时约 10 ms
|
||||
nop # 占位:保持每个显示点仍是 2 个字,指令地址不变
|
||||
|
||||
bne s0, zero, IntLeftShift3
|
||||
sub s4,s4,s5 #循环次数递减
|
||||
bne s4, zero, IntLoop3
|
||||
|
||||
################# 关中断
|
||||
csrrci zero,0x4,1 #ustatus.MIE=0
|
||||
|
||||
|
||||
|
||||
################# 恢复现场
|
||||
|
||||
addi sp, sp, -4
|
||||
lw s6, 0(sp)
|
||||
csrrw zero,0x41,s6 #uepc=s6
|
||||
|
||||
addi sp, sp, -4
|
||||
lw s6, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw s5, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw s4, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw s3, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw s0, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw a0, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw a7, 0(sp)
|
||||
addi sp, sp, -4
|
||||
lw s1, 0(sp) #恢复s1现场
|
||||
|
||||
################ 中断返回
|
||||
uret #同步开中断,uepc-->pc
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# DISP_DLY —— 显示 + 延时(对应 ROM 末尾 0x438 起的 8 条指令;11 个显示点都用
|
||||
# "jal ra, DISP_DLY" 调用它)
|
||||
#
|
||||
# sw ra, 256(sp) # 保存返回地址(故意存在 sp+256,见下面的说明)
|
||||
# lui a7, 1 # a7 = 0x1000 = LED_ADDR
|
||||
# sw a0, 0(a7) # 把 a0 写进 LED 寄存器(数码管显示它的 8 位十六进制)
|
||||
# lui t2, 0xFFFC0 # 计数初值为负(-262144),向上数到 0 ≈ 10 ms @50 MHz
|
||||
# 1:addi t2, t2, 1
|
||||
# blt t2, zero, 1b
|
||||
# lw ra, 256(sp)
|
||||
# jalr zero, ra, 0
|
||||
#
|
||||
# 为什么返回地址存在 sp+256,而不是按常规压在 sp-8 的栈帧里:
|
||||
# 本程序的 ISR 用的是"先把 sp 加上去、再往里存"的向上增长栈,一层最多 48 字节,
|
||||
# 最多嵌套 4 层(IRQ_STACK_DEPTH=4)共 192 字节 —— 也就是说 [sp, sp+192) 是中断
|
||||
# 随时可能写到的区域。而显示调用既可能被中断打断,也可能从 ISR 内部发出,所以:
|
||||
# * 若压在 sp-8(常规做法),从 ISR 里调用时会正好盖掉 ISR 存好的 s6 / uepc;
|
||||
# uepc 一坏,uret 就返回到 ISR 内部地址,最后执行到 ISR 尾部的 csrrci 把 MIE
|
||||
# 永久清零,之后再也不响应中断(实测就是这个现象);
|
||||
# * 放在 sp+256 越过了中断可能压栈的 192 字节区域,嵌套中断打断延时也不会破坏
|
||||
# 它;每次调用各用自己的 sp+256,嵌套调用互不影响。
|
||||
#
|
||||
# 延时计数用"负数向上数到 0"而不是"递减到 0":万一 t2 被嵌套调用改掉,循环只会
|
||||
# 在 t2 >= 0 时立即退出,不会像递减写法那样回绕成 40 亿次而死等。
|
||||
# -----------------------------------------------------------------------------
|
||||
DISP_DLY:
|
||||
sw ra, 256(sp)
|
||||
lui a7, 1
|
||||
sw a0, 0(a7)
|
||||
lui t2, 0xFFFC0
|
||||
DISP_DLY_WAIT:
|
||||
addi t2, t2, 1
|
||||
blt t2, zero, DISP_DLY_WAIT
|
||||
lw ra, 256(sp)
|
||||
jalr zero, ra, 0
|
||||
Reference in New Issue
Block a user