/* Copyright (c) 2013, IIT Madras All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of IIT Madras nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */ package plic; import Vector::*; import defined_types::*; import ConfigReg::*; import Semi_FIFOF::*; import AXI4_Lite_Types::*; import BUtils ::*; import ConcatReg ::*; import encoder ::*; `include "instance_defines.bsv" `define INTERRUPT_LEVELS 8 // import ConfigReg::*; /*Platform level interrupt controller: Refer to RISC-V privilege spec-v-1.10 chapter 7 Memory maps of Registers Interrupt enable registers : rg_ie_0 :0C002000 rg_ie_1 :0C002000 rg_ie_2 :0C002000 . . . rg_ie_7 :0C002000 rg_ie_8 :0C002001 rg_ie_9 :0C002001 . . Interrupt priority registers : rg_priority_0 : 0C000000 rg_priority_1 : 0C000002 rg_priority_2 : 0C000004 . . . Priority Threshold register : rg_priority_threshold : 0C200000 Claim register : rg_interrupt_id : 0C200004 */ interface Ifc_PLIC#(numeric type addr_width,numeric type word_size,numeric type no_of_ir_pins, numeric type no_of_ir_levels); interface Vector#(no_of_ir_pins,Ifc_global_interrupt) ifc_external_irq; interface Ifc_program_registers#(addr_width,word_size) ifc_prog_reg; method ActionValue#(Tuple2#(Bool,Bool)) intrpt_note; method ActionValue#(Bit#(TLog#(no_of_ir_pins))) intrpt_completion; endinterface //(*conflict_free = "rl_prioritise, prog_reg"*) module mkplic(Ifc_PLIC#(addr_width,word_size,no_of_ir_pins,no_of_ir_levels)) provisos( Log#(no_of_ir_pins, ir_bits), Log#(no_of_ir_levels, priority_bits), Mul#(8,word_size,data_width), //Mul#(3,no_iterations,ir_bits), Add#(1,ir_bits,x_ir_bits), Add#(msb_ir_bits,1,ir_bits), Add#(msb_ir_pins,1,no_of_ir_pins), Add#(msb_priority_levels,1,no_of_ir_levels), Add#(msb_priority_bits,1,priority_bits), Add#(a__, no_of_ir_levels, data_width), Add#(b__, ir_bits, data_width), Add#(c__, no_of_ir_pins, 1024), Add#(d__, ir_bits, 10), Add#(f__, no_of_ir_levels, 1024), Add#(g__, priority_bits, 10), Add#(h__, no_of_ir_levels, 32), Add#(e__, 32, data_width) //Mul#(no_iterations, 3, ir_bits), ); let v_no_of_ir_pins = valueOf(no_of_ir_pins); let v_ir_bits = valueOf(ir_bits); let v_msb_ir_bits = valueOf(msb_ir_bits); let v_msb_ir_pins = valueOf(msb_ir_pins); let v_msb_priority = valueOf(msb_priority_levels); let v_data_width = valueOf(data_width); //(* noinline *) Vector#(no_of_ir_pins,Array#(Reg#(Bool))) rg_ip <- replicateM(mkCReg(2,False)); Reg#(Bool) rg_ie[v_no_of_ir_pins]; for(Integer i = 0; i < v_no_of_ir_pins;i=i+1) if(i==28 || i == 29) rg_ie[i] = readOnlyReg(True); else rg_ie[i] <- mkReg(False); Reg#(Bit#(no_of_ir_levels)) rg_priority_low[v_no_of_ir_pins]; for(Integer i =0; i < v_no_of_ir_pins; i=i+1) if(i==28 || i == 29) rg_priority_low[i] = readOnlyReg(1); else rg_priority_low[i] <- mkConfigReg(0); Reg#(Bit#(32)) rg_priority[v_no_of_ir_pins]; for(Integer i=0;i < v_no_of_ir_pins;i=i+1) rg_priority[i] = concatReg2(readOnlyReg(0), rg_priority_low[i]); Reg#(Bit#(no_of_ir_levels)) rg_priority_threshold <- mkReg(0); Reg#(Bit#(ir_bits)) rg_interrupt_id <- mkConfigReg(0); Reg#(Bool) rg_interrupt_valid <- mkConfigReg(False); Reg#(Maybe#(Bit#(ir_bits))) rg_completion_id <- mkReg(tagged Invalid); Reg#(Bit#(no_of_ir_pins)) rg_total_priority <- mkReg(0); Reg#(Bit#(1)) rg_plic_state <- mkReg(0); //TODO put an enum later Reg#(Bit#(no_of_ir_levels)) rg_winner_priority <- mkReg(0); Ifc_encoder#(no_of_ir_levels) ir_priority_encoder <- mkencoder(); Ifc_encoder#(no_of_ir_pins) irencoder <- mkencoder(); rule rl_prioritise(rg_plic_state==0); Bit#(priority_bits) winner_priority = 0; Bit#(ir_bits) winner_interrupts = 0; Bit#(x_ir_bits) ir_id_valid = 0; Bit#(no_of_ir_levels) lv_priority = 0; Bit#(no_of_ir_pins) lv_total_priority = 0; for(Integer i = 0; i < v_no_of_ir_pins; i = i + 1) begin if(rg_ip[i][1] && rg_ie[i]) begin lv_priority = lv_priority | truncate(rg_priority[i]); `ifdef verbose $display($time,"\tInterrupt id %d and priority is %d", i, lv_priority);`endif end end winner_priority = ir_priority_encoder.encode(lv_priority); `ifdef verbose $display($time,"\t winner priority is %d", winner_priority);`endif for(Integer i = 0; i < v_no_of_ir_pins; i = i + 1) begin if(rg_priority[i][winner_priority] == 1 && rg_ip[i][1] && rg_ie[i]) lv_total_priority[i] = 1; end if(lv_total_priority!=0) begin rg_total_priority <= lv_total_priority; rg_plic_state <= 1; Bit#(no_of_ir_levels) lv_winner_priority = 0; lv_winner_priority[winner_priority] = 1; rg_winner_priority <= lv_winner_priority; end endrule rule rl_encoder(rg_plic_state==1); Bit#(ir_bits) interrupt_id = irencoder.encode(rg_total_priority); if(interrupt_id!=0 && rg_priority_threshold >= rg_winner_priority) begin `ifdef verbose $display("Interrupt valid");`endif rg_interrupt_id <= interrupt_id; rg_interrupt_valid <= True; $display($time,"\t The highest priority interrupt is %d and the priority is ", interrupt_id, rg_winner_priority); end rg_plic_state <= 0; //if(lv_total_priority!=0) //winner_interrupts = encoder(encoder_tree(lv_total_priority))[v_msb_priority:0]; //if(winner_interrupts!=0) begin // if(winner_priority <= rg_priority_threshold) // begin // // `ifdef verbose $display("Interrupt valid");`endif // rg_interrupt_id <= winner_interrupts; // rg_interrupt_valid <= True; // $display($time,"\t The highest priority interrupt is %d and the priority is ", winner_interrupts, winner_priority); // end //end endrule //for(Integer i = 0; i> 2; if(mem_req.ld_st == Load) begin source_id = address[v_msb_ir_bits:0]; `ifdef verbose $display($time,"\tPLIC : source %d Priority set to %h", source_id, mem_req.write_data);`endif data_return = zeroExtend(rg_priority[source_id]); end else if(mem_req.ld_st == Store) begin Bit#(data_width) store_data; if(mem_req.byte_offset==0) store_data=mem_req.write_data[v_msb_ir_pins:0]; else store_data=mem_req.write_data[v_data_width-1:v_data_width-v_no_of_ir_pins]; mem_req.byte_offset = mem_req.byte_offset >> 2; source_id = address[v_msb_ir_bits:0]; $display($time,"\tPLIC : source %d Priority set to %h", source_id, store_data); rg_priority[source_id] <= truncate(store_data); end end else if(address < 'h0C002000) begin if(mem_req.ld_st == Load) begin source_id = address[v_msb_ir_bits:0]; source_id = source_id << 3; for(Integer i = 0; i < 8; i = i+1) data_return[i] = pack(rg_ip[source_id + fromInteger(i)][1]); end else if(mem_req.ld_st == Store) begin source_id = address[v_msb_ir_bits:0]; source_id = source_id << 3; for(Integer i = 0; i < 8; i = i+1) begin `ifdef verbose $display($time,"\tPLIC : pending interrupt %b id %d", mem_req.write_data[i], source_id);`endif rg_ip[source_id + fromInteger(i)][1] <= unpack(mem_req.write_data[i]); end end end else if(address < 'h0C020000) begin if(mem_req.ld_st == Load) begin source_id = address[v_msb_ir_bits:0]; source_id = source_id << 3; for(Integer i = 0; i < 8; i = i+1) data_return[i] = pack(rg_ie[source_id + fromInteger(i)]); `ifdef verbose $display($time,"PLIC: Printing Source Enable Interrupt: %h data_return: %h",source_id,data_return); `endif end else if(mem_req.ld_st == Store) begin source_id = address[v_msb_ir_bits:0]; source_id = source_id << 3; for(Integer i = 0; i < 8; i = i+1) begin `ifdef verbose $display($time,"\tPLIC : enabled interrupt %b id %d", mem_req.write_data[i], source_id);`endif rg_ie[source_id + fromInteger(i)] <= unpack(mem_req.write_data[i]); end end end else if(address == 'hC200000) begin if(mem_req.ld_st == Load) begin data_return = zeroExtend(rg_priority_threshold); end else if(mem_req.ld_st == Store) rg_priority_threshold <= mem_req.write_data[v_msb_priority:0]; end else if(address == 'hC200004) begin if(mem_req.ld_st == Load) begin data_return = zeroExtend(rg_interrupt_id); rg_ip[rg_interrupt_id][1] <= False; `ifdef verbose $display($time,"rg_ip is made false here"); `endif end else if(mem_req.ld_st == Store) begin source_id = mem_req.write_data[v_msb_ir_bits:0]; rg_completion_id <= tagged Valid source_id; `ifdef verbose $display("rg_completion_id is made tagged valid and completion is signaled-- source_id: %d",source_id); `endif end end return data_return; endmethod endinterface; method ActionValue#(Bit#(TLog#(no_of_ir_pins))) intrpt_completion if(isValid(rg_completion_id)); let completion_msg = validValue(rg_completion_id); rg_completion_id <= tagged Invalid; `ifdef verbose $display("Sending Completion to SoC"); `endif return completion_msg; endmethod method ActionValue#(Tuple2#(Bool,Bool)) intrpt_note; Bool if_nmi = (rg_interrupt_id == 28 || rg_interrupt_id == 29); Bool valid_interrupt = rg_interrupt_valid; rg_interrupt_valid <= False; return tuple2(valid_interrupt, if_nmi); endmethod endmodule interface Ifc_PLIC_AXI; interface AXI4_Lite_Slave_IFC#(`PADDR, `Reg_width,`USERSPACE) axi4_slave_plic; interface Vector#(`INTERRUPT_PINS,Ifc_global_interrupt) ifc_external_irq; method ActionValue#(Tuple2#(Bool,Bool)) intrpt_note; method ActionValue#(Bit#(TLog#(`INTERRUPT_PINS))) intrpt_completion; endinterface (*synthesize*) //(*conflict_free="rl_config_plic_reg_write,intrpt_completion"*) module mkplicperipheral(Ifc_PLIC_AXI); AXI4_Lite_Slave_Xactor_IFC #(`PADDR, `Reg_width, `USERSPACE) s_xactor_plic <- mkAXI4_Lite_Slave_Xactor; Ifc_PLIC#(`PADDR, `DCACHE_WORD_SIZE, `INTERRUPT_PINS, `INTERRUPT_LEVELS) plic <- mkplic(); (*preempts="rl_config_plic_reg_read, rl_config_plic_reg_write"*) rule rl_config_plic_reg_write; let aw <- pop_o(s_xactor_plic.o_wr_addr); let w <- pop_o(s_xactor_plic.o_wr_data); let w_strobe = w.wstrb; Bit#(3) byte_offset=0; for(Integer i=7; i >= 0; i=i-1) begin if(w_strobe[i]==1) byte_offset=fromInteger(i); end let x <- plic.ifc_prog_reg.prog_reg(UncachedMemReq{address : aw.awaddr, transfer_size : 'd3, u_signed : 0, byte_offset : byte_offset, write_data : w.wdata, ld_st : Store}); let w_resp = AXI4_Lite_Wr_Resp {bresp: AXI4_LITE_OKAY, buser: 0 }; //TODO user value is null s_xactor_plic.i_wr_resp.enq(w_resp); endrule rule rl_config_plic_reg_read; let ar <- pop_o(s_xactor_plic.o_rd_addr); let x <- plic.ifc_prog_reg.prog_reg(UncachedMemReq{address : ar.araddr, transfer_size : 'd3, u_signed : 0, byte_offset : 0, ld_st : Load}); if(ar.arsize==3'd0) x = duplicate(x[7:0]); else if(ar.arsize==3'd1) x = duplicate(x[15:0]); else if(ar.arsize==3'd2) x = duplicate(x[7:0]); let r = AXI4_Lite_Rd_Data {rresp: AXI4_LITE_OKAY, rdata: duplicate(x), ruser: 0}; s_xactor_plic.i_rd_data.enq(r); endrule interface axi4_slave_plic = s_xactor_plic.axi_side; interface ifc_external_irq = plic.ifc_external_irq; method ActionValue#(Tuple2#(Bool,Bool)) intrpt_note = plic.intrpt_note; method ActionValue#(Bit#(TLog#(`INTERRUPT_PINS))) intrpt_completion = plic.intrpt_completion; endmodule endpackage