tinyriscv/rtl/core/clint.v

243 lines
8.6 KiB
Verilog
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
Copyright 2020 Blue Liang, liangkangnan@163.com
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
`include "defines.v"
// core local interruptor module
// 核心中断管理、仲裁模块
module clint(
input wire clk,
input wire rst,
// from core
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, // 流水线暂停标志
// to csr_reg
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;
localparam S_CSR_MEPC = 5'b00100;
localparam S_CSR_MSTATUS_MRET = 5'b01000;
localparam S_CSR_MCAUSE = 5'b10000;
reg[3:0] int_state;
reg[4:0] csr_state;
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
// 如果执行阶段的指令为除法指令,则先不处理同步中断,等除法指令执行完再处理
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
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)
S_CSR_IDLE: begin
// 同步中断
if (int_state == S_INT_SYNC_ASSERT) begin
csr_state <= S_CSR_MEPC;
// 在中断处理函数里会将中断返回地址加4
if (jump_flag_i == `JumpEnable) begin
inst_addr <= jump_addr_i - 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;
if (jump_flag_i == `JumpEnable) begin
inst_addr <= jump_addr_i;
// 异步中断可以中断除法指令的执行,中断处理完再重新执行除法指令
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;
end
end
S_CSR_MEPC: begin
csr_state <= S_CSR_MSTATUS;
end
S_CSR_MSTATUS: begin
csr_state <= S_CSR_MCAUSE;
end
S_CSR_MCAUSE: begin
csr_state <= S_CSR_IDLE;
end
S_CSR_MSTATUS_MRET: begin
csr_state <= S_CSR_IDLE;
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};
data_o <= inst_addr;
end
// 写中断产生的原因
S_CSR_MCAUSE: begin
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
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
case (csr_state)
// 发出中断进入信号.写完mcause寄存器才能发
S_CSR_MCAUSE: begin
int_assert_o <= `INT_ASSERT;
int_addr_o <= csr_mtvec;
end
// 发出中断返回信号
S_CSR_MSTATUS_MRET: begin
int_assert_o <= `INT_ASSERT;
int_addr_o <= csr_mepc;
end
default: begin
int_assert_o <= `INT_DEASSERT;
int_addr_o <= `ZeroWord;
end
endcase
end
end
endmodule