From 09513f8f2ccd7506ff6d51a864b60f76251f688f Mon Sep 17 00:00:00 2001 From: liangkangnan Date: Sat, 25 Apr 2020 17:03:13 +0800 Subject: [PATCH] support preemption Signed-off-by: liangkangnan --- rtl/core/clint.v | 212 +++++++++++++++++++++++++++-------------------- 1 file changed, 121 insertions(+), 91 deletions(-) diff --git a/rtl/core/clint.v b/rtl/core/clint.v index 242d8cb..d93966a 100644 --- a/rtl/core/clint.v +++ b/rtl/core/clint.v @@ -36,6 +36,14 @@ module clint( // 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寄存器标志 @@ -44,130 +52,152 @@ module clint( output reg[`RegBus] data_o, // 写CSR寄存器数据 // to ex - output reg[`InstAddrBus] int_addr_o, // 被中断的指令地址 + output reg[`InstAddrBus] int_addr_o, // 中断入口地址 output reg int_assert_o // 中断标志 ); - // 状态定义 - localparam STATE_IDLE = 4'b0001; - localparam STATE_ASSERT = 4'b0010; - localparam STATE_WAIT_MRET = 4'b0100; - localparam STATE_MRET = 4'b1000; + // 中断状态定义 + 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; - reg[3:0] state; - reg[3:0] next_state; + // 写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; - // 状态更新 - always @ (posedge clk) begin - if (rst == `RstEnable) begin - state <= STATE_IDLE; - end else begin - state <= next_state; - end - end + assign hold_flag_o = ((int_state != S_INT_IDLE) || (csr_state != S_CSR_IDLE))? `HoldEnable: `HoldDisable; - // 状态切换 + + // 中断仲裁逻辑 always @ (*) begin if (rst == `RstEnable) begin - next_state <= STATE_IDLE; + int_state <= S_INT_IDLE; end else begin - case (state) - STATE_IDLE: begin - // 目前只要外设有中断信号发出就立马响应. - // 后续增加中断优先级(嵌套)时需要修改这里的逻辑 - if (int_flag_i != `INT_NONE) begin - next_state <= STATE_ASSERT; - end else begin - next_state <= STATE_IDLE; - end - end - STATE_ASSERT: begin - next_state <= STATE_WAIT_MRET; - end - STATE_WAIT_MRET: begin - if (inst_i == `INST_MRET) begin - next_state <= STATE_MRET; - end else begin - next_state <= STATE_WAIT_MRET; - end - end - STATE_MRET: begin - next_state <= STATE_IDLE; - end - default: begin - next_state <= STATE_IDLE; - end - endcase + if (inst_i == `INST_ECALL) begin + int_state <= S_INT_SYNC_ASSERT; + 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 @ (*) begin - if (rst == `RstEnable) begin - raddr_o <= `ZeroWord; - end else begin - case (state) - STATE_IDLE: begin - raddr_o <= {20'h0, `CSR_MTVEC}; - end - STATE_ASSERT: begin - raddr_o <= {20'h0, `CSR_MTVEC}; - end - STATE_WAIT_MRET: begin - raddr_o <= {20'h0, `CSR_MEPC}; - end - STATE_MRET: begin - raddr_o <= {20'h0, `CSR_MEPC}; - end - default: begin - raddr_o <= {20'h0, `CSR_MTVEC}; - end - endcase - end - end - - // 发出中断信号 - // 中断响应和中断返回时都要发 + // 写CSR寄存器状态切换 always @ (posedge clk) begin if (rst == `RstEnable) begin - int_assert_o <= `INT_DEASSERT; - int_addr_o <= `ZeroWord; + csr_state <= S_CSR_IDLE; + cause <= `ZeroWord; + inst_addr <= `ZeroWord; end else begin - case (state) - STATE_ASSERT: begin - int_assert_o <= `INT_ASSERT; - int_addr_o <= data_i; + case (csr_state) + S_CSR_IDLE: begin + if (int_state == S_INT_SYNC_ASSERT) begin + // ecall异常 + cause <= 32'd11; + csr_state <= S_CSR_MEPC; + inst_addr <= inst_addr_i; + end else if (int_state == S_INT_ASYNC_ASSERT) begin + // 定时器中断 + cause <= 32'h80000004; + csr_state <= S_CSR_MEPC; + inst_addr <= inst_addr_i; + // 中断返回 + end else if (int_state == S_INT_MRET) begin + csr_state <= S_CSR_MSTATUS_MRET; + end end - STATE_MRET: begin - int_assert_o <= `INT_ASSERT; - int_addr_o <= data_i; + S_CSR_MEPC: begin + csr_state <= S_CSR_MCAUSE; + end + S_CSR_MCAUSE: begin + csr_state <= S_CSR_MSTATUS; + end + S_CSR_MSTATUS: begin + csr_state <= S_CSR_IDLE; + end + S_CSR_MSTATUS_MRET: begin + csr_state <= S_CSR_IDLE; end default: begin - int_assert_o <= `INT_DEASSERT; - int_addr_o <= `ZeroWord; + csr_state <= S_CSR_IDLE; end endcase end end - // 根据不同的状态,写对应的CSR寄存器 + // 发出中断信号前,先写几个CSR寄存器 always @ (posedge clk) begin if (rst == `RstEnable) begin we_o <= `WriteDisable; waddr_o <= `ZeroWord; data_o <= `ZeroWord; end else begin - if (state == STATE_ASSERT) begin - we_o <= `WriteEnable; - waddr_o <= {20'h0, `CSR_MEPC}; - data_o <= inst_addr_i; + 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 + // 发出中断进入信号.写完mstatus寄存器才能发 + 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 + int_assert_o <= `INT_ASSERT; + int_addr_o <= csr_mepc; end else begin - we_o <= `WriteEnable; - waddr_o <= {20'h0, `CSR_MCAUSE}; - data_o <= {24'h0, int_flag_i}; + int_assert_o <= `INT_DEASSERT; + int_addr_o <= `ZeroWord; end end end