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:
@@ -8,121 +8,99 @@
|
||||
// an_en_i 每一位对应一个数码管的使能(1 = 显示,0 = 熄灭)。
|
||||
// 输出:an_o[7:0] 位选(低有效)、seg_o[6:0] 段选 a..g(低有效)、dp_o 小数点。
|
||||
//
|
||||
// 扫描频率:CLK_DIV 分频后约 1 kHz 切换一位(8 位 => 每位数码管 125 Hz 刷新)。
|
||||
// 扫描频率: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 // 小数点,低有效
|
||||
);
|
||||
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 // 小数点,低有效
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 刷新计数与当前位
|
||||
// ---------------------------------------------------------------------------
|
||||
reg [15:0] div_q;
|
||||
reg [2:0] digit_q;
|
||||
wire tick = (div_q == (REFRESH_DIV - 1));
|
||||
// ---------------------------------------------------------------------------
|
||||
// 刷新计数与当前位
|
||||
// ---------------------------------------------------------------------------
|
||||
// 计数位宽要能装下 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 <= 16'd0;
|
||||
digit_q <= 3'd0;
|
||||
end
|
||||
else if (tick) begin
|
||||
div_q <= 16'd0;
|
||||
digit_q <= digit_q + 3'd1;
|
||||
end
|
||||
else begin
|
||||
div_q <= div_q + 16'd1;
|
||||
end
|
||||
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
|
||||
// 当前要显示的十六进制数字
|
||||
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
|
||||
// ---------------------------------------------------------------------------
|
||||
// 十六进制 -> 七段码(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
|
||||
// ---------------------------------------------------------------------------
|
||||
// 位选与段选输出(共阳极 => 低有效)
|
||||
// ---------------------------------------------------------------------------
|
||||
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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user