`include "defines.v"
// core local interruptor module
// 核心中断管理、仲裁模块
module clint(
input wire clk,
input wire rst,
// from if_id
input wire[`INT_BUS] int_flag_i, // 外设的中断信号
// from id
input wire[`InstBus] inst_i, // 当前正在执行的指令
input wire[`InstAddrBus] inst_addr_i, // 当前正在执行的指令的地址
// from ex
input wire jump_flag_i, // 跳转标志
input wire[`InstAddrBus] jump_addr_i, // 将要跳转的地址
input wire div_started_i, // 将要执行除法
// from ctrl
input wire[`Hold_Flag_Bus] hold_flag_i, // 暂停标志
// from csr_reg
input wire[`RegBus] data_i, // CSR寄存器输入数据
input wire[`RegBus] csr_mtvec, // mtvec寄存器值
input wire[`RegBus] csr_mepc, // mepc寄存器值
input wire[`RegBus] csr_mstatus, // mstatus寄存器值
input wire global_int_en_i, // 全局中断使能标志
// to ctrl
output wire hold_flag_o, // 向ctrl模块请求暂停流水线申请
// to csr_reg
// 中断管理模块配置csr寄存器模块
output reg we_o, // 写CSR寄存器标志
output reg[`MemAddrBus] waddr_o, // 写CSR寄存器地址
output reg[`MemAddrBus] raddr_o, // 读CSR寄存器地址
output reg[`RegBus] data_o, // 写CSR寄存器数据
// to ex
output reg[`InstAddrBus] int_addr_o, // 中断入口地址
output reg int_assert_o // 中断标志
);
// 中断状态空间定义
localparam S_INT_IDLE = 4'b0001; // 空闲
localparam S_INT_SYNC_ASSERT = 4'b0010; // 同步中断_生效 :异常原因能够被精确定位至某条指令,如非法指令
localparam S_INT_ASYNC_ASSERT = 4'b0100; // 异步中断_生效 :异常原因不能够被精确定位至某条指令,如外部中断
localparam S_INT_MRET = 4'b1000; // 中断返回
// 写CSR寄存器状态空间定义
localparam S_CSR_IDLE = 5'b00001; // 空闲
localparam S_CSR_MSTATUS = 5'b00010; // 正在写mstatus寄存器(中断开始时写)
localparam S_CSR_MEPC = 5'b00100; // 正在写mepc寄存器
localparam S_CSR_MSTATUS_MRET = 5'b01000; // 正在写mstatus寄存器(中断返回时写)
localparam S_CSR_MCAUSE = 5'b10000; // 正在写mcause寄存器
reg[3:0] int_state; // 中断状态
reg[4:0] csr_state; // 写CSR寄存器状态
reg[`InstAddrBus] inst_addr; // 当前执行的指令地址
reg[31:0] cause; // 中断原因
// 当中断状态或者写寄存器状态不空闲,发出申请暂停流水线信号
assign hold_flag_o = ((int_state != S_INT_IDLE) | (csr_state != S_CSR_IDLE))? `HoldEnable: `HoldDisable;
// 中断仲裁逻辑
// 中断状态转换
always @ (*) begin
if (rst == `RstEnable) begin
int_state = S_INT_IDLE; //复位时,中断空闲
end else begin
//先处理同步中断
if (inst_i == `INST_ECALL || inst_i == `INST_EBREAK) begin // 当前执行的指令为ecall与eback时
// 如果执行阶段的指令为除法指令,则先不处理同步中断,等除法指令执行完再处理
if (div_started_i == `DivStop) begin // 除法指令已完成
int_state = S_INT_SYNC_ASSERT; // 中断状态转换为同步中断
end else begin
int_state = S_INT_IDLE; //否则,除法指令未完成,中断状态空闲
end
// 再处理异步中断
end else if (int_flag_i != `INT_NONE && global_int_en_i == `True) begin // 有外设中断信号且全局中断使能标志为真
int_state = S_INT_ASYNC_ASSERT; // 中断状态转换为异步中断
// 退出中断
end else if (inst_i == `INST_MRET) begin // 当前执行的指令为mret时
int_state = S_INT_MRET; // 中断状态转换为中断返回
// 无中断
end else begin
int_state = S_INT_IDLE; // 无情况,中断空闲
end
end
end
// 写CSR寄存器状态切换
always @ (posedge clk) begin
// 复位时初始化
if (rst == `RstEnable) begin
csr_state <= S_CSR_IDLE; // 复位时,写寄存器状态空闲
cause <= `ZeroWord; // 复位初始化
inst_addr <= `ZeroWord; // 复位初始化
end else begin
case (csr_state) // 判断当前写CSR寄存器状态
S_CSR_IDLE: begin //写寄存器状态空闲时
// 同步中断
if (int_state == S_INT_SYNC_ASSERT) begin // 当同步中断生效时
csr_state <= S_CSR_MEPC; // 写CSR寄存器状态装换为:正在写mepc寄存器
if (jump_flag_i == `JumpEnable) begin // 如果有当前即将要跳转,保存即将要跳转的指令地址
inst_addr <= jump_addr_i - 4'h4; // 在同步中断处理函数里会将中断返回地址加4,因此此处要-4'h4
end else begin
inst_addr <= inst_addr_i; //不需要跳转,保存当前指令地址
end
case (inst_i) // 判断当前指令,确定中断原因
`INST_ECALL: begin
cause <= 32'd11;
end
`INST_EBREAK: begin
cause <= 32'd3;
end
default: begin
cause <= 32'd10;
end
endcase
// 异步中断
end else if (int_state == S_INT_ASYNC_ASSERT) begin // 当异步中断生效时
cause <= 32'h80000004; // 定时器中断(好像只支持定时器中断?)
csr_state <= S_CSR_MEPC; // 写CSR寄存器状态装换为:正在写mepc寄存器
if (jump_flag_i == `JumpEnable) begin // 如果有当前即将要跳转,保存即将要跳转的指令地址
inst_addr <= jump_addr_i; // 在异步中断处理函数里不会将中断返回地址加4,因此此处不需要-4'h4
// 异步中断可以中断除法指令的执行,中断处理完再重新执行除法指令
end else if (div_started_i == `DivStart) begin // 如果正在进行除法操作
inst_addr <= inst_addr_i - 4'h4; // 保存当前指令的前一条指令地址,以便中断服务完成后从新执行这条除法指令
end else begin
inst_addr <= inst_addr_i; // 无其他情况,保存当前执行的指令地址
end
// 中断返回
end else if (int_state == S_INT_MRET) begin
csr_state <= S_CSR_MSTATUS_MRET; // 写CSR寄存器状态装换为:正在写mstatus寄存器(中断返回时写)
end
end
// 状态机流转,写CSR寄存器状态装换为 正在写mepc寄存器 之后
S_CSR_MEPC: begin
csr_state <= S_CSR_MSTATUS; // 写完mepc寄存器后写mstatus寄存器(中断开始时写)
end
S_CSR_MSTATUS: begin
csr_state <= S_CSR_MCAUSE; // 写完mstatus寄存器后写mcause寄存器
end
S_CSR_MCAUSE: begin
csr_state <= S_CSR_IDLE; // 写完mcause寄存器后状态置为空闲
end
S_CSR_MSTATUS_MRET: begin
csr_state <= S_CSR_IDLE; // 中断返回时写完mstatus寄存器后状态置为空闲
end
default: begin
csr_state <= S_CSR_IDLE; // 默认空闲
end
endcase
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寄存器的值设为当前指令地址
S_CSR_MEPC: begin
we_o <= `WriteEnable; // 写使能
waddr_o <= {20'h0, `CSR_MEPC}; // mepc寄存器地址
data_o <= inst_addr; // 写入保存到的地址
end
// 写中断产生的原因
S_CSR_MCAUSE: begin
we_o <= `WriteEnable; // 写使能
waddr_o <= {20'h0, `CSR_MCAUSE}; // mcause寄存器地址
data_o <= cause; // 写入获取到的原因
end
// 关闭全局中断
S_CSR_MSTATUS: begin
we_o <= `WriteEnable; // 写使能
waddr_o <= {20'h0, `CSR_MSTATUS}; // mstatus寄存器地址
data_o <= {csr_mstatus[31:4], 1'b0, csr_mstatus[2:0]}; //寄存器第3位置1'b0,表示关闭全局中断
// RISC-V架构规定:中断发生时,中断全局关闭,所有的中断都将屏蔽
end
// 中断返回
S_CSR_MSTATUS_MRET: begin
we_o <= `WriteEnable; // 写使能
waddr_o <= {20'h0, `CSR_MSTATUS}; // mstatus寄存器地址
// RISC-V架构规定中断发生后,mstatus[7]保存中断发生前mstatus[3]的值
// 相当于退出中断后利用mstatus[7]将mstatus[3]恢复为中断发生前的状态
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
case (csr_state)
// 发出中断进入信号.写完mcause寄存器才能发
S_CSR_MCAUSE: begin
int_assert_o <= `INT_ASSERT;
int_addr_o <= csr_mtvec; // 从mtvec定义的PC地址开始执行,将此地址发送给ex模块
end
// 发出中断返回信号
S_CSR_MSTATUS_MRET: begin
int_assert_o <= `INT_ASSERT;
int_addr_o <= csr_mepc; // 从mepc保存的PC地址开始执行,将此地址发送给ex模块
end
default: begin
int_assert_o <= `INT_DEASSERT; // 默认
int_addr_o <= `ZeroWord; // 默认
end
endcase
end
end
endmodule
详细注释tinyriscv项目中的clint.v模块(中断管理模块)
最新推荐文章于 2024-03-03 17:47:12 发布