xics: Rework the irq_gen process
authorPaul Mackerras <paulus@ozlabs.org>
Tue, 19 Oct 2021 04:13:31 +0000 (15:13 +1100)
committerPaul Mackerras <paulus@ozlabs.org>
Mon, 21 Feb 2022 02:29:51 +0000 (13:29 +1100)
At present, the loop in the irq_gen process generates a chain of
comparators and other logic to work out the source number and priority
of the most-favoured (lowest priority number) pending interrupt.
This replaces that chain with (1) logic to generate an array of bits,
one per priority, indicating whether any interrupt is pending at that
priority, (2) a priority encoder to select the most favoured priority
with an interrupt pending, (3) logic to generate an array of bits, one
per source, indicating whether an interrupt is pending at the priority
calculated in step 2, and (4) a priority encoder to work out the
lowest numbered source that has an interrupt pending at the selected
priority.  This reduces LUT utilization.

The priority encoder function implemented here uses the optimized
count-leading-zeroes logic from helpers.vhdl.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
helpers.vhdl
xics.vhdl

index 654c113553cd97a814bfa82813072627de2f35b5..bb69927209ed7a861a1cfad23e6246ae5c60beea 100644 (file)
@@ -30,6 +30,7 @@ package helpers is
     function bit_number(a: std_ulogic_vector(63 downto 0)) return std_ulogic_vector;
     function edgelocation(v: std_ulogic_vector; nbits: natural) return std_ulogic_vector;
     function count_left_zeroes(val: std_ulogic_vector) return std_ulogic_vector;
+    function count_right_zeroes(val: std_ulogic_vector) return std_ulogic_vector;
 end package helpers;
 
 package body helpers is
@@ -270,22 +271,28 @@ package body helpers is
         return p;
     end function;
 
-    -- Count leading zeroes operation
+    -- Count leading zeroes operations
     -- Assumes the value passed in is not zero (if it is, zero is returned)
-    function count_left_zeroes(val: std_ulogic_vector) return std_ulogic_vector is
-        variable rev: std_ulogic_vector(val'left downto val'right);
+    function count_right_zeroes(val: std_ulogic_vector) return std_ulogic_vector is
         variable sum: std_ulogic_vector(val'left downto val'right);
         variable onehot: std_ulogic_vector(val'left downto val'right);
         variable edge: std_ulogic_vector(val'left downto val'right);
         variable bn, bn_e, bn_o: std_ulogic_vector(5 downto 0);
     begin
-        rev := bit_reverse(val);
-        sum := std_ulogic_vector(- signed(rev));
-        onehot := sum and rev;
-        edge := sum or rev;
+        sum := std_ulogic_vector(- signed(val));
+        onehot := sum and val;
+        edge := sum or val;
         bn_e := edgelocation(std_ulogic_vector(resize(signed(edge), 64)), 6);
         bn_o := bit_number(std_ulogic_vector(resize(unsigned(onehot), 64)));
         bn := bn_e(5 downto 2) & bn_o(1 downto 0);
         return bn;
     end;
+
+    function count_left_zeroes(val: std_ulogic_vector) return std_ulogic_vector is
+        variable rev: std_ulogic_vector(val'left downto val'right);
+    begin
+        rev := bit_reverse(val);
+        return count_right_zeroes(rev);
+    end;
+
 end package body helpers;
index 0e3a1da7b504313944b66457aa15e3dace94357d..6daa5d44927ba92ab79bb113c1133744019ff670 100644 (file)
--- a/xics.vhdl
+++ b/xics.vhdl
@@ -54,9 +54,6 @@ architecture behaviour of xics_icp is
 
     signal r, r_next : reg_internal_t;
 
-    -- hardwire the hardware IRQ priority
-    constant HW_PRIORITY : std_ulogic_vector(7 downto 0) := x"80";
-
     -- 8 bit offsets for each presentation
     constant XIRR_POLL : std_ulogic_vector(7 downto 0) := x"00";
     constant XIRR      : std_ulogic_vector(7 downto 0) := x"04";
@@ -207,12 +204,14 @@ use ieee.numeric_std.all;
 
 library work;
 use work.common.all;
+use work.utils.all;
 use work.wishbone_types.all;
