diff --git a/Bender.yml b/Bender.yml new file mode 100644 index 00000000..caefc452 --- /dev/null +++ b/Bender.yml @@ -0,0 +1,84 @@ +package: + name: ibex + +dependencies: + tech_cells_generic: { git: "git@github.com:pulp-platform/tech_cells_generic.git", version: 0.2.2 } + +sources: + - target: synthesis + files: + - rtl/ibex_register_file_latch.sv + - target: xilinx + files: + - rtl/ibex_register_file_fpga.sv + - target: not(rtl) + include_dirs: + - rtl + - vendor/lowrisc_ip/ip/prim/rtl + + files: + # Source files grouped in levels. Files in level 0 have no dependencies on files in this + # package. Files in level 1 only depend on files in level 0, files in level 2 on files in + # levels 1 and 0, etc. Files within a level are ordered alphabetically. + # Level 0 + - rtl/ibex_pkg.sv + - vendor/lowrisc_ip/ip/prim/rtl/prim_assert.sv + # Level 1 + - rtl/ibex_alu.sv + - rtl/ibex_compressed_decoder.sv + - rtl/ibex_controller.sv + - rtl/ibex_counter.sv + - rtl/ibex_csr.sv + - rtl/ibex_decoder.sv + - rtl/ibex_fetch_fifo.sv + - rtl/ibex_load_store_unit.sv + - rtl/ibex_multdiv_fast.sv + - rtl/ibex_multdiv_slow.sv + - rtl/ibex_pmp.sv + - rtl/ibex_wb_stage.sv + # Level 2 + - rtl/ibex_cs_registers.sv + - rtl/ibex_ex_block.sv + - rtl/ibex_id_stage.sv + - rtl/ibex_prefetch_buffer.sv + # Level 3 + - rtl/ibex_if_stage.sv + # Level 4 + - rtl/ibex_core.sv + - target: rtl + include_dirs: + - rtl + - vendor/lowrisc_ip/ip/prim/rtl + defines: + RVFI: true + files: + # Level 0 + - rtl/ibex_pkg.sv + - rtl/ibex_register_file_ff.sv + - vendor/lowrisc_ip/ip/prim/rtl/prim_assert.sv + # Level 1 + - rtl/ibex_alu.sv + - rtl/ibex_compressed_decoder.sv + - rtl/ibex_controller.sv + - rtl/ibex_counter.sv + - rtl/ibex_csr.sv + - rtl/ibex_decoder.sv + - rtl/ibex_fetch_fifo.sv + - rtl/ibex_load_store_unit.sv + - rtl/ibex_multdiv_fast.sv + - rtl/ibex_multdiv_slow.sv + - rtl/ibex_pmp.sv + - rtl/ibex_tracer_pkg.sv + - rtl/ibex_wb_stage.sv + # Level 2 + - rtl/ibex_cs_registers.sv + - rtl/ibex_ex_block.sv + - rtl/ibex_id_stage.sv + - rtl/ibex_prefetch_buffer.sv + - rtl/ibex_tracer.sv + # Level 3 + - rtl/ibex_if_stage.sv + # Level 4 + - rtl/ibex_core.sv + # Level 5 + - rtl/ibex_core_tracing.sv diff --git a/rtl/ibex_controller.sv b/rtl/ibex_controller.sv index db98a0ae..77869e98 100644 --- a/rtl/ibex_controller.sv +++ b/rtl/ibex_controller.sv @@ -75,6 +75,10 @@ module ibex_controller #( // mie CSR input logic irq_nm_i, // non-maskeable interrupt output logic nmi_mode_o, // core executing NMI handler + input logic [31:0] irqs_x_i, // x interrupt requests qualified with + // miex CSR + output logic irq_x_ack_o, // to ext. int controller + output logic [4:0] irq_x_ack_id_o, // to ext. int controller // debug signals input logic debug_req_i, @@ -150,6 +154,8 @@ module ibex_controller #( logic [3:0] mfip_id; logic unused_irq_timer; + logic [4:0] irq_x_id; + logic ecall_insn; logic mret_insn; logic dret_insn; @@ -353,6 +359,42 @@ module ibex_controller #( assign unused_irq_timer = irqs_i.irq_timer; + // generate ID of custom interrupts, highest priority to highest ID + always_comb begin : gen_irq_x_id + if (irqs_x_i[31]) irq_x_id = 5'd31; + else if (irqs_x_i[30]) irq_x_id = 5'd30; + else if (irqs_x_i[29]) irq_x_id = 5'd29; + else if (irqs_x_i[28]) irq_x_id = 5'd28; + else if (irqs_x_i[27]) irq_x_id = 5'd27; + else if (irqs_x_i[26]) irq_x_id = 5'd26; + else if (irqs_x_i[25]) irq_x_id = 5'd25; + else if (irqs_x_i[24]) irq_x_id = 5'd24; + else if (irqs_x_i[23]) irq_x_id = 5'd23; + else if (irqs_x_i[22]) irq_x_id = 5'd22; + else if (irqs_x_i[21]) irq_x_id = 5'd21; + else if (irqs_x_i[20]) irq_x_id = 5'd20; + else if (irqs_x_i[19]) irq_x_id = 5'd19; + else if (irqs_x_i[18]) irq_x_id = 5'd18; + else if (irqs_x_i[17]) irq_x_id = 5'd17; + else if (irqs_x_i[16]) irq_x_id = 5'd16; + else if (irqs_x_i[15]) irq_x_id = 5'd15; + else if (irqs_x_i[14]) irq_x_id = 5'd14; + else if (irqs_x_i[13]) irq_x_id = 5'd13; + else if (irqs_x_i[12]) irq_x_id = 5'd12; + else if (irqs_x_i[11]) irq_x_id = 5'd11; + else if (irqs_x_i[10]) irq_x_id = 5'd10; + else if (irqs_x_i[ 9]) irq_x_id = 5'd9; + else if (irqs_x_i[ 8]) irq_x_id = 5'd8; + else if (irqs_x_i[ 7]) irq_x_id = 5'd7; + else if (irqs_x_i[ 6]) irq_x_id = 5'd6; + else if (irqs_x_i[ 5]) irq_x_id = 5'd5; + else if (irqs_x_i[ 4]) irq_x_id = 5'd4; + else if (irqs_x_i[ 3]) irq_x_id = 5'd3; + else if (irqs_x_i[ 2]) irq_x_id = 5'd2; + else if (irqs_x_i[ 1]) irq_x_id = 5'd1; + else irq_x_id = 5'd0; + end + ///////////////////// // Core controller // ///////////////////// @@ -399,6 +441,9 @@ module ibex_controller #( controller_run_o = 1'b0; + irq_x_ack_o = 1'b0; + irq_x_ack_id_o = 5'b0; + unique case (ctrl_fsm_cs) RESET: begin instr_req_o = 1'b0; @@ -569,6 +614,13 @@ module ibex_controller #( if (irq_nm_i && !nmi_mode_q) begin exc_cause_o = EXC_CAUSE_IRQ_NM; nmi_mode_d = 1'b1; // enter NMI mode + end else if (irqs_x_i != 32'b0) begin + // generate exception cause ID from fast interrupt ID: + // - first bit distinguishes interrupts from exceptions, + exc_cause_o = exc_cause_e'({1'b1, irq_x_id}); + exc_pc_mux_o = EXC_PC_IRQ_X; + irq_x_ack_o = 1'b1; + irq_x_ack_id_o = irq_x_id; end else if (irqs_i.irq_fast != 15'b0) begin // generate exception cause ID from fast interrupt ID: // - first bit distinguishes interrupts from exceptions, diff --git a/rtl/ibex_core.sv b/rtl/ibex_core.sv index 0f43faef..2858a66c 100644 --- a/rtl/ibex_core.sv +++ b/rtl/ibex_core.sv @@ -67,6 +67,12 @@ module ibex_core #( input logic irq_external_i, input logic [14:0] irq_fast_i, input logic irq_nm_i, // non-maskeable interrupt + input logic [31:0] irq_x_i, + output logic irq_x_ack_o, + output logic [4:0] irq_x_ack_id_o, + + // External performance counters + input logic [15:0] external_perf_i, // Bind to zero if unused // Debug Interface input logic debug_req_i, @@ -264,6 +270,7 @@ module ibex_core #( logic irq_pending; logic nmi_mode; irqs_t irqs; + logic [31:0] irqs_x; logic csr_mstatus_mie; logic [31:0] csr_mepc, csr_depc; @@ -282,6 +289,7 @@ module ibex_core #( logic csr_save_cause; logic csr_mtvec_init; logic [31:0] csr_mtvec; + logic [31:0] csr_mtvecx; logic [31:0] csr_mtval; logic csr_mstatus_tw; priv_lvl_e priv_mode_id; @@ -386,7 +394,7 @@ module ibex_core #( // main clock gate of the core // generates all clocks except the one for the debug unit which is // independent - prim_clock_gating core_clock_gate_i ( + tc_clk_gating core_clock_gate_i ( .clk_i ( clk_i ), .en_i ( clock_en ), .test_en_i ( test_en_i ), @@ -458,6 +466,7 @@ module ibex_core #( .csr_mepc_i ( csr_mepc ), // exception return address .csr_depc_i ( csr_depc ), // debug return address .csr_mtvec_i ( csr_mtvec ), // trap-vector base address + .csr_mtvecx_i ( csr_mtvecx ), // x trap-vector base address .csr_mtvec_init_o ( csr_mtvec_init ), // pipeline stalls @@ -586,6 +595,9 @@ module ibex_core #( .irqs_i ( irqs ), .irq_nm_i ( irq_nm_i ), .nmi_mode_o ( nmi_mode ), + .irqs_x_i ( irqs_x ), + .irq_x_ack_o ( irq_x_ack_o ), + .irq_x_ack_id_o ( irq_x_ack_id_o ), // Debug Signal .debug_mode_o ( debug_mode ), @@ -991,6 +1003,7 @@ module ibex_core #( // mtvec .csr_mtvec_o ( csr_mtvec ), + .csr_mtvecx_o ( csr_mtvecx ), .csr_mtvec_init_i ( csr_mtvec_init ), .boot_addr_i ( boot_addr_i ), @@ -1007,9 +1020,11 @@ module ibex_core #( .irq_timer_i ( irq_timer_i ), .irq_external_i ( irq_external_i ), .irq_fast_i ( irq_fast_i ), + .irq_x_i ( irq_x_i ), .nmi_mode_i ( nmi_mode ), .irq_pending_o ( irq_pending ), .irqs_o ( irqs ), + .irqs_x_o ( irqs_x ), .csr_mstatus_mie_o ( csr_mstatus_mie ), .csr_mstatus_tw_o ( csr_mstatus_tw ), .csr_mepc_o ( csr_mepc ), @@ -1061,7 +1076,8 @@ module ibex_core #( .mem_store_i ( perf_store ), .dside_wait_i ( perf_dside_wait ), .mul_wait_i ( perf_mul_wait ), - .div_wait_i ( perf_div_wait ) + .div_wait_i ( perf_div_wait ), + .external_perf_i ( external_perf_i ) ); // These assertions are in top-level as instr_valid_id required as the enable term diff --git a/rtl/ibex_core_tracing.sv b/rtl/ibex_core_tracing.sv index a14eb5af..4632cad5 100644 --- a/rtl/ibex_core_tracing.sv +++ b/rtl/ibex_core_tracing.sv @@ -61,6 +61,11 @@ module ibex_core_tracing #( input logic irq_external_i, input logic [14:0] irq_fast_i, input logic irq_nm_i, // non-maskeable interrupt + input logic [31:0] irq_x_i, + output logic irq_x_ack_o, + output logic [4:0] irq_x_ack_id_o, + + input logic [15:0] external_perf_i, // Debug Interface input logic debug_req_i, @@ -155,6 +160,11 @@ module ibex_core_tracing #( .irq_external_i, .irq_fast_i, .irq_nm_i, + .irq_x_i, + .irq_x_ack_o, + .irq_x_ack_id_o, + + .external_perf_i, .debug_req_i, diff --git a/rtl/ibex_cs_registers.sv b/rtl/ibex_cs_registers.sv index c776af27..5c84ef03 100644 --- a/rtl/ibex_cs_registers.sv +++ b/rtl/ibex_cs_registers.sv @@ -43,6 +43,7 @@ module ibex_cs_registers #( // mtvec output logic [31:0] csr_mtvec_o, + output logic [31:0] csr_mtvecx_o, input logic csr_mtvec_init_i, input logic [31:0] boot_addr_i, @@ -59,9 +60,11 @@ module ibex_cs_registers #( input logic irq_timer_i, input logic irq_external_i, input logic [14:0] irq_fast_i, + input logic [31:0] irq_x_i, input logic nmi_mode_i, output logic irq_pending_o, // interrupt request pending output ibex_pkg::irqs_t irqs_o, // interrupt requests qualified with mie + output logic [31:0] irqs_x_o, // custom interrupt requests qualified with miex output logic csr_mstatus_mie_o, output logic [31:0] csr_mepc_o, @@ -115,7 +118,8 @@ module ibex_cs_registers #( input logic mem_store_i, // store to memory in this cycle input logic dside_wait_i, // core waiting for the dside input logic mul_wait_i, // core waiting for multiply - input logic div_wait_i // core waiting for divide + input logic div_wait_i, // core waiting for divide + input logic [15:0] external_perf_i // external performance counters ); import ibex_pkg::*; @@ -209,6 +213,14 @@ module ibex_cs_registers #( logic [31:0] dscratch1_q; logic dscratch0_en, dscratch1_en; + // CLINTx + logic [31:0] miex_q; + logic miex_en; + logic [31:0] mipx; + logic [31:0] mtvecx_q, mtvecx_d; + logic mtvecx_err; + logic mtvecx_en; + // CSRs for recoverable NMIs // NOTE: these CSRS are nonstandard, see https://github.com/riscv/riscv-isa-manual/issues/261 status_stk_t mstack_q, mstack_d; @@ -286,6 +298,9 @@ module ibex_cs_registers #( assign mip.irq_external = irq_external_i; assign mip.irq_fast = irq_fast_i; + // mipx CSR is purely combinational - must be able to re-enable the clock upon WFI + assign mipx = irq_x_i; + // read logic always_comb begin csr_rdata_int = '0; @@ -458,6 +473,11 @@ module ibex_cs_registers #( csr_rdata_int = '0; end + // CLINTx + CSR_MIEX: csr_rdata_int = miex_q; + CSR_MIPX: csr_rdata_int = mipx; + CSR_MTVECX: csr_rdata_int = mtvecx_q; + default: begin illegal_csr = 1'b1; end @@ -503,6 +523,13 @@ module ibex_cs_registers #( cpuctrl_we = 1'b0; + miex_en = 1'b0; + mtvecx_en = csr_mtvec_init_i; + // mtvecx.MODE set to vectored + // mtvecx.BASE must be 256-byte aligned + mtvecx_d = csr_mtvec_init_i ? {boot_addr_i[31:8], 6'b0, 2'b01} : + {csr_wdata_int[31:8], 6'b0, 2'b01}; + if (csr_we_int) begin unique case (csr_addr_i) // mstatus: IE bit @@ -599,6 +626,10 @@ module ibex_cs_registers #( CSR_CPUCTRL: cpuctrl_we = 1'b1; + // CLINTx + CSR_MIEX: miex_en = 1'b1; + CSR_MTVECX: mtvecx_en = 1'b1; + default:; endcase end @@ -730,7 +761,14 @@ module ibex_cs_registers #( // Qualify incoming interrupt requests in mip CSR with mie CSR for controller and to re-enable // clock upon WFI (must be purely combinational). assign irqs_o = mip & mie_q; - assign irq_pending_o = |irqs_o; + assign irq_pending_o = (|irqs_o) | (|irqs_x_o); + + // Qualify incoming interrupt requests in mipx CSR with miex CSR for controller and to re-enable + // clock upon WFI (must be purely combinational). + assign irqs_x_o = mipx & miex_q; + + // directly output mtvecx to IF stage + assign csr_mtvecx_o = mtvecx_q; //////////////////////// // CSR instantiations // @@ -1129,6 +1167,10 @@ module ibex_cs_registers #( mhpmcounter_incr[10] = instr_ret_compressed_i; // num of compressed instr mhpmcounter_incr[11] = mul_wait_i; // cycles waiting for multiply mhpmcounter_incr[12] = div_wait_i; // cycles waiting for divide + + for (int unsigned i=0; i<16; i++) begin : gen_mhpmcounter_incr_external + mhpmcounter_incr[13+i] = external_perf_i[i]; // Start at ID=13 + end end // event selector (hardwired, 0 means no event) @@ -1412,7 +1454,39 @@ module ibex_cs_registers #( .rd_error_o (cpuctrl_err) ); - assign csr_shadow_err_o = mstatus_err | mtvec_err | pmp_csr_err | cpuctrl_err; + assign csr_shadow_err_o = mstatus_err | mtvec_err | mtvecx_err | pmp_csr_err | cpuctrl_err; + + //////////// + // CLINTx // + //////////// + + // MIEX + ibex_csr #( + .Width (32), + .ShadowCopy (1'b0), + .ResetValue ({32{1'b1}}) + ) u_miex_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (csr_wdata_int), + .wr_en_i (miex_en), + .rd_data_o (miex_q), + .rd_error_o () + ); + + // MTVECX + ibex_csr #( + .Width (32), + .ShadowCopy (ShadowCSR), + .ResetValue (32'd1) + ) u_mtvecx_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (mtvecx_d), + .wr_en_i (mtvecx_en), + .rd_data_o (mtvecx_q), + .rd_error_o (mtvecx_err) + ); //////////////// // Assertions // diff --git a/rtl/ibex_id_stage.sv b/rtl/ibex_id_stage.sv index 7c91eae1..162ecdf3 100644 --- a/rtl/ibex_id_stage.sv +++ b/rtl/ibex_id_stage.sv @@ -127,6 +127,9 @@ module ibex_id_stage #( input ibex_pkg::irqs_t irqs_i, input logic irq_nm_i, output logic nmi_mode_o, + input logic [31:0] irqs_x_i, + output logic irq_x_ack_o, + output logic [4:0] irq_x_ack_id_o, input logic lsu_load_err_i, input logic lsu_store_err_i, @@ -582,6 +585,9 @@ module ibex_id_stage #( .irqs_i ( irqs_i ), .irq_nm_i ( irq_nm_i ), .nmi_mode_o ( nmi_mode_o ), + .irqs_x_i ( irqs_x_i ), + .irq_x_ack_o ( irq_x_ack_o ), + .irq_x_ack_id_o ( irq_x_ack_id_o ), // CSR Controller Signals .csr_save_if_o ( csr_save_if_o ), diff --git a/rtl/ibex_if_stage.sv b/rtl/ibex_if_stage.sv index 33911e46..201fb884 100644 --- a/rtl/ibex_if_stage.sv +++ b/rtl/ibex_if_stage.sv @@ -83,6 +83,7 @@ module ibex_if_stage #( input logic [31:0] csr_depc_i, // PC to restore after handling // the debug request input logic [31:0] csr_mtvec_i, // base PC to jump to on exception + input logic [31:0] csr_mtvecx_i, // base PC to jump to on x interrupts output logic csr_mtvec_init_o, // tell CS regfile to init mtvec // pipeline stall @@ -139,9 +140,11 @@ module ibex_if_stage #( logic [7:0] unused_boot_addr; logic [7:0] unused_csr_mtvec; + logic [7:0] unused_csr_mtvecx; assign unused_boot_addr = boot_addr_i[7:0]; assign unused_csr_mtvec = csr_mtvec_i[7:0]; + assign unused_csr_mtvecx = csr_mtvecx_i[7:0]; // extract interrupt ID from exception cause assign irq_id = {exc_cause}; @@ -152,6 +155,7 @@ module ibex_if_stage #( unique case (exc_pc_mux_i) EXC_PC_EXC: exc_pc = { csr_mtvec_i[31:8], 8'h00 }; EXC_PC_IRQ: exc_pc = { csr_mtvec_i[31:8], 1'b0, irq_id[4:0], 2'b00 }; + EXC_PC_IRQ_X: exc_pc = { csr_mtvecx_i[31:8],1'b0, irq_id[4:0], 2'b00 }; EXC_PC_DBD: exc_pc = DmHaltAddr; EXC_PC_DBG_EXC: exc_pc = DmExceptionAddr; default: exc_pc = { csr_mtvec_i[31:8], 8'h00 }; diff --git a/rtl/ibex_pkg.sv b/rtl/ibex_pkg.sv index 42ac4863..8aed775a 100644 --- a/rtl/ibex_pkg.sv +++ b/rtl/ibex_pkg.sv @@ -257,9 +257,10 @@ typedef enum logic [2:0] { } pc_sel_e; // Exception PC mux selection -typedef enum logic [1:0] { +typedef enum logic [2:0] { EXC_PC_EXC, EXC_PC_IRQ, + EXC_PC_IRQ_X, EXC_PC_DBD, EXC_PC_DBG_EXC // Exception while in debug mode } exc_pc_sel_e; @@ -386,6 +387,11 @@ typedef enum logic[11:0] { CSR_DSCRATCH0 = 12'h7b2, // optional CSR_DSCRATCH1 = 12'h7b3, // optional + // CLINTx + CSR_MIEX = 12'h7D0, + CSR_MTVECX = 12'h7D1, + CSR_MIPX = 12'h7D2, + // Machine Counter/Timers CSR_MCOUNTINHIBIT = 12'h320, CSR_MHPMEVENT3 = 12'h323, diff --git a/rtl/ibex_register_file_latch.sv b/rtl/ibex_register_file_latch.sv index 8c5fb35b..3fa38672 100644 --- a/rtl/ibex_register_file_latch.sv +++ b/rtl/ibex_register_file_latch.sv @@ -67,7 +67,7 @@ module ibex_register_file_latch #( // WRITE // /////////// // Global clock gating - prim_clock_gating cg_we_global ( + tc_clk_gating cg_we_global ( .clk_i ( clk_i ), .en_i ( we_a_i ), .test_en_i ( test_en_i ), @@ -99,7 +99,7 @@ module ibex_register_file_latch #( // Individual clock gating (if integrated clock-gating cells are available) for (genvar x = 1; x < NUM_WORDS; x++) begin : gen_cg_word_iter - prim_clock_gating cg_i ( + tc_clk_gating cg_i ( .clk_i ( clk_int ), .en_i ( waddr_onehot_a[x] ), .test_en_i ( test_en_i ), @@ -129,7 +129,7 @@ module ibex_register_file_latch #( assign we_r0_dummy = we_a_i & dummy_instr_id_i; // R0 clock gate - prim_clock_gating cg_i ( + tc_clk_gating cg_i ( .clk_i ( clk_int ), .en_i ( we_r0_dummy ), .test_en_i ( test_en_i ), diff --git a/src_files.yml b/src_files.yml index 3149b3be..ea30ea97 100644 --- a/src_files.yml +++ b/src_files.yml @@ -1,28 +1,30 @@ ibex: incdirs: [ rtl, - shared/rtl, + vendor/lowrisc_ip/ip/prim/rtl, ] files: [ rtl/ibex_pkg.sv, + rtl/ibex_register_file_ff.sv, + vendor/lowrisc_ip/ip/prim/rtl/prim_assert.sv, rtl/ibex_alu.sv, rtl/ibex_compressed_decoder.sv, rtl/ibex_controller.sv, - rtl/ibex_cs_registers.sv, - rtl/ibex_counters.sv, + rtl/ibex_counter.sv, + rtl/ibex_csr.sv, rtl/ibex_decoder.sv, + rtl/ibex_fetch_fifo.sv, + rtl/ibex_load_store_unit.sv, + rtl/ibex_multdiv_fast.sv, + rtl/ibex_multdiv_slow.sv, + rtl/ibex_pmp.sv, + rtl/ibex_wb_stage.sv, + rtl/ibex_cs_registers.sv, rtl/ibex_ex_block.sv, rtl/ibex_id_stage.sv, - rtl/ibex_if_stage.sv, - rtl/ibex_wb_stage.sv, - rtl/ibex_load_store_unit.sv, - rtl/ibex_multdiv_slow.sv, - rtl/ibex_multdiv_fast.sv, rtl/ibex_prefetch_buffer.sv, - rtl/ibex_fetch_fifo.sv, - rtl/ibex_pmp.sv, + rtl/ibex_if_stage.sv, rtl/ibex_core.sv, - shared/rtl/prim_assert.sv, ] ibex_vip_rtl: targets: [