//******************************************************************************** // SPDX-License-Identifier: Apache-2.0 // Copyright 2020 Western Digital Corporation or its affiliates. // // 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. //******************************************************************************** //******************************************************************************** // Function: Programmable Interrupt Controller // Comments: //******************************************************************************** module el2_pic_ctrl #( `include "el2_param.vh" ) ( input logic clk, // Core clock input logic free_clk, // free clock input logic rst_l, // Reset for all flops input logic clk_override, // Clock over-ride for gating input logic io_clk_override, // PIC IO Clock over-ride for gating input logic [pt.PIC_TOTAL_INT_PLUS1-1:0] extintsrc_req, // Interrupt requests input logic [31:0] picm_rdaddr, // Address of the register input logic [31:0] picm_wraddr, // Address of the register input logic [31:0] picm_wr_data, // Data to be written to the register input logic picm_wren, // Write enable to the register input logic picm_rden, // Read enable for the register input logic picm_mken, // Read the Mask for the register input logic [3:0] meicurpl, // Current Priority Level input logic [3:0] meipt, // Current Priority Threshold output logic mexintpend, // External Inerrupt request to the core output logic [7:0] claimid, // Claim Id of the requested interrupt output logic [3:0] pl, // Priority level of the requested interrupt output logic [31:0] picm_rd_data, // Read data of the register output logic mhwakeup, // Wake-up interrupt request input logic scan_mode // scan mode ); localparam NUM_LEVELS = $clog2(pt.PIC_TOTAL_INT_PLUS1); localparam INTPRIORITY_BASE_ADDR = pt.PIC_BASE_ADDR ; localparam INTPEND_BASE_ADDR = pt.PIC_BASE_ADDR + 32'h00001000 ; localparam INTENABLE_BASE_ADDR = pt.PIC_BASE_ADDR + 32'h00002000 ; localparam EXT_INTR_PIC_CONFIG = pt.PIC_BASE_ADDR + 32'h00003000 ; localparam EXT_INTR_GW_CONFIG = pt.PIC_BASE_ADDR + 32'h00004000 ; localparam EXT_INTR_GW_CLEAR = pt.PIC_BASE_ADDR + 32'h00005000 ; localparam INTPEND_SIZE = (pt.PIC_TOTAL_INT_PLUS1 < 32) ? 32 : (pt.PIC_TOTAL_INT_PLUS1 < 64) ? 64 : (pt.PIC_TOTAL_INT_PLUS1 < 128) ? 128 : (pt.PIC_TOTAL_INT_PLUS1 < 256) ? 256 : (pt.PIC_TOTAL_INT_PLUS1 < 512) ? 512 : 1024 ; localparam INT_GRPS = INTPEND_SIZE / 32 ; localparam INTPRIORITY_BITS = 4 ; localparam ID_BITS = 8 ; localparam int GW_CONFIG[pt.PIC_TOTAL_INT_PLUS1-1:0] = '{default:0} ; localparam INT_ENABLE_GRPS = (pt.PIC_TOTAL_INT_PLUS1 - 1) / 4 ; logic [pt.PIC_TOTAL_INT_PLUS1-1:0] intenable_clk_enable ; logic [INT_ENABLE_GRPS:0] intenable_clk_enable_grp ; logic [INT_ENABLE_GRPS:0] gw_clk ; logic addr_intpend_base_match; logic raddr_config_pic_match ; logic raddr_intenable_base_match; logic raddr_intpriority_base_match; logic raddr_config_gw_base_match ; logic waddr_config_pic_match ; logic waddr_intpriority_base_match; logic waddr_intenable_base_match; logic waddr_config_gw_base_match ; logic addr_clear_gw_base_match ; logic mexintpend_in; logic mhwakeup_in ; logic intpend_reg_read ; logic [31:0] picm_rd_data_in, intpend_rd_out; logic intenable_rd_out ; logic [INTPRIORITY_BITS-1:0] intpriority_rd_out; logic [1:0] gw_config_rd_out; logic [pt.PIC_TOTAL_INT_PLUS1-1:0] [INTPRIORITY_BITS-1:0] intpriority_reg; logic [pt.PIC_TOTAL_INT_PLUS1-1:0] [INTPRIORITY_BITS-1:0] intpriority_reg_inv; logic [pt.PIC_TOTAL_INT_PLUS1-1:0] intpriority_reg_we; logic [pt.PIC_TOTAL_INT_PLUS1-1:0] intpriority_reg_re; logic [pt.PIC_TOTAL_INT_PLUS1-1:0] [1:0] gw_config_reg; logic [pt.PIC_TOTAL_INT_PLUS1-1:0] intenable_reg; logic [pt.PIC_TOTAL_INT_PLUS1-1:0] intenable_reg_we; logic [pt.PIC_TOTAL_INT_PLUS1-1:0] intenable_reg_re; logic [pt.PIC_TOTAL_INT_PLUS1-1:0] gw_config_reg_we; logic [pt.PIC_TOTAL_INT_PLUS1-1:0] gw_config_reg_re; logic [pt.PIC_TOTAL_INT_PLUS1-1:0] gw_clear_reg_we; logic [INTPEND_SIZE-1:0] intpend_reg_extended; logic [pt.PIC_TOTAL_INT_PLUS1-1:0] [INTPRIORITY_BITS-1:0] intpend_w_prior_en; logic [pt.PIC_TOTAL_INT_PLUS1-1:0] [ID_BITS-1:0] intpend_id; logic [INTPRIORITY_BITS-1:0] maxint; logic [INTPRIORITY_BITS-1:0] selected_int_priority; logic [INT_GRPS-1:0] [31:0] intpend_rd_part_out ; logic config_reg; logic intpriord; logic config_reg_we ; logic config_reg_re ; logic config_reg_in ; logic prithresh_reg_write , prithresh_reg_read; logic intpriority_reg_read ; logic intenable_reg_read ; logic gw_config_reg_read ; logic picm_wren_ff , picm_rden_ff ; logic [31:0] picm_raddr_ff; logic [31:0] picm_waddr_ff; logic [31:0] picm_wr_data_ff; logic [3:0] mask; logic picm_mken_ff; logic [ID_BITS-1:0] claimid_in ; logic [INTPRIORITY_BITS-1:0] pl_in ; logic [INTPRIORITY_BITS-1:0] pl_in_q ; logic [pt.PIC_TOTAL_INT_PLUS1-1:0] extintsrc_req_sync; logic [pt.PIC_TOTAL_INT_PLUS1-1:0] extintsrc_req_gw; logic picm_bypass_ff; // clkens logic pic_raddr_c1_clken; logic pic_waddr_c1_clken; logic pic_data_c1_clken; logic pic_pri_c1_clken; logic pic_int_c1_clken; logic gw_config_c1_clken; // clocks logic pic_raddr_c1_clk; logic pic_data_c1_clk; logic pic_pri_c1_clk; logic pic_int_c1_clk; logic gw_config_c1_clk; // ---- Clock gating section ------ // c1 clock enables assign pic_raddr_c1_clken = picm_mken | picm_rden | clk_override; assign pic_data_c1_clken = picm_wren | clk_override; assign pic_pri_c1_clken = (waddr_intpriority_base_match & picm_wren_ff) | (raddr_intpriority_base_match & picm_rden_ff) | clk_override; assign pic_int_c1_clken = (waddr_intenable_base_match & picm_wren_ff) | (raddr_intenable_base_match & picm_rden_ff) | clk_override; assign gw_config_c1_clken = (waddr_config_gw_base_match & picm_wren_ff) | (raddr_config_gw_base_match & picm_rden_ff) | clk_override; // C1 - 1 clock pulse for data rvoclkhdr pic_addr_c1_cgc ( .en(pic_raddr_c1_clken), .l1clk(pic_raddr_c1_clk), .* ); rvoclkhdr pic_data_c1_cgc ( .en(pic_data_c1_clken), .l1clk(pic_data_c1_clk), .* ); rvoclkhdr pic_pri_c1_cgc ( .en(pic_pri_c1_clken), .l1clk(pic_pri_c1_clk), .* ); rvoclkhdr pic_int_c1_cgc ( .en(pic_int_c1_clken), .l1clk(pic_int_c1_clk), .* ); rvoclkhdr gw_config_c1_cgc ( .en(gw_config_c1_clken), .l1clk(gw_config_c1_clk), .* ); // ------ end clock gating section ------------------------ assign raddr_intenable_base_match = (picm_raddr_ff[31:NUM_LEVELS+2] == INTENABLE_BASE_ADDR[31:NUM_LEVELS+2]) ; assign raddr_intpriority_base_match = (picm_raddr_ff[31:NUM_LEVELS+2] == INTPRIORITY_BASE_ADDR[31:NUM_LEVELS+2]) ; assign raddr_config_gw_base_match = (picm_raddr_ff[31:NUM_LEVELS+2] == EXT_INTR_GW_CONFIG[31:NUM_LEVELS+2]) ; assign raddr_config_pic_match = (picm_raddr_ff[31:0] == EXT_INTR_PIC_CONFIG[31:0]) ; assign addr_intpend_base_match = (picm_raddr_ff[31:6] == INTPEND_BASE_ADDR[31:6]) ; assign waddr_config_pic_match = (picm_waddr_ff[31:0] == EXT_INTR_PIC_CONFIG[31:0]) ; assign addr_clear_gw_base_match = (picm_waddr_ff[31:NUM_LEVELS+2] == EXT_INTR_GW_CLEAR[31:NUM_LEVELS+2]) ; assign waddr_intpriority_base_match = (picm_waddr_ff[31:NUM_LEVELS+2] == INTPRIORITY_BASE_ADDR[31:NUM_LEVELS+2]) ; assign waddr_intenable_base_match = (picm_waddr_ff[31:NUM_LEVELS+2] == INTENABLE_BASE_ADDR[31:NUM_LEVELS+2]) ; assign waddr_config_gw_base_match = (picm_waddr_ff[31:NUM_LEVELS+2] == EXT_INTR_GW_CONFIG[31:NUM_LEVELS+2]) ; assign picm_bypass_ff = picm_rden_ff & picm_wren_ff & ( picm_raddr_ff[31:0] == picm_waddr_ff[31:0] ); // pic writes and reads to same address together rvdff #(32) picm_radd_flop (.*, .din (picm_rdaddr), .dout(picm_raddr_ff), .clk(pic_raddr_c1_clk)); rvdff #(32) picm_wadd_flop (.*, .din (picm_wraddr), .dout(picm_waddr_ff), .clk(pic_data_c1_clk)); rvdff #(1) picm_wre_flop (.*, .din (picm_wren), .dout(picm_wren_ff), .clk(free_clk)); rvdff #(1) picm_rde_flop (.*, .din (picm_rden), .dout(picm_rden_ff), .clk(free_clk)); rvdff #(1) picm_mke_flop (.*, .din (picm_mken), .dout(picm_mken_ff), .clk(free_clk)); rvdff #(32) picm_dat_flop (.*, .din (picm_wr_data[31:0]), .dout(picm_wr_data_ff[31:0]), .clk(pic_data_c1_clk)); //rvsyncss #(pt.PIC_TOTAL_INT_PLUS1-1) sync_inst //( // .clk (free_clk), // .dout(extintsrc_req_sync[pt.PIC_TOTAL_INT_PLUS1-1:1]), // .din (extintsrc_req[pt.PIC_TOTAL_INT_PLUS1-1:1]), // .*) ; // //assign extintsrc_req_sync[0] = extintsrc_req[0]; /* genvar p ; for (p=0; p<=INT_ENABLE_GRPS ; p++) begin : IO_CLK_GRP if (p==INT_ENABLE_GRPS) begin : LAST_GRP assign intenable_clk_enable_grp[p] = |intenable_clk_enable[pt.PIC_TOTAL_INT_PLUS1-1 : p*4] | io_clk_override; rvoclkhdr intenable_c1_cgc ( .en(intenable_clk_enable_grp[p]), .l1clk(gw_clk[p]), .* ); end else begin : CLK_GRPS assign intenable_clk_enable_grp[p] = |intenable_clk_enable[p*4+3 : p*4] | io_clk_override; rvoclkhdr intenable_c1_cgc ( .en(intenable_clk_enable_grp[p]), .l1clk(gw_clk[p]), .* ); end end */ genvar i ; genvar p ; for (p=0; p<=INT_ENABLE_GRPS ; p++) begin : IO_CLK_GRP wire grp_clk, grp_clken; assign grp_clken = |intenable_clk_enable[(p==INT_ENABLE_GRPS?pt.PIC_TOTAL_INT_PLUS1-1:p*4+3) : p*4] | io_clk_override; `ifndef RV_FPGA_OPTIMIZE rvclkhdr intenable_c1_cgc( .en(grp_clken), .l1clk(grp_clk), .* ); `else assign gw_clk[p] = 1'b0 ; `endif for(genvar i= (p==0 ? 1: 0); i< (p==INT_ENABLE_GRPS ? pt.PIC_TOTAL_INT_PLUS1-p*4 :4); i++) begin : GW el2_configurable_gw gw_inst( .*, .gw_clk(grp_clk), .rawclk(clk), .clken (grp_clken), .extintsrc_req(extintsrc_req[i+p*4]) , .meigwctrl_polarity(gw_config_reg[i+p*4][0]) , .meigwctrl_type(gw_config_reg[i+p*4][1]) , .meigwclr(gw_clear_reg_we[i+p*4]) , .extintsrc_req_config(extintsrc_req_gw[i+p*4]) ); end end for (i=0; i 0 ) begin : NON_ZERO_INT assign intpriority_reg_we[i] = waddr_intpriority_base_match & (picm_waddr_ff[NUM_LEVELS+1:2] == i) & picm_wren_ff; assign intpriority_reg_re[i] = raddr_intpriority_base_match & (picm_raddr_ff[NUM_LEVELS+1:2] == i) & picm_rden_ff; assign intenable_reg_we[i] = waddr_intenable_base_match & (picm_waddr_ff[NUM_LEVELS+1:2] == i) & picm_wren_ff; assign intenable_reg_re[i] = raddr_intenable_base_match & (picm_raddr_ff[NUM_LEVELS+1:2] == i) & picm_rden_ff; assign gw_config_reg_we[i] = waddr_config_gw_base_match & (picm_waddr_ff[NUM_LEVELS+1:2] == i) & picm_wren_ff; assign gw_config_reg_re[i] = raddr_config_gw_base_match & (picm_raddr_ff[NUM_LEVELS+1:2] == i) & picm_rden_ff; assign gw_clear_reg_we[i] = addr_clear_gw_base_match & (picm_waddr_ff[NUM_LEVELS+1:2] == i) & picm_wren_ff ; rvdffs #(INTPRIORITY_BITS) intpriority_ff (.*, .en( intpriority_reg_we[i]), .din (picm_wr_data_ff[INTPRIORITY_BITS-1:0]), .dout(intpriority_reg[i]), .clk(pic_pri_c1_clk)); rvdffs #(1) intenable_ff (.*, .en( intenable_reg_we[i]), .din (picm_wr_data_ff[0]), .dout(intenable_reg[i]), .clk(pic_int_c1_clk)); rvdffs #(2) gw_config_ff (.*, .en( gw_config_reg_we[i]), .din (picm_wr_data_ff[1:0]), .dout(gw_config_reg[i]), .clk(gw_config_c1_clk)); assign intenable_clk_enable[i] = gw_config_reg[i][1] | intenable_reg_we[i] | intenable_reg[i] | gw_clear_reg_we[i] ; /* rvsyncss_fpga #(1) sync_inst ( .gw_clk (gw_clk[i/4]), .rawclk (clk), .clken (intenable_clk_enable_grp[i/4]), .dout (extintsrc_req_sync[i]), .din (extintsrc_req[i]), .*) ; el2_configurable_gw config_gw_inst(.*, .gw_clk(gw_clk[i/4]), .rawclk(clk), .clken (intenable_clk_enable_grp[i/4]), .extintsrc_req_sync(extintsrc_req_sync[i]) , .meigwctrl_polarity(gw_config_reg[i][0]) , .meigwctrl_type(gw_config_reg[i][1]) , .meigwclr(gw_clear_reg_we[i]) , .extintsrc_req_config(extintsrc_req_gw[i]) ); */ end else begin : INT_ZERO assign intpriority_reg_we[i] = 1'b0 ; assign intpriority_reg_re[i] = 1'b0 ; assign intenable_reg_we[i] = 1'b0 ; assign intenable_reg_re[i] = 1'b0 ; assign gw_config_reg_we[i] = 1'b0 ; assign gw_config_reg_re[i] = 1'b0 ; assign gw_clear_reg_we[i] = 1'b0 ; assign gw_config_reg[i] = '0 ; assign intpriority_reg[i] = {INTPRIORITY_BITS{1'b0}} ; assign intenable_reg[i] = 1'b0 ; assign extintsrc_req_gw[i] = 1'b0 ; assign extintsrc_req_sync[i] = 1'b0 ; assign intenable_clk_enable[i] = 1'b0; end assign intpriority_reg_inv[i] = intpriord ? ~intpriority_reg[i] : intpriority_reg[i] ; assign intpend_w_prior_en[i] = {INTPRIORITY_BITS{(extintsrc_req_gw[i] & intenable_reg[i])}} & intpriority_reg_inv[i] ; assign intpend_id[i] = i ; end assign pl_in[INTPRIORITY_BITS-1:0] = selected_int_priority[INTPRIORITY_BITS-1:0] ; //if (pt.PIC_2CYCLE == 1) begin : genblock //end //else begin : genblock //end genvar l, m , j, k; if (pt.PIC_2CYCLE == 1) begin : genblock logic [NUM_LEVELS/2:0] [pt.PIC_TOTAL_INT_PLUS1+2:0] [INTPRIORITY_BITS-1:0] level_intpend_w_prior_en; logic [NUM_LEVELS/2:0] [pt.PIC_TOTAL_INT_PLUS1+2:0] [ID_BITS-1:0] level_intpend_id; logic [NUM_LEVELS:NUM_LEVELS/2] [(pt.PIC_TOTAL_INT_PLUS1/2**(NUM_LEVELS/2))+1:0] [INTPRIORITY_BITS-1:0] levelx_intpend_w_prior_en; logic [NUM_LEVELS:NUM_LEVELS/2] [(pt.PIC_TOTAL_INT_PLUS1/2**(NUM_LEVELS/2))+1:0] [ID_BITS-1:0] levelx_intpend_id; assign level_intpend_w_prior_en[0][pt.PIC_TOTAL_INT_PLUS1+2:0] = {4'b0,4'b0,4'b0,intpend_w_prior_en[pt.PIC_TOTAL_INT_PLUS1-1:0]} ; assign level_intpend_id[0][pt.PIC_TOTAL_INT_PLUS1+2:0] = {8'b0,8'b0,8'b0,intpend_id[pt.PIC_TOTAL_INT_PLUS1-1:0]} ; logic [(pt.PIC_TOTAL_INT_PLUS1/2**(NUM_LEVELS/2)):0] [INTPRIORITY_BITS-1:0] l2_intpend_w_prior_en_ff; logic [(pt.PIC_TOTAL_INT_PLUS1/2**(NUM_LEVELS/2)):0] [ID_BITS-1:0] l2_intpend_id_ff; assign levelx_intpend_w_prior_en[NUM_LEVELS/2][(pt.PIC_TOTAL_INT_PLUS1/2**(NUM_LEVELS/2))+1:0] = {{1*INTPRIORITY_BITS{1'b0}},l2_intpend_w_prior_en_ff[(pt.PIC_TOTAL_INT_PLUS1/2**(NUM_LEVELS/2)):0]} ; assign levelx_intpend_id[NUM_LEVELS/2][(pt.PIC_TOTAL_INT_PLUS1/2**(NUM_LEVELS/2))+1:0] = {{1*ID_BITS{1'b1}},l2_intpend_id_ff[(pt.PIC_TOTAL_INT_PLUS1/2**(NUM_LEVELS/2)):0]} ; /// Do the prioritization of the interrupts here //////////// for (l=0; l meipt_inv[INTPRIORITY_BITS-1:0]) & ( selected_int_priority[INTPRIORITY_BITS-1:0] > meicurpl_inv[INTPRIORITY_BITS-1:0]) ); rvdff #(1) mexintpend_ff (.*, .clk(free_clk), .din (mexintpend_in), .dout(mexintpend)); assign maxint[INTPRIORITY_BITS-1:0] = intpriord ? 0 : 15 ; assign mhwakeup_in = ( pl_in_q[INTPRIORITY_BITS-1:0] == maxint) ; rvdff #(1) wake_up_ff (.*, .clk(free_clk), .din (mhwakeup_in), .dout(mhwakeup)); ////////////////////////////////////////////////////////////////////////// // Reads of register. // 1- intpending ////////////////////////////////////////////////////////////////////////// assign intpend_reg_read = addr_intpend_base_match & picm_rden_ff ; assign intpriority_reg_read = raddr_intpriority_base_match & picm_rden_ff; assign intenable_reg_read = raddr_intenable_base_match & picm_rden_ff; assign gw_config_reg_read = raddr_config_gw_base_match & picm_rden_ff; assign intpend_reg_extended[INTPEND_SIZE-1:0] = {{INTPEND_SIZE-pt.PIC_TOTAL_INT_PLUS1{1'b0}},extintsrc_req_gw[pt.PIC_TOTAL_INT_PLUS1-1:0]} ; for (i=0; i<(INT_GRPS); i++) begin assign intpend_rd_part_out[i] = (({32{intpend_reg_read & picm_raddr_ff[5:2] == i}}) & intpend_reg_extended[((32*i)+31):(32*i)]) ; end always_comb begin : INTPEND_RD intpend_rd_out = '0 ; for (int i=0; i