Files
risc-v-cpu/pipelined-cpu/srcs/rtl/cpu21_seg7.v
Anccend f7844b7b1c 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。
2026-09-19 15:44:47 +08:00

108 lines
3.9 KiB
Verilog
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
`timescale 1ns / 1ps
`default_nettype none
// -----------------------------------------------------------------------------
// cpu21_seg7 —— Nexys4DDR 板载 8 位数码管的动态扫描驱动。
//
// 输入:seg_data_i 每 4 位一个十六进制数字(低 4 位 = 最右边那个数码管),
// an_en_i 每一位对应一个数码管的使能(1 = 显示,0 = 熄灭)。
// 输出:an_o[7:0] 位选(低有效)、seg_o[6:0] 段选 a..g(低有效)、dp_o 小数点。
//
// 扫描频率:REFRESH_DIV 分频后约 1 kHz 切换一位(8 位 => 每位数码管 125 Hz
// 刷新)。REFRESH_DIV 由顶层按 CPU 时钟频率给出(默认 50000 @50 MHz),
// 这样 CPU 降频时扫描刷新率不会跟着变慢。
// 板载数码管是共阳极:位选拉低选中,段选拉低点亮。
// -----------------------------------------------------------------------------
module cpu21_seg7 #(
parameter integer REFRESH_DIV = 50000 // 50 MHz / 50000 = 1 kHz 切换一位
) (
input wire clk,
input wire reset,
input wire [31:0] seg_data_i, // 8 个十六进制数字
input wire [ 7:0] an_en_i, // 每位是否显示
output reg [ 7:0] an_o, // 位选,低有效
output reg [ 6:0] seg_o, // 段选 a..g,低有效
output reg dp_o // 小数点,低有效
);
// ---------------------------------------------------------------------------
// 刷新计数与当前位
// ---------------------------------------------------------------------------
// 计数位宽要能装下 REFRESH_DIV-1:顶层按 CPU 频率等比给出该值
//(50 MHz 时 50000,12.5 MHz 时 12500;若不分频直接跑 100 MHz 则 100000),
// 因此这里用 20 位(最大约 100 万)留足余量。
reg [19:0] div_q;
reg [ 2:0] digit_q;
wire tick = (div_q == (REFRESH_DIV - 1));
always @(posedge clk or posedge reset) begin
if (reset) begin
div_q <= 20'd0;
digit_q <= 3'd0;
end else if (tick) begin
div_q <= 20'd0;
digit_q <= digit_q + 3'd1;
end else begin
div_q <= div_q + 16'd1;
end
end
// 当前要显示的十六进制数字
reg [3:0] nib;
always @* begin
case (digit_q)
3'd0: nib = seg_data_i[3:0];
3'd1: nib = seg_data_i[7:4];
3'd2: nib = seg_data_i[11:8];
3'd3: nib = seg_data_i[15:12];
3'd4: nib = seg_data_i[19:16];
3'd5: nib = seg_data_i[23:20];
3'd6: nib = seg_data_i[27:24];
default: nib = seg_data_i[31:28];
endcase
end
// ---------------------------------------------------------------------------
// 十六进制 -> 七段码(bit0=a ... bit6=g,1 表示该段点亮)
// ---------------------------------------------------------------------------
reg [6:0] seg_on;
always @* begin
case (nib)
4'h0: seg_on = 7'b0111111;
4'h1: seg_on = 7'b0000110;
4'h2: seg_on = 7'b1011011;
4'h3: seg_on = 7'b1001111;
4'h4: seg_on = 7'b1100110;
4'h5: seg_on = 7'b1101101;
4'h6: seg_on = 7'b1111101;
4'h7: seg_on = 7'b0000111;
4'h8: seg_on = 7'b1111111;
4'h9: seg_on = 7'b1101111;
4'hA: seg_on = 7'b1110111;
4'hB: seg_on = 7'b1111100;
4'hC: seg_on = 7'b0111001;
4'hD: seg_on = 7'b1011110;
4'hE: seg_on = 7'b1111001;
default: seg_on = 7'b1110001; // F
endcase
end
// ---------------------------------------------------------------------------
// 位选与段选输出(共阳极 => 低有效)
// ---------------------------------------------------------------------------
always @(posedge clk or posedge reset) begin
if (reset) begin
an_o <= 8'hFF;
seg_o <= 7'h7F;
dp_o <= 1'b1;
end else if (tick) begin
an_o <= ~(8'b0000_0001 << digit_q) | ~an_en_i;
seg_o <= ~seg_on;
dp_o <= 1'b1; // 不用小数点
end
end
endmodule
`default_nettype wire