注意:个人学习笔记,后续会进行修改完善,目前还在初步学习阶段。参考gitee上《从零开始写RISC-V处理器》。
中断(中断返回)本质上也是一种跳转,只不过还需要附加一些读写CSR寄存器的操作。
RISC-V中断分为两种类型,一种是同步中断,即ECALL、EBREAK等指令所产生的中断,另一种是异步中断,即GPIO、UART等外设产生的中断。
对于中断模块设计,一种简单的方法就是当检测到中断(中断返回)信号时,先暂停整条流水线,设置跳转地址为中断入口地址,然后读、写必要的CSR寄存器(mstatus、mepc、mcause等),等读写完这些CSR寄存器后取消流水线暂停,这样处理器就可以从中断入口地址开始取指,进入中断服务程序。
输入输出信号列表如下:
序号 | 信号名 | 输入/输出 | 位宽(bits) | 说明 |
---|---|---|---|---|
1 | clk | 输入 | 1 | 时钟信号 |
2 | rst | 输入 | 1 | 复位信号 |
3 | int_flag_i | 输入 | 8 | 外设中断信号 |
4 | inst_i | 输入 | 32 | 指令内容 |
5 | inst_addr_i | 输入 | 32 | 指令地址 |
6 | hold_flag_i | 输入 | 1 | 未使用 |
7 | data_i | 输入 | 32 | 未使用 |
8 | csr_mtvec | 输入 | 32 | mtvec寄存器内容 |
9 | csr_mepc | 输入 | 32 | mepc寄存器内容 |
10 | csr_mstatus | 输入 | 32 | mstatus寄存器内容 |
11 | global_int_en_i | 输入 | 1 | 全局外设中断使能 |
12 | hold_flag_o | 输出 | 1 | 流水线暂停标志 |
13 | we_o | 输出 | 1 | 写使能 |
14 | waddr_o | 输出 | 32 | 写地址 |
15 | raddr_o | 输出 | 32 | 读地址 |
16 | data_o | 输出 | 32 | 写数据 |
17 | int_addr_o | 输出 | 32 | 中断入口地址 |
18 | int_assert_o | 输出 | 1 | 中断有效标志 |
先看中断模块是怎样判断有中断信号产生的,如下代码:
...
always @ (*) begin
if (rst == `RstEnable) begin
int_state = S_INT_IDLE;//复位后的状态,默认没有中断要处理
end else begin
if (inst_i == `INST_ECALL || inst_i == `INST_EBREAK) begin
int_state = S_INT_SYNC_ASSERT;
//判断当前指令是否是ECALL或者EBREAK指令,如果是则设置中断状态为S_INT_SYNC_ASSERT,表示有同步中断要处理
end else if (int_flag_i != `INT_NONE && global_int_en_i == `True) begin
int_state = S_INT_ASYNC_ASSERT;
//判断是否有外设中断信号产生,如果是则设置中断状态为S_INT_ASYNC_ASSERT,表示有异步中断要处理
end else if (inst_i == `INST_MRET) begin
int_state = S_INT_MRET;
//判断当前指令是否是MRET指令,MRET指令是中断返回指令。如果是,则设置中断状态为S_INT_MRET
end else begin
int_state = S_INT_IDLE;
end
end
end
...
...
// 发出中断信号前,先写几个CSR寄存器
always @ (posedge clk) begin
if (rst == `RstEnable) begin
we_o <= `WriteDisable;
waddr_o <= `ZeroWord;
data_o <= `ZeroWord;
//复位
end else begin
case (csr_state)//写mepc寄存器
// 将mepc寄存器的值设为当前指令地址
S_CSR_MEPC: begin
we_o <= `WriteEnable;
waddr_o <= {20'h0, `CSR_MEPC};
data_o <= inst_addr;
end
// 写中断产生的原因
S_CSR_MCAUSE: begin//写mcause寄存器
we_o <= `WriteEnable;
waddr_o <= {20'h0, `CSR_MCAUSE};
data_o <= cause;
end
// 关闭全局异步中断
S_CSR_MSTATUS: begin
we_o <= `WriteEnable;
waddr_o <= {20'h0, `CSR_MSTATUS};
data_o <= {csr_mstatus[31:4], 1'b0, csr_mstatus[2:0]};
end
// 中断返回
S_CSR_MSTATUS_MRET: begin//写mstatus寄存器
we_o <= `WriteEnable;
waddr_o <= {20'h0, `CSR_MSTATUS};
data_o <= {csr_mstatus[31:4], csr_mstatus[7], csr_mstatus[2:0]};
end
default: begin
we_o <= `WriteDisable;
waddr_o <= `ZeroWord;
data_o <= `ZeroWord;
end
endcase
end
end
...
最后就是发出中断信号,中断信号会进入到执行阶段。
...
// 发出中断信号给ex模块
always @ (posedge clk) begin
if (rst == `RstEnable) begin
int_assert_o <= `INT_DEASSERT;
int_addr_o <= `ZeroWord;
end else begin
// 发出中断进入信号.写完mstatus寄存器才能发
//发出中断进入信号,中断入口地址就是mtvec寄存器的值
if (csr_state == S_CSR_MSTATUS) begin
int_assert_o <= `INT_ASSERT;
int_addr_o <= csr_mtvec;
// 发出中断返回信号
end else if (csr_state == S_CSR_MSTATUS_MRET) begin
//发出中断退出信号,中断退出地址就是mepc寄存器的值
int_assert_o <= `INT_ASSERT;
int_addr_o <= csr_mepc;
end else begin
int_assert_o <= `INT_DEASSERT;
int_addr_o <= `ZeroWord;
end
end
end
...