+use work.helpers.all;
 
 entity xics_ics is
     generic (
         SRC_NUM    : integer range 1 to 256  := 16;
-        PRIO_BITS  : integer range 1 to 8    := 8
+        PRIO_BITS  : integer range 1 to 8    := 3
         );
     port (
         clk          : in std_logic;
@@ -228,12 +227,16 @@ end xics_ics;
 
 architecture rtl of xics_ics is
 
+    constant SRC_NUM_BITS : natural := log2(SRC_NUM);
+
     subtype pri_t is std_ulogic_vector(PRIO_BITS-1 downto 0);
     type xive_t is record
         pri : pri_t;
     end record;
     constant pri_masked : pri_t := (others => '1');
 
+    subtype pri_vector_t is std_ulogic_vector(2**PRIO_BITS - 1 downto 0);
+
     type xive_array_t is array(0 to SRC_NUM-1) of xive_t;
     signal xives : xive_array_t;
 
@@ -262,8 +265,15 @@ architecture rtl of xics_ics is
     end function;
 
     function prio_pack(pri8: std_ulogic_vector(7 downto 0)) return pri_t is
+        variable masked : std_ulogic_vector(7 downto 0);
     begin
-        return pri8(PRIO_BITS-1 downto 0);
+        masked := x"00";
+        masked(PRIO_BITS - 1 downto 0) := (others => '1');
+        if pri8 >= masked then
+            return pri_masked;
+        else
+            return pri8(PRIO_BITS-1 downto 0);
+        end if;
     end function;
 
     function prio_unpack(pri: pri_t) return std_ulogic_vector is
@@ -276,8 +286,27 @@ architecture rtl of xics_ics is
             r(PRIO_BITS-1 downto 0) := pri;
         end if;
         return r;
-   end function;
+    end function;
 
+    function prio_decode(pri: pri_t) return pri_vector_t is
+        variable v: pri_vector_t;
+    begin
+        v := (others => '0');
+        v(to_integer(unsigned(pri))) := '1';
+        return v;
+    end function;
+
+    -- Assumes nbits <= 6; v is 2^nbits wide
+    function priority_encoder(v: std_ulogic_vector; nbits: natural) return std_ulogic_vector is
+        variable h: std_ulogic_vector(2**nbits - 1 downto 0);
+        variable p: std_ulogic_vector(5 downto 0);
+    begin
+        -- Set the lowest-priority (highest-numbered) bit
+        h := v;
+        h(2**nbits - 1) := '1';
+        p := count_right_zeroes(h);
+        return p(nbits - 1 downto 0);
+    end function;
 
 -- Register map
     --     0  : Config
@@ -391,35 +420,33 @@ begin
     end process;
 
     irq_gen: process(all)
-        variable max_idx : integer range 0 to SRC_NUM-1;
+        variable max_idx : std_ulogic_vector(SRC_NUM_BITS - 1 downto 0);
         variable max_pri : pri_t;
-
-        -- A more favored than b ?
-        function a_mf_b(a: pri_t; b: pri_t) return boolean is
-            variable a_i : unsigned(PRIO_BITS-1 downto 0);
-            variable b_i : unsigned(PRIO_BITS-1 downto 0);
-        begin
-            a_i := unsigned(a);
-            b_i := unsigned(b);
-            report "a_mf_b a=" & to_hstring(a) &
-                " b=" & to_hstring(b) &
-                " r=" & boolean'image(a < b);
-            return a_i < b_i;
-        end function;
+        variable pending_pri : pri_vector_t;
+        variable pending_at_pri : std_ulogic_vector(SRC_NUM - 1 downto 0);
     begin
-        -- XXX FIXME: Use a tree
-        max_pri := pri_masked;
-        max_idx := 0;
+        -- Work out the most-favoured (lowest) priority of the pending interrupts
+        pending_pri := (others => '0');
         for i in 0 to SRC_NUM - 1 loop
-            if int_level_l(i) = '1' and a_mf_b(xives(i).pri, max_pri) then
-                max_pri := xives(i).pri;
-                max_idx := i;
+            if int_level_l(i) = '1' then
+                pending_pri := pending_pri or prio_decode(xives(i).pri);
             end if;
         end loop;
+        max_pri := priority_encoder(pending_pri, PRIO_BITS);
+
+        -- Work out which interrupts are pending at that priority
+        pending_at_pri := (others => '0');
+        for i in 0 to SRC_NUM - 1 loop
+            if int_level_l(i) = '1' and xives(i).pri = max_pri then
+                pending_at_pri(i) := '1';
+            end if;
+        end loop;
+        max_idx := priority_encoder(pending_at_pri, SRC_NUM_BITS);
+
         if max_pri /= pri_masked then
-            report "MFI: " & integer'image(max_idx) & " pri=" & to_hstring(prio_unpack(max_pri));
+            report "MFI: " & integer'image(to_integer(unsigned(max_idx))) & " pri=" & to_hstring(prio_unpack(max_pri));
         end if;
-        icp_out_next.src <= std_ulogic_vector(to_unsigned(max_idx, 4));
+        icp_out_next.src <= max_idx;
         icp_out_next.pri <= prio_unpack(max_pri);
     end process;