From 3227fb1ffd40522690676e13f25f9b259cb8f32f Mon Sep 17 00:00:00 2001 From: liangkangnan Date: Thu, 22 Jul 2021 09:36:04 +0800 Subject: [PATCH] rtl:perips: add rvic Signed-off-by: liangkangnan --- rtl.flist | 1 + rtl/core/defines.sv | 3 + rtl/core/exception.sv | 100 ++++----------- rtl/core/tinyriscv_core.sv | 12 +- rtl/perips/rvic.sv | 240 +++++++++++++++++++++++++++++++++++ rtl/top/tinyriscv_soc_top.sv | 52 ++++++-- 6 files changed, 313 insertions(+), 95 deletions(-) create mode 100644 rtl/perips/rvic.sv diff --git a/rtl.flist b/rtl.flist index 848dcf7..01efb30 100644 --- a/rtl.flist +++ b/rtl.flist @@ -39,6 +39,7 @@ ../rtl/perips/timer.sv ../rtl/perips/uart.sv ../rtl/perips/machine_timer.sv +../rtl/perips/rvic.sv ../rtl/sys_bus/obi_interconnect.sv ../rtl/sys_bus/obi_interconnect_master_sel.sv diff --git a/rtl/core/defines.sv b/rtl/core/defines.sv index 953e2c6..2a69b26 100644 --- a/rtl/core/defines.sv +++ b/rtl/core/defines.sv @@ -43,6 +43,9 @@ // Machine Timer `define MTIMER_ADDR_MASK ~32'hffff `define MTIMER_ADDR_BASE 32'hA0000000 +// Interrupt controller +`define RVIC_ADDR_MASK ~32'hffff +`define RVIC_ADDR_BASE 32'hD0000000 // SIM CTRL `define SIM_CTRL_ADDR_MASK ~32'hffff `define SIM_CTRL_ADDR_BASE 32'hE0000000 diff --git a/rtl/core/exception.sv b/rtl/core/exception.sv index dc68f6f..9be50ca 100644 --- a/rtl/core/exception.sv +++ b/rtl/core/exception.sv @@ -16,18 +16,11 @@ `include "defines.sv" -`define CAUSE_IRQ_EXTERNAL_M {1'b1, 31'd11} -`define CAUSE_IRQ_SOFTWARE_M {1'b1, 31'd3} -`define CAUSE_IRQ_TIMER_M {1'b1, 31'd7} `define CAUSE_EXCEP_ECALL_M {1'b0, 31'd11} `define CAUSE_EXCEP_EBREAK_M {1'b0, 31'd3} `define CAUSE_EXCEP_ILLEGAL_INST_M {1'b0, 31'd2} -`define MIE_MTIE_BIT 7 -`define MIE_MEIE_BIT 11 -`define MIE_MSIE_BIT 3 - `define DCSR_CAUSE_NONE 3'h0 `define DCSR_CAUSE_STEP 3'h4 `define DCSR_CAUSE_DBGREQ 3'h3 @@ -49,7 +42,7 @@ module exception ( input wire inst_dret_i, // dret指令 input wire[31:0] inst_addr_i, // 指令地址 - input wire illegal_inst_i, + input wire illegal_inst_i, // 非法指令 input wire[31:0] mtvec_i, // mtvec寄存器 input wire[31:0] mepc_i, // mepc寄存器 @@ -58,10 +51,8 @@ module exception ( input wire[31:0] dpc_i, // dpc寄存器 input wire[31:0] dcsr_i, // dcsr寄存器 - input wire irq_software_i, - input wire irq_timer_i, - input wire irq_external_i, - input wire[14:0] irq_fast_i, + input wire int_req_i, + input wire[7:0] int_id_i, input wire trigger_match_i, @@ -78,6 +69,7 @@ module exception ( ); + // 异常偏移 localparam ILLEGAL_INSTR_OFFSET = 0; localparam INSTR_ADDR_MISA_OFFSET = 4; localparam ECALL_OFFSET = 8; @@ -86,12 +78,8 @@ module exception ( localparam STORE_MISA_OFFSET = 20; localparam RESERVED1_EXCEPTION_OFFSET = 24; localparam RESERVED2_EXCEPTION_OFFSET = 28; - - localparam EXTERNAL_INT_OFFSET = 32; - localparam SOFTWARE_INT_OFFSET = 36; - localparam TIMER_INT_OFFSET = 40; - localparam FAST_INT_OFFSET = 44; - + // 中断偏移 + localparam INT_OFFSET = 32; localparam S_IDLE = 5'b00001; localparam S_W_MEPC = 5'b00010; @@ -108,61 +96,16 @@ module exception ( reg[31:0] csr_waddr; reg[31:0] csr_wdata; + reg[7:0] int_id_d, int_id_q; + reg in_irq_context_d, in_irq_context_q; wire global_int_en; + wire interrupt_req_valid; assign global_int_en = mstatus_i[3]; - reg[3:0] fast_irq_id; - wire fast_irq_req; - - always @ (*) begin - if (irq_fast_i[ 0]) fast_irq_id = 4'd0; - else if (irq_fast_i[ 1]) fast_irq_id = 4'd1; - else if (irq_fast_i[ 2]) fast_irq_id = 4'd2; - else if (irq_fast_i[ 3]) fast_irq_id = 4'd3; - else if (irq_fast_i[ 4]) fast_irq_id = 4'd4; - else if (irq_fast_i[ 5]) fast_irq_id = 4'd5; - else if (irq_fast_i[ 6]) fast_irq_id = 4'd6; - else if (irq_fast_i[ 7]) fast_irq_id = 4'd7; - else if (irq_fast_i[ 8]) fast_irq_id = 4'd8; - else if (irq_fast_i[ 9]) fast_irq_id = 4'd9; - else if (irq_fast_i[10]) fast_irq_id = 4'd10; - else if (irq_fast_i[11]) fast_irq_id = 4'd11; - else if (irq_fast_i[12]) fast_irq_id = 4'd12; - else if (irq_fast_i[13]) fast_irq_id = 4'd13; - else fast_irq_id = 4'd14; - end - - assign fast_irq_req = |irq_fast_i; - - reg interrupt_req_tmp; - reg[31:0] interrupt_cause; - reg[31:0] interrupt_offset; - wire interrupt_req = inst_valid_i & interrupt_req_tmp; - - always @ (*) begin - if (fast_irq_req) begin - interrupt_req_tmp = 1'b1; - interrupt_cause = {1'b1, {26{1'b0}}, 1'b1, fast_irq_id}; - interrupt_offset = {fast_irq_id, 2'b0} + FAST_INT_OFFSET; - end else if (irq_external_i & mie_i[`MIE_MEIE_BIT]) begin - interrupt_req_tmp = 1'b1; - interrupt_cause = `CAUSE_IRQ_EXTERNAL_M; - interrupt_offset = EXTERNAL_INT_OFFSET; - end else if (irq_software_i & mie_i[`MIE_MSIE_BIT]) begin - interrupt_req_tmp = 1'b1; - interrupt_cause = `CAUSE_IRQ_SOFTWARE_M; - interrupt_offset = SOFTWARE_INT_OFFSET; - end else if (irq_timer_i & mie_i[`MIE_MTIE_BIT]) begin - interrupt_req_tmp = 1'b1; - interrupt_cause = `CAUSE_IRQ_TIMER_M; - interrupt_offset = TIMER_INT_OFFSET; - end else begin - interrupt_req_tmp = 1'b0; - interrupt_cause = 32'h0; - interrupt_offset = 32'h0; - end - end + assign interrupt_req_valid = inst_valid_i & + int_req_i & + ((int_id_i != int_id_q) | (~in_irq_context_q)); reg exception_req; reg[31:0] exception_cause; @@ -188,9 +131,9 @@ module exception ( wire[31:0] int_or_exception_cause; wire[31:0] int_or_exception_offset; - assign int_or_exception_req = (interrupt_req & global_int_en & (~debug_mode_q)) | exception_req; - assign int_or_exception_cause = exception_req ? exception_cause : interrupt_cause; - assign int_or_exception_offset = exception_req ? exception_offset : interrupt_offset; + assign int_or_exception_req = (interrupt_req_valid & global_int_en & (~debug_mode_q)) | exception_req; + assign int_or_exception_cause = exception_req ? exception_cause : int_id_i; + assign int_or_exception_offset = exception_req ? exception_offset : INT_OFFSET; wire trigger_matching; @@ -244,7 +187,7 @@ module exception ( enter_debug_cause_ebreak; assign stall_flag_o = ((state_q != S_IDLE) & (state_q != S_ASSERT)) | - (interrupt_req & global_int_en) | exception_req | + int_or_exception_req | debug_mode_req | inst_mret_i | inst_dret_i; @@ -258,6 +201,8 @@ module exception ( csr_waddr = 32'h0; csr_wdata = 32'h0; trigger_match_d = trigger_match_q; + int_id_d = int_id_q; + in_irq_context_d = in_irq_context_q; case (state_q) S_IDLE: begin @@ -268,6 +213,8 @@ module exception ( assert_addr_d = mtvec_i + int_or_exception_offset; return_addr_d = inst_addr_i; state_d = S_W_MSTATUS; + int_id_d = int_id_i; + in_irq_context_d = 1'b1; end else if (debug_mode_req) begin debug_mode_d = 1'b1; if (enter_debug_cause_debugger_req | @@ -293,9 +240,11 @@ module exception ( state_d = S_W_DCSR; end end else if (inst_mret_i) begin + in_irq_context_d = 1'b0; assert_addr_d = mepc_i; csr_we = 1'b1; csr_waddr = {20'h0, `CSR_MSTATUS}; + // 开全局中断 csr_wdata = {mstatus_i[31:4], 1'b1, mstatus_i[2:0]}; state_d = S_ASSERT; end else if (inst_dret_i) begin @@ -309,6 +258,7 @@ module exception ( S_W_MSTATUS: begin csr_we = 1'b1; csr_waddr = {20'h0, `CSR_MSTATUS}; + // 关全局中断 csr_wdata = {mstatus_i[31:4], 1'b0, mstatus_i[2:0]}; state_d = S_W_MEPC; end @@ -352,6 +302,8 @@ module exception ( return_addr_q <= 32'h0; dcsr_cause_q <= `DCSR_CAUSE_NONE; trigger_match_q <= 1'b0; + int_id_q <= 8'h0; + in_irq_context_q <= 1'b0; end else begin state_q <= state_d; assert_addr_q <= assert_addr_d; @@ -359,6 +311,8 @@ module exception ( return_addr_q <= return_addr_d; dcsr_cause_q <= dcsr_cause_d; trigger_match_q <= trigger_match_d; + int_id_q <= int_id_d; + in_irq_context_q <= in_irq_context_d; end end diff --git a/rtl/core/tinyriscv_core.sv b/rtl/core/tinyriscv_core.sv index a09ed0b..ff7eac3 100644 --- a/rtl/core/tinyriscv_core.sv +++ b/rtl/core/tinyriscv_core.sv @@ -47,10 +47,8 @@ module tinyriscv_core #( input wire data_err_i, // interrupt input - input wire irq_software_i, - input wire irq_timer_i, - input wire irq_external_i, - input wire[14:0] irq_fast_i, + input wire int_req_i, + input wire[7:0] int_id_i, // debug request signal input wire debug_req_i @@ -343,10 +341,8 @@ module tinyriscv_core #( .dpc_i(csr_dpc_o), .dcsr_i(csr_dcsr_o), .trigger_match_i(csr_trigger_match_o), - .irq_software_i(irq_software_i), - .irq_timer_i(irq_timer_i), - .irq_external_i(irq_external_i), - .irq_fast_i(irq_fast_i), + .int_req_i(int_req_i), + .int_id_i(int_id_i), .debug_halt_addr_i(DEBUG_HALT_ADDR), .debug_req_i(debug_req_i), .csr_we_o(excep_csr_we_o), diff --git a/rtl/perips/rvic.sv b/rtl/perips/rvic.sv new file mode 100644 index 0000000..f2fcb26 --- /dev/null +++ b/rtl/perips/rvic.sv @@ -0,0 +1,240 @@ + /* + Copyright 2021 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 "../core/defines.sv" + +// RISC-V中断控制器 +// 支持32个中断源,每个中断源支持256级优先级 +module rvic #( + + )( + + input logic clk_i, + input logic rst_ni, + + input logic [31:0] src_i, + output logic irq_o, // 中断请求信号 + output logic [7:0] irq_id_o, // 中断号 + + input logic [31:0] addr_i, + input logic [31:0] data_i, + input logic [3:0] be_i, + input logic we_i, + output logic [31:0] data_o + + ); + + // 寄存器地址偏移 + parameter logic [7:0] IE_OFFSET = 8'h0; + parameter logic [7:0] IP_OFFSET = 8'h4; + parameter logic [7:0] PRIO0_OFFSET = 8'h8; + parameter logic [7:0] PRIO1_OFFSET = 8'hc; + parameter logic [7:0] PRIO2_OFFSET = 8'h10; + parameter logic [7:0] PRIO3_OFFSET = 8'h14; + parameter logic [7:0] PRIO4_OFFSET = 8'h18; + parameter logic [7:0] PRIO5_OFFSET = 8'h1c; + parameter logic [7:0] PRIO6_OFFSET = 8'h20; + parameter logic [7:0] PRIO7_OFFSET = 8'h24; + parameter logic [7:0] ID_OFFSET = 8'h28; + + logic ie_we; + logic ip_we; + logic [7:0] prio_we; + + logic [31:0] prio_q[8]; + logic [31:0] ie_q; + logic [31:0] ip_q; + logic [31:0] id_q; + + logic [10:0] addr_hit; + logic [7:0] reg_addr = addr_i[7:0]; + + always_comb begin + addr_hit = 11'h0; + addr_hit[ 0] = (reg_addr == IE_OFFSET); + addr_hit[ 1] = (reg_addr == IP_OFFSET); + addr_hit[ 2] = (reg_addr == PRIO0_OFFSET); + addr_hit[ 3] = (reg_addr == PRIO1_OFFSET); + addr_hit[ 4] = (reg_addr == PRIO2_OFFSET); + addr_hit[ 5] = (reg_addr == PRIO3_OFFSET); + addr_hit[ 6] = (reg_addr == PRIO4_OFFSET); + addr_hit[ 7] = (reg_addr == PRIO5_OFFSET); + addr_hit[ 8] = (reg_addr == PRIO6_OFFSET); + addr_hit[ 9] = (reg_addr == PRIO7_OFFSET); + addr_hit[10] = (reg_addr == ID_OFFSET); + end + + assign ie_we = we_i & addr_hit[0]; + assign ip_we = we_i & addr_hit[1]; + for (genvar p = 0; p < 8; p = p + 1) begin + assign prio_we[p] = we_i & addr_hit[p + 2]; + end + + // 写寄存器 + logic [31:0] reg_wdata; + + always_comb begin + reg_wdata = 32'h0; + + // IP寄存器是写1清零的,因此要区别对待 + if (ip_we) begin + reg_wdata = ip_q; + if (be_i[0]) + reg_wdata[7:0] = ip_q[7:0] & (~data_i[7:0]); + if (be_i[1]) + reg_wdata[15:8] = ip_q[15:8] & (~data_i[15:8]); + if (be_i[2]) + reg_wdata[23:16] = ip_q[23:16] & (~data_i[23:16]); + if (be_i[3]) + reg_wdata[31:24] = ip_q[31:24] & (~data_i[31:24]); + end else begin + if (ie_we) begin + reg_wdata = ie_q; + end + for (int j = 0; j < 8; j = j + 1) begin + if (prio_we[j]) begin + reg_wdata = prio_q[j]; + end + end + if (be_i[0]) + reg_wdata[7:0] = data_i[7:0]; + if (be_i[1]) + reg_wdata[15:8] = data_i[15:8]; + if (be_i[2]) + reg_wdata[23:16] = data_i[23:16]; + if (be_i[3]) + reg_wdata[31:24] = data_i[31:24]; + end + end + + gen_en_dff #(32) ie_ff(clk_i, rst_ni, ie_we, reg_wdata, ie_q); + + logic [31:0] ip_wdata; + + assign ip_wdata = ip_we ? reg_wdata : (ip_q | src_i); + gen_en_dff #(32) ip_ff(clk_i, rst_ni, 1'b1, ip_wdata, ip_q); + + for (genvar m = 0; m < 8; m = m + 1) begin + gen_en_dff #(32) prio_ff(clk_i, rst_ni, prio_we[m], reg_wdata, prio_q[m]); + end + + // 读寄存器 + always_ff @ (posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + data_o <= 32'h0; + end else begin + case (addr_i[7:0]) + IE_OFFSET: data_o <= ie_q; + IP_OFFSET: data_o <= ip_q; + PRIO0_OFFSET: data_o <= prio_q[0]; + PRIO1_OFFSET: data_o <= prio_q[1]; + PRIO2_OFFSET: data_o <= prio_q[2]; + PRIO3_OFFSET: data_o <= prio_q[3]; + PRIO4_OFFSET: data_o <= prio_q[4]; + PRIO5_OFFSET: data_o <= prio_q[5]; + PRIO6_OFFSET: data_o <= prio_q[6]; + PRIO7_OFFSET: data_o <= prio_q[7]; + ID_OFFSET: data_o <= id_q; + default: data_o <= 32'h0; + endcase + end + end + + // 找出优先级最高(优先级值最大)的中断源 + // 二分法查找 + + logic [7:0] each_prio[32]; + + for (genvar i = 0; i < 8; i = i + 1) begin + for (genvar j = 0; j < 4; j = j + 1) begin + assign each_prio[i*4+j] = prio_q[i][8*j+7:8*j] & {8{ie_q[i*4+j]}}; + end + end + + typedef struct packed { + logic [7:0] id; + logic [7:0] prio; + } int_info_t; + + int_info_t l1_max[16]; + always_comb begin + for (int i = 0; i < 16; i = i + 1) begin + if (each_prio[2*i+1] > each_prio[2*i]) begin + l1_max[i].id = 2*i+1; + l1_max[i].prio = each_prio[2*i+1]; + end else begin + l1_max[i].id = 2*i; + l1_max[i].prio = each_prio[2*i]; + end + end + end + + int_info_t l2_max[8]; + always_comb begin + for (int i = 0; i < 8; i = i + 1) begin + if (l1_max[2*i+1].prio > l1_max[2*i].prio) begin + l2_max[i].id = l1_max[2*i+1].id; + l2_max[i].prio = l1_max[2*i+1].prio; + end else begin + l2_max[i].id = l1_max[2*i].id; + l2_max[i].prio = l1_max[2*i].prio; + end + end + end + + int_info_t l3_max[4]; + always_comb begin + for (int i = 0; i < 4; i = i + 1) begin + if (l2_max[2*i+1].prio > l2_max[2*i].prio) begin + l3_max[i].id = l2_max[2*i+1].id; + l3_max[i].prio = l2_max[2*i+1].prio; + end else begin + l3_max[i].id = l2_max[2*i].id; + l3_max[i].prio = l2_max[2*i].prio; + end + end + end + + int_info_t l4_max[2]; + always_comb begin + for (int i = 0; i < 2; i = i + 1) begin + if (l3_max[2*i+1].prio > l3_max[2*i].prio) begin + l4_max[i].id = l3_max[2*i+1].id; + l4_max[i].prio = l3_max[2*i+1].prio; + end else begin + l4_max[i].id = l3_max[2*i].id; + l4_max[i].prio = l3_max[2*i].prio; + end + end + end + + logic [7:0] irq_id; + + assign irq_id = (l4_max[1].prio > l4_max[0].prio) ? l4_max[1].id : l4_max[0].id; + + always_ff @ (posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + irq_id_o <= 8'h0; + irq_o <= 1'b0; + end else begin + irq_id_o <= irq_id; + irq_o <= |((src_i | ip_q) & ie_q); + end + end + + assign id_q = {24'h0, irq_id}; + +endmodule diff --git a/rtl/top/tinyriscv_soc_top.sv b/rtl/top/tinyriscv_soc_top.sv index 7698319..a32e39b 100644 --- a/rtl/top/tinyriscv_soc_top.sv +++ b/rtl/top/tinyriscv_soc_top.sv @@ -41,9 +41,9 @@ module tinyriscv_soc_top #( localparam int MASTERS = 3; // Number of master ports `ifdef VERILATOR - localparam int SLAVES = 7; // Number of slave ports + localparam int SLAVES = 8; // Number of slave ports `else - localparam int SLAVES = 6; // Number of slave ports + localparam int SLAVES = 7; // Number of slave ports `endif // masters @@ -58,8 +58,9 @@ module tinyriscv_soc_top #( localparam int Mtimer = 3; localparam int Gpio = 4; localparam int Uart = 5; + localparam int Rvic = 6; `ifdef VERILATOR - localparam int SimCtrl = 6; + localparam int SimCtrl = 7; `endif @@ -85,12 +86,12 @@ module tinyriscv_soc_top #( wire [31:0] slave_addr_base [SLAVES]; `ifdef VERILATOR - wire sim_jtag_tck; - wire sim_jtag_tms; - wire sim_jtag_tdi; - wire sim_jtag_trstn; - wire sim_jtag_tdo; - wire [31:0] sim_jtag_exit; + wire sim_jtag_tck; + wire sim_jtag_tms; + wire sim_jtag_tdi; + wire sim_jtag_trstn; + wire sim_jtag_tdo; + wire [31:0] sim_jtag_exit; `endif wire clk; @@ -100,12 +101,21 @@ module tinyriscv_soc_top #( wire debug_req; wire core_halted; + reg[31:0] irq_src; + wire int_req; + wire[7:0] int_id; + wire mtimer_irq; wire[1:0] io_in; wire[31:0] gpio_ctrl; wire[31:0] gpio_data; + always @ (*) begin + irq_src = 32'h0; + irq_src[0] = mtimer_irq; + end + `ifdef VERILATOR assign halted_ind_pin = core_halted; `else @@ -139,10 +149,8 @@ module tinyriscv_soc_top #( .data_rdata_i (master_rdata[CoreD]), .data_err_i (1'b0), - .irq_software_i (1'b0), - .irq_timer_i (mtimer_irq), - .irq_external_i (1'b0), - .irq_fast_i (15'b0), + .int_req_i (int_req), + .int_id_i (int_id), .debug_req_i (debug_req) ); @@ -229,10 +237,26 @@ module tinyriscv_soc_top #( .rx_pin (uart_rx_pin) ); + assign slave_addr_mask[Rvic] = `RVIC_ADDR_MASK; + assign slave_addr_base[Rvic] = `RVIC_ADDR_BASE; + // 6.中断控制器模块 + rvic u_rvic( + .clk_i (clk), + .rst_ni (ndmreset_n), + .src_i (irq_src), + .irq_o (int_req), + .irq_id_o (int_id), + .addr_i (slave_addr[Rvic]), + .data_i (slave_wdata[Rvic]), + .be_i (slave_be[Rvic]), + .we_i (slave_we[Rvic]), + .data_o (slave_rdata[Rvic]) + ); + `ifdef VERILATOR assign slave_addr_mask[SimCtrl] = `SIM_CTRL_ADDR_MASK; assign slave_addr_base[SimCtrl] = `SIM_CTRL_ADDR_BASE; - // 6.仿真控制模块 + // 7.仿真控制模块 sim_ctrl u_sim_ctrl( .clk_i (clk), .rst_ni (ndmreset_n),