From 08af7a09d846cf595ec6b53f222a1b2fb64b26b1 Mon Sep 17 00:00:00 2001
From: Mathias Kreider <m.kreider@gsi.de>
Date: Wed, 15 Apr 2015 17:26:17 +0200
Subject: [PATCH] fixed irq loss in edge triggered mode

---
 modules/wishbone/wb_irq/irqm_core.vhd | 81 ++++++++++++++++++---------
 1 file changed, 56 insertions(+), 25 deletions(-)

diff --git a/modules/wishbone/wb_irq/irqm_core.vhd b/modules/wishbone/wb_irq/irqm_core.vhd
index f999c94b..7b0c1e69 100644
--- a/modules/wishbone/wb_irq/irqm_core.vhd
+++ b/modules/wishbone/wb_irq/irqm_core.vhd
@@ -34,7 +34,7 @@ use work.wb_irq_pkg.all;
 entity irqm_core is
 generic( g_channels     : natural := 32;     -- number of interrupt lines
          g_round_rb     : boolean := true;   -- scheduler       true: round robin,                         false: prioritised 
-         g_det_edge     : boolean := true    -- edge detection. true: trigger on rising edge of irq lines, false: trigger on high level
+         g_det_edge     : boolean   -- edge detection. true: trigger on rising edge of irq lines, false: trigger on high level
 ); 
 port    (clk_i          : in  std_logic;   -- clock
          rst_n_i        : in  std_logic;   -- reset, active LO
@@ -55,6 +55,11 @@ end entity;
 
 architecture behavioral of irqm_core is
 
+subtype cnt is unsigned(8 downto 0);
+type cnt_array is array(natural range <>) of cnt;
+
+signal r_cnt_array : cnt_array(g_channels-1 downto 0);
+
 signal s_msg         : t_wishbone_data_array(g_channels-1 downto 0);    
 signal s_dst         : t_wishbone_address_array(g_channels-1 downto 0);
 
@@ -68,7 +73,7 @@ signal r_irq0,
 signal r_en0,
        r_en1         : std_logic;
 
-signal r_pending     : std_logic_vector(g_channels-1 downto 0);
+signal r_pending, r_wait     : std_logic_vector(g_channels-1 downto 0);
 
 signal s_wb_send     : std_logic;
 
@@ -76,7 +81,7 @@ signal idx           : natural range 0 to g_channels-1;
 signal idx_robin     : natural range 0 to g_channels-1;
 signal idx_prio      : natural range 0 to g_channels-1;
 
-signal r_cyc         : std_logic;
+signal r_cyc0, r_cyc1, s_cyc_f_edge : std_logic;
 signal r_stb         : std_logic;
 
 begin
@@ -86,11 +91,14 @@ s_msg             <= msi_msg_array;
 s_dst             <= msi_dst_array;
 
 -- always full words, always write 
-irq_master_o.cyc  <= r_cyc;
+irq_master_o.cyc  <= r_cyc0;
 irq_master_o.stb  <= r_stb;
 irq_master_o.sel  <= (others => '1');
 irq_master_o.we   <= '1';
 
+
+   
+
 -------------------------------------------------------------------------
 -- registering and counters
 -------------------------------------------------------------------------
@@ -130,6 +138,7 @@ irq_master_o.we   <= '1';
       s_irq_edge <= r_irqm0;
    end generate;
   
+   s_cyc_f_edge <= not r_cyc0 and r_cyc1;
    
      -- round robin
      idx_round_robin : process(clk_i)
@@ -138,7 +147,7 @@ irq_master_o.we   <= '1';
          if(rst_n_i = '0') then
             idx_robin       <= 0;
          else 
-           if(r_cyc = '0' and r_pending(idx_robin) = '0') then 
+           if(r_cyc0 = '0' and r_cyc1 = '0') then 
               if(idx_robin = g_channels-1) then
                   idx_robin <= 0;   
               else
@@ -164,22 +173,42 @@ irq_master_o.we   <= '1';
 --------------------------------------------------------------------------------------------
    s_wb_send   <= r_pending(idx);
  
-   -- keep track of what needs sending
-   queue_mux : process(clk_i)
-   variable v_set_pending, v_clr_pending : std_logic_vector(r_pending'length-1 downto 0);
-   begin
-      if rising_edge(clk_i) then
-         if((rst_n_i) = '0') then            
-            r_pending <= (others => '0');
-         else
-            v_clr_pending        := (others => '1');                
-            v_clr_pending(idx)   := not r_cyc;
-            v_set_pending        := s_irq_edge; 
-            r_pending            <= (r_pending or v_set_pending) and v_clr_pending;
-          end if;
-      end if;
-   end process queue_mux; 
-
+   
+ 
+   GEN_REG: 
+   for i in 0 to g_channels-1 generate
+      r_pending(i) <= not r_cnt_array(i)(r_cnt_array(i)'high);
+
+      -- keep track of what needs sending
+      queue_mux : process(clk_i)
+      variable v_inc, v_dec : unsigned(cnt'range);
+      begin
+         if rising_edge(clk_i) then
+            if((rst_n_i) = '0') then            
+               r_cnt_array(i) <= (others => '1');
+               r_wait(i) <= '0';
+            else
+              -- add to each channel count with rising edge
+              
+               v_inc := (others => '0');
+               v_inc(0) := s_irq_edge(i);
+               
+               -- subtract from sending channel count when cycle is finished
+               v_dec := (others => '0');
+               -- when my cnt is pending and it's my turn ...
+               if(r_pending(i) = '1' and i = idx) then
+                  r_wait(i) <= '1';
+               end if;
+               if( r_wait(i) = '1' and s_cyc_f_edge = '1') then  
+                  v_dec := (others => s_cyc_f_edge);
+                  r_wait(i) <= '0';
+               end if;      
+               r_cnt_array(i) <= r_cnt_array(i) + v_inc + v_dec;
+                  
+             end if;
+         end if;
+      end process queue_mux; 
+   end generate GEN_REG; 
 
 -------------------------------------------------------------------------
 -- WB master generating IRQ msgs
@@ -189,18 +218,20 @@ irq_master_o.we   <= '1';
   begin
    if rising_edge(clk_i) then
       if(rst_n_i = '0') then
-         r_cyc <= '0';
+         r_cyc0 <= '0';
+         r_cyc1 <= '0';
          r_stb <= '0';
       else
-         if r_cyc = '1' then
+         r_cyc1 <= r_cyc0;
+         if r_cyc0 = '1' then
            if irq_master_i.stall = '0' then
              r_stb <= '0';
            end if;
            if (irq_master_i.ack or irq_master_i.err) = '1' then
-             r_cyc <= '0';
+             r_cyc0 <= '0';
            end if;
          else
-           r_cyc <= s_wb_send;
+           r_cyc0 <= s_wb_send;
            r_stb <= s_wb_send;
            irq_master_o.adr <= s_dst(idx); 
            irq_master_o.dat <= s_msg(idx);
-- 
GitLab