PLIC modified to support interrupts upto 1024
[shakti-peripherals.git] / src / peripherals / plic / plic.bsv
index 0e94ee7bd7d075f37033ebdf68522a047d775ee1..1f1661acb35b5951542a93d92bbb216987ec7d68 100644 (file)
@@ -13,14 +13,17 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 */
 package plic;
        import Vector::*;
-       //import defined_parameters::*;
        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
@@ -51,7 +54,8 @@ package plic;
 */
 
 
-interface Ifc_PLIC#(numeric type addr_width,numeric type word_size,numeric type no_of_ir_pins);
+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;
@@ -59,95 +63,36 @@ interface Ifc_PLIC#(numeric type addr_width,numeric type word_size,numeric type
 endinterface
 
 //(*conflict_free = "rl_prioritise, prog_reg"*)
-module mkplic(Ifc_PLIC#(addr_width,word_size,no_of_ir_pins))
+module mkplic(Ifc_PLIC#(addr_width,word_size,no_of_ir_pins,no_of_ir_levels))
        provisos(
-       Log#(no_of_ir_pins, priority_bits),
+       Log#(no_of_ir_pins, ir_bits),
+       Log#(no_of_ir_levels, priority_bits),
        Mul#(8,word_size,data_width),
-       Add#(1,priority_bits,x_priority_bits),
-       Add#(msb_priority,1,priority_bits),
-       Add#(msb_priority_bits,1,no_of_ir_pins),
-       Add#(b__, no_of_ir_pins, data_width),
-       Add#(c__, priority_bits, data_width),
-       Add#(a__, 8, no_of_ir_pins),
-       Add#(e__, 32, data_width),
-       Add#(g__, 32, no_of_ir_pins),
-       Add#(f__, 3, priority_bits),
-       Add#(d__, 5, priority_bits)
+       //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_priority_bits = valueOf(priority_bits);
-       let v_msb_priority_bits = valueOf(msb_priority_bits);
-       let v_msb_priority = valueOf(msb_priority);
+       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 *)
 
-       /* This function defines the working of priority encoder with 4 bit inputs */
-       function Bit#(8) priority_encoder(Bit#(8) inp, Bool alu_free);
-          Bit#(8) outp = 0;
-          if(alu_free) begin
-               if(inp[0]==1)
-                  outp[0] = 1'b1;
-               else if(inp[1]==1)
-                  outp[1] = 1'b1;
-               else if(inp[2]==1)
-                  outp[2] = 1'b1;
-               else if(inp[3]==1)
-                  outp[3] = 1'b1;
-               else if(inp[4]==1)
-                  outp[4] = 1'b1;
-               else if(inp[5]==1)
-                  outp[5] = 1'b1;
-               else if(inp[6]==1)
-                  outp[6] = 1'b1;
-               else if(inp[7]==1)
-                  outp[7] = 1'b1;
-          end
-       
-          return outp;
-       endfunction
-       
-       function bit any_req(Bit#(8) inp);
-          return inp[0] | inp[1] | inp[2] | inp[3] | inp[4] | inp[5] | inp[6] | inp[7];
-       endfunction
-       
-       /* Encodes the grant vector */
-       function Bit#(x_priority_bits) encoder(Bit#(no_of_ir_pins) inp);
-          Bit#(priority_bits) outp = 0;
-          bit outp_valid = 1'b1;
-          for(Integer i = 0; i < v_no_of_ir_pins; i = i+1) begin
-                       if(inp[i]==1)
-                               outp = fromInteger(i);
-          end
-          return {outp_valid,outp};
-       endfunction
-       
-  function Reg#(t) readOnlyReg(t r);
-    return (interface Reg;
-       method t _read = r;
-       method Action _write(t x) = noAction;
-    endinterface);
-  endfunction
-
-       /* Request vectors are passed down the tree and the grants are given back */
-       function Bit#(no_of_ir_pins) encoder_tree(Bit#(no_of_ir_pins) inp);
-               Bit#(no_of_ir_pins) outp = 0;
-               //request to root
-               Bit#(8) root_reqs;
-               
-               //grant from root
-               Bit#(8) root_grants;
-               
-               for(Integer i=0;i<8;i=i+1)
-                  root_reqs[i] = any_req(inp[8*fromInteger(i)+7:8*fromInteger(i)]);
-               
-               root_grants = priority_encoder(root_reqs, True);
-               
-               //grants are passed back to leaves
-               for(Integer i=0;i<8;i=i+1)
-                       outp[8*fromInteger(i)+7:8*fromInteger(i)] = priority_encoder(inp[8*fromInteger(i)+7:8*fromInteger(i)], unpack(root_grants[i]));
-               return outp;
-       endfunction                     
 
        Vector#(no_of_ir_pins,Array#(Reg#(Bool))) rg_ip <- replicateM(mkCReg(2,False));
        Reg#(Bool) rg_ie[v_no_of_ir_pins];
@@ -156,57 +101,117 @@ module mkplic(Ifc_PLIC#(addr_width,word_size,no_of_ir_pins))
                        rg_ie[i] = readOnlyReg(True); 
                else
                        rg_ie[i] <- mkReg(False); 
-       Reg#(Bit#(32)) rg_priority_low[v_no_of_ir_pins];
+       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(32'h00000001);
+                       rg_priority_low[i] = readOnlyReg(1);
                else
                        rg_priority_low[i] <- mkConfigReg(0);
-       Reg#(Bit#(no_of_ir_pins)) rg_priority[v_no_of_ir_pins];
+       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#(5)) rg_priority_threshold_low <- mkReg(0);
-       Reg#(Bit#(priority_bits))        rg_priority_threshold = concatReg2(readOnlyReg(0),rg_priority_threshold_low);
-       Reg#(Bit#(priority_bits))        rg_interrupt_id <- mkConfigReg(0);
+       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#(priority_bits))) rg_completion_id <- mkReg(tagged Invalid);
+       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;
+       rule rl_prioritise(rg_plic_state==0);
                Bit#(priority_bits) winner_priority = 0;
-               Bit#(priority_bits) winner_interrupts = 0;
-               Bit#(x_priority_bits) ir_id_valid = 0;
-               Bit#(no_of_ir_pins) lv_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 | rg_priority[i];
-                               winner_interrupts = fromInteger(i);
+                               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 = encoder(encoder_tree(lv_priority))[v_msb_priority:0];
+               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)
-               winner_interrupts = encoder(encoder_tree(lv_total_priority))[v_msb_priority:0];
-               if(winner_interrupts!=0) begin
-                       ir_id_valid = encoder(rg_priority[winner_interrupts]);
-                       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
+               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<no_iterations; i=i+1) begin
+       //      rule rl_encoder(rg_plic_state==1);
+       //              Bit#(no_of_ir_pins) lv_total_priority = rg_total_priority;
+       //              Vector#(tree_ir, Bit#(8)) lv_priority_ip;
+       //              Bit#(tree_ir) level_encoder;
+       //              Vector#(tree_ir, 3) ir_encoded;
+       //              
+       //              for(Integer i=0;i<tree_ir;i=i+1) begin
+       //                      lv_priority_ip[i]=lv_total_priority[i*8+7:i*8];
+       //              end
+       //              for(Integer i=0;i<tree_ir;i=i+1) begin
+       //                      level_encoder[i] = any_req(lv_priority_ip[i]);
+       //              end
+       //              for(Integer i=0;i<-tree_ir;i=i+1) begin
+       //                      ir_encoded[i] = encoder(priority_encoder(lv_priority_ip));
+       //              end
+       //              for(Integer i=0;i<-tree_ir;i=i+1) begin
+       //                      rg_prioritized_encoded <= ir_encoded;
+       //              end
+       //                      
+       //              
+       //              //if(lv_total_priority!=0)
+       //              //winner_interrupts = encoder(encoder_tree(lv_total_priority))[v_msb_priority:0];
+       //              //if(winner_interrupts!=0) begin
+       //              //      ir_id_valid = encoder(rg_priority[winner_interrupts]);
+       //              //      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
+       //end
+
        Vector#(no_of_ir_pins, Ifc_global_interrupt) temp_ifc_irq;
 
        for(Integer i = 0; i < v_no_of_ir_pins; i = i + 1) begin
@@ -229,38 +234,36 @@ interface ifc_prog_reg = interface Ifc_program_registers;
                                                                //update memory mapped registers
                                                                `ifdef verbose $display($time,"\tPLIC : programming registers for address %h", mem_req.address);`endif
                                                                let address = mem_req.address;
-                                                               Bit#(priority_bits) source_id=0;
+                                                               Bit#(ir_bits) source_id=0;
                                                                Bit#(data_width) data_return = 0;
-//                                                             if(address < 'h0C001000) begin
-                                                               if (address < `PLICBase + 'h1000)begin
+                                                               if(address < 'h0C001000) begin
                                                                        address = address >> 2;
                                                                        if(mem_req.ld_st == Load) begin
-                                                                               source_id = address[v_msb_priority:0];
+                                                                               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#(no_of_ir_pins) store_data;
+                                                                               Bit#(data_width) store_data;
                                                                                if(mem_req.byte_offset==0)
-                                                                                       store_data=mem_req.write_data[v_msb_priority_bits: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_priority:0] | zeroExtend(mem_req.byte_offset);
+                                                                               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] <= store_data;
+                                                                               rg_priority[source_id] <= truncate(store_data);
                                                                        end
                                                                end
-                                                               //else if(address < 'h0C002000) begin
-                                                                 else if(address<`PLICBase+'h2000)begin
+                                                               else if(address < 'h0C002000) begin
                                                                        if(mem_req.ld_st == Load) begin
-                                                                               source_id = address[v_msb_priority:0];
+                                                                               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 = zeroExtend(mem_req.byte_offset);
+                                                                               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
@@ -268,17 +271,16 @@ interface ifc_prog_reg = interface Ifc_program_registers;
                                                                                end
                                                                        end
                                                                end
-                                                               //else if(address < 'h0C020000) begin
-                                                                 else if(address < `PLICBase+'h20000)begin
+                                                               else if(address < 'h0C020000) begin
                                                                        if(mem_req.ld_st == Load) begin
-                                                                               source_id = address[v_msb_priority:0];
+                                                                               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 = zeroExtend(mem_req.byte_offset);
+                                                                               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
@@ -286,23 +288,21 @@ interface ifc_prog_reg = interface Ifc_program_registers;
                                                                                end
                                                                        end
                                                                end
-                                               //              else if(address == 'hC200000) begin
-                                                               else if(address ==`PLICBase+'h200000)begin
+                                                               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
-                                                               else if(address == `PLICBase+'h200004)begin
+                                                               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_priority:0];
+                                                                               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
@@ -339,7 +339,7 @@ endinterface
 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) plic <- mkplic();
+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;
@@ -362,11 +362,12 @@ Ifc_PLIC#(`PADDR, `DCACHE_WORD_SIZE, `INTERRUPT_PINS) plic <- mkplic();
                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)
+        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);