当前位置: 首页 > 工具软件 > clint > 使用案例 >

中断模块详解-clint

焦苏燕
2023-12-01

注意:个人学习笔记,后续会进行修改完善,目前还在初步学习阶段。参考gitee上《从零开始写RISC-V处理器》。

中断(中断返回)本质上也是一种跳转,只不过还需要附加一些读写CSR寄存器的操作。

RISC-V中断分为两种类型,一种是同步中断,即ECALL、EBREAK等指令所产生的中断,另一种是异步中断,即GPIO、UART等外设产生的中断。

对于中断模块设计,一种简单的方法就是当检测到中断(中断返回)信号时,先暂停整条流水线,设置跳转地址为中断入口地址,然后读、写必要的CSR寄存器(mstatus、mepc、mcause等),等读写完这些CSR寄存器后取消流水线暂停,这样处理器就可以从中断入口地址开始取指,进入中断服务程序。

输入输出信号列表如下:

序号信号名输入/输出位宽(bits)说明
1clk输入1时钟信号
2rst输入1复位信号
3int_flag_i输入8外设中断信号
4inst_i输入32指令内容
5inst_addr_i输入32指令地址
6hold_flag_i输入1未使用
7data_i输入32未使用
8csr_mtvec输入32mtvec寄存器内容
9csr_mepc输入32mepc寄存器内容
10csr_mstatus输入32mstatus寄存器内容
11global_int_en_i输入1全局外设中断使能
12hold_flag_o输出1流水线暂停标志
13we_o输出1写使能
14waddr_o输出32写地址
15raddr_o输出32读地址
16data_o输出32写数据
17int_addr_o输出32中断入口地址
18int_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
...

 类似资料: