You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			454 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			VHDL
		
	
			
		
		
	
	
			454 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			VHDL
		
	
| --
 | |
| -- This is a simple XICS compliant interrupt controller.  This is a
 | |
| -- Presenter (ICP) and Source (ICS) in two small units directly
 | |
| -- connected to each other with no routing layer.
 | |
| --
 | |
| -- The sources have a configurable IRQ priority set a set of ICS
 | |
| -- registers in the source units.
 | |
| --
 | |
| -- The source ids start at 16 for int_level_in(0) and go up from
 | |
| -- there (ie int_level_in(1) is source id 17). XXX Make a generic
 | |
| --
 | |
| -- The presentation layer will pick an interupt that is more
 | |
| -- favourable than the current CPPR and present it via the XISR and
 | |
| -- send an interrpt to the processor (via e_out). This may not be the
 | |
| -- highest priority interrupt currently presented (which is allowed
 | |
| -- via XICS)
 | |
| --
 | |
| 
 | |
| library ieee;
 | |
| use ieee.std_logic_1164.all;
 | |
| use ieee.numeric_std.all;
 | |
| 
 | |
| library work;
 | |
| use work.common.all;
 | |
| use work.wishbone_types.all;
 | |
| 
 | |
| entity xics_icp is
 | |
|     port (
 | |
|         clk          : in std_logic;
 | |
|         rst          : in std_logic;
 | |
| 
 | |
|         wb_in        : in wb_io_master_out;
 | |
|         wb_out       : out wb_io_slave_out;
 | |
| 
 | |
|         ics_in       : in ics_to_icp_t;
 | |
|         core_irq_out : out std_ulogic
 | |
|         );
 | |
| end xics_icp;
 | |
| 
 | |
| architecture behaviour of xics_icp is
 | |
|     type reg_internal_t is record
 | |
|         xisr       : std_ulogic_vector(23 downto 0);
 | |
|         cppr       : std_ulogic_vector(7 downto 0);
 | |
|         mfrr       : std_ulogic_vector(7 downto 0);
 | |
|         irq        : std_ulogic;
 | |
|         wb_rd_data : std_ulogic_vector(31 downto 0);
 | |
|         wb_ack     : std_ulogic;
 | |
|     end record;
 | |
|     constant reg_internal_init : reg_internal_t :=
 | |
|         (wb_ack => '0',
 | |
|          mfrr => x"ff", -- mask everything on reset
 | |
|          irq => '0',
 | |
|          others => (others => '0'));
 | |
| 
 | |
|     signal r, r_next : reg_internal_t;
 | |
| 
 | |
|     -- 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";
 | |
|     constant RESV0     : std_ulogic_vector(7 downto 0) := x"08";
 | |
|     constant MFRR      : std_ulogic_vector(7 downto 0) := x"0c";
 | |
| 
 | |
| begin
 | |
| 
 | |
|     regs : process(clk)
 | |
|     begin
 | |
|         if rising_edge(clk) then
 | |
|             r <= r_next;
 | |
| 
 | |
|             -- We delay core_irq_out by a cycle to help with timing
 | |
|             core_irq_out <= r.irq;
 | |
|         end if;
 | |
|     end process;
 | |
| 
 | |
|     wb_out.dat <= r.wb_rd_data;
 | |
|     wb_out.ack <= r.wb_ack;
 | |
|     wb_out.stall <= '0'; -- never stall wishbone
 | |
| 
 | |
|     comb : process(all)
 | |
|         variable v : reg_internal_t;
 | |
|         variable xirr_accept_rd : std_ulogic;
 | |
| 
 | |
|         function  bswap(v : in std_ulogic_vector(31 downto 0)) return std_ulogic_vector is
 | |
|             variable r : std_ulogic_vector(31 downto 0);
 | |
|         begin
 | |
|             r( 7 downto  0) := v(31 downto 24);
 | |
|             r(15 downto  8) := v(23 downto 16);
 | |
|             r(23 downto 16) := v(15 downto  8);
 | |
|             r(31 downto 24) := v( 7 downto  0);
 | |
|             return r;
 | |
|         end function;
 | |
| 
 | |
|         variable be_in  : std_ulogic_vector(31 downto 0);
 | |
|         variable be_out : std_ulogic_vector(31 downto 0);
 | |
| 
 | |
|         variable pending_priority : std_ulogic_vector(7 downto 0);
 | |
|     begin
 | |
|         v := r;
 | |
| 
 | |
|         v.wb_ack := '0';
 | |
| 
 | |
|         xirr_accept_rd := '0';
 | |
| 
 | |
|         be_in := bswap(wb_in.dat);
 | |
|         be_out := (others => '0');
 | |
| 
 | |
|         if wb_in.cyc = '1' and wb_in.stb = '1' then
 | |
|             v.wb_ack := '1'; -- always ack
 | |
|             if wb_in.we = '1' then -- write
 | |
|                 -- writes to both XIRR are the same
 | |
|                 case wb_in.adr(5 downto 0) & "00" is
 | |
|                 when XIRR_POLL =>
 | |
|                     report "ICP XIRR_POLL write";
 | |
|                     v.cppr := be_in(31 downto 24);
 | |
|                 when XIRR =>
 | |
|                     v.cppr := be_in(31 downto 24);
 | |
|                     if wb_in.sel = x"f"  then -- 4 byte
 | |
|                         report "ICP XIRR write word (EOI) :" & to_hstring(be_in);
 | |
|                     elsif wb_in.sel = x"1"  then -- 1 byte
 | |
|                         report "ICP XIRR write byte (CPPR):" & to_hstring(be_in(31 downto 24));
 | |
|                     else
 | |
|                         report "ICP XIRR UNSUPPORTED write ! sel=" & to_hstring(wb_in.sel);
 | |
|                     end if;
 | |
|                 when MFRR =>
 | |
|                     v.mfrr := be_in(31 downto 24);
 | |
|                     if wb_in.sel = x"f" then -- 4 bytes
 | |
|                         report "ICP MFRR write word:" & to_hstring(be_in);
 | |
|                     elsif wb_in.sel = x"1" then -- 1 byte
 | |
|                         report "ICP MFRR write byte:" & to_hstring(be_in(31 downto 24));
 | |
|                     else
 | |
|                         report "ICP MFRR UNSUPPORTED write ! sel=" & to_hstring(wb_in.sel);
 | |
|                     end if;
 | |
|                 when others =>                        
 | |
|                 end case;
 | |
| 
 | |
|             else -- read
 | |
| 
 | |
|                 case wb_in.adr(5 downto 0) & "00" is
 | |
|                 when XIRR_POLL =>
 | |
|                     report "ICP XIRR_POLL read";
 | |
|                     be_out := r.cppr & r.xisr;
 | |
|                 when XIRR =>
 | |
|                     report "ICP XIRR read";
 | |
|                     be_out := r.cppr & r.xisr;
 | |
|                     if wb_in.sel = x"f" then
 | |
|                         xirr_accept_rd := '1';
 | |
|                     end if;
 | |
|                 when MFRR =>
 | |
|                     report "ICP MFRR read";
 | |
|                     be_out(31 downto 24) := r.mfrr;
 | |
|                 when others =>                        
 | |
|                 end case;
 | |
|             end if;
 | |
|         end if;
 | |
| 
 | |
|         pending_priority := x"ff";
 | |
|         v.xisr := x"000000";
 | |
|         v.irq := '0';
 | |
| 
 | |
|         if ics_in.pri /= x"ff" then
 | |
|             v.xisr := x"00001" & ics_in.src;
 | |
|             pending_priority := ics_in.pri;
 | |
|         end if;
 | |
| 
 | |
|         -- Check MFRR
 | |
|         if unsigned(r.mfrr) < unsigned(pending_priority) then --
 | |
|             v.xisr := x"000002"; -- special XICS MFRR IRQ source number
 | |
|             pending_priority := r.mfrr;
 | |
|         end if;
 | |
| 
 | |
|         -- Accept the interrupt
 | |
|         if xirr_accept_rd = '1' then
 | |
|             report "XICS: ICP ACCEPT" &
 | |
|                 " cppr:" &  to_hstring(r.cppr) &
 | |
|                 " xisr:" & to_hstring(r.xisr) &
 | |
|                 " mfrr:" & to_hstring(r.mfrr);
 | |
|             v.cppr := pending_priority;
 | |
|         end if;
 | |
| 
 | |
|         v.wb_rd_data := bswap(be_out);
 | |
| 
 | |
|         if unsigned(pending_priority) < unsigned(v.cppr) then
 | |
|             if r.irq = '0' then
 | |
|                 report "IRQ set";
 | |
|             end if;
 | |
|             v.irq := '1';
 | |
|         elsif r.irq = '1' then
 | |
|             report "IRQ clr";
 | |
|         end if;
 | |
| 
 | |
|         if rst = '1' then
 | |
|             v := reg_internal_init;
 | |
|         end if;
 | |
| 
 | |
|         r_next <= v;
 | |
| 
 | |
|     end process;
 | |
| 
 | |
| end architecture behaviour;
 | |
| 
 | |
| library ieee;
 | |
| use ieee.std_logic_1164.all;
 | |
| 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    := 3
 | |
|         );
 | |
|     port (
 | |
|         clk          : in std_logic;
 | |
|         rst          : in std_logic;
 | |
| 
 | |
|         wb_in        : in wb_io_master_out;
 | |
|         wb_out       : out wb_io_slave_out;
 | |
| 
 | |
|         int_level_in : in std_ulogic_vector(SRC_NUM - 1 downto 0);
 | |
|         icp_out      : out ics_to_icp_t
 | |
|         );
 | |
| 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;
 | |
| 
 | |
|     signal wb_valid : std_ulogic;
 | |
|     signal reg_idx : integer range 0 to SRC_NUM - 1;
 | |
|     signal icp_out_next : ics_to_icp_t;
 | |
|     signal int_level_l : std_ulogic_vector(SRC_NUM - 1 downto 0);
 | |
| 
 | |
|     function bswap(v : in std_ulogic_vector(31 downto 0)) return std_ulogic_vector is
 | |
|         variable r : std_ulogic_vector(31 downto 0);
 | |
|     begin
 | |
|         r( 7 downto  0) := v(31 downto 24);
 | |
|         r(15 downto  8) := v(23 downto 16);
 | |
|         r(23 downto 16) := v(15 downto  8);
 | |
|         r(31 downto 24) := v( 7 downto  0);
 | |
|         return r;
 | |
|     end function;
 | |
| 
 | |
|     function get_config return std_ulogic_vector is
 | |
|         variable r: std_ulogic_vector(31 downto 0);
 | |
|     begin
 | |
|         r := (others => '0');
 | |
|         r(23 downto  0) := std_ulogic_vector(to_unsigned(SRC_NUM, 24));
 | |
|         r(27 downto 24) := std_ulogic_vector(to_unsigned(PRIO_BITS, 4));
 | |
|         return r;
 | |
|     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
 | |
|         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
 | |
|         variable r : std_ulogic_vector(7 downto 0);
 | |
|     begin
 | |
|         if pri = pri_masked then
 | |
|             r := x"ff";
 | |
|         else
 | |
|             r := (others => '0');
 | |
|             r(PRIO_BITS-1 downto 0) := pri;
 | |
|         end if;
 | |
|         return r;
 | |
|     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
 | |
|     --     4  : Debug/diagnostics
 | |
|     --   800  : XIVE0
 | |
|     --   804  : XIVE1 ...
 | |
|     --
 | |
|     -- Config register format:
 | |
|     --
 | |
|     --  23..  0 : Interrupt base (hard wired to 16)
 | |
|     --  27.. 24 : #prio bits (1..8)
 | |
|     --
 | |
|     -- XIVE register format:
 | |
|     --
 | |
|     --       31 : input bit (reflects interrupt input)
 | |
|     --       30 : reserved
 | |
|     --       29 : P (mirrors input for now)
 | |
|     --       28 : Q (not implemented in this version)
 | |
|     -- 30 ..    : reserved
 | |
|     -- 19 ..  8 : target (not implemented in this version)
 | |
|     --  7 ..  0 : prio/mask
 | |
| 
 | |
|     signal reg_is_xive   : std_ulogic;
 | |
|     signal reg_is_config : std_ulogic;
 | |
|     signal reg_is_debug  : std_ulogic;
 | |
| 
 | |
| begin
 | |
| 
 | |
|     assert SRC_NUM = 16 report "Fixup address decode with log2";
 | |
| 
 | |
|     reg_is_xive   <= wb_in.adr(9);
 | |
|     reg_is_config <= '1' when wb_in.adr(9 downto 0) = 10x"000" else '0';
 | |
|     reg_is_debug  <= '1' when wb_in.adr(9 downto 0) = 10x"001" else '0';
 | |
| 
 | |
|     -- Register index XX FIXME: figure out bits from SRC_NUM
 | |
|     reg_idx <= to_integer(unsigned(wb_in.adr(3 downto 0)));
 | |
| 
 | |
|     -- Latch interrupt inputs for timing
 | |
|     int_latch: process(clk)
 | |
|     begin
 | |
|         if rising_edge(clk) then
 | |
|             int_level_l <= int_level_in;
 | |
|         end if;
 | |
|     end process;
 | |
| 
 | |
|     -- We don't stall. Acks are sent by the read machine one cycle
 | |
|     -- after a request, but we can handle one access per cycle.
 | |
|     wb_out.stall <= '0';
 | |
|     wb_valid <= wb_in.cyc and wb_in.stb;
 | |
| 
 | |
|     -- Big read mux. This could be replaced by a slower state
 | |
|     -- machine iterating registers instead if timing gets tight.
 | |
|     reg_read: process(clk)
 | |
|         variable be_out : std_ulogic_vector(31 downto 0);
 | |
|     begin
 | |
|         if rising_edge(clk) then
 | |
|             be_out := (others => '0');
 | |
| 
 | |
|             if reg_is_xive = '1' then
 | |
|                 be_out := int_level_l(reg_idx) &
 | |
|                           '0' &
 | |
|                           int_level_l(reg_idx) &
 | |
|                           '0' &
 | |
|                           x"00000" &
 | |
|                           prio_unpack(xives(reg_idx).pri);
 | |
|             elsif reg_is_config = '1' then
 | |
|                 be_out := get_config;
 | |
|             elsif reg_is_debug = '1' then
 | |
|                 be_out := x"00000" & icp_out_next.src & icp_out_next.pri;
 | |
|             end if;
 | |
|             wb_out.dat <= bswap(be_out);
 | |
|             wb_out.ack <= wb_valid;
 | |
|         end if;
 | |
|     end process;
 | |
| 
 | |
|     -- Register write machine
 | |
|     reg_write: process(clk)
 | |
|         variable be_in  : std_ulogic_vector(31 downto 0);
 | |
|     begin
 | |
|         -- Byteswapped input
 | |
|         be_in := bswap(wb_in.dat);
 | |
| 
 | |
|         if rising_edge(clk) then
 | |
|             if rst = '1' then
 | |
|                 for i in 0 to SRC_NUM - 1 loop
 | |
|                     xives(i) <= (pri => pri_masked);
 | |
|                 end loop;
 | |
|             elsif wb_valid = '1' and wb_in.we = '1' then
 | |
|                 if reg_is_xive then
 | |
|                     -- TODO: When adding support for other bits, make sure to
 | |
|                     -- properly implement wb_in.sel to allow partial writes.
 | |
|                     xives(reg_idx).pri <= prio_pack(be_in(7 downto 0));
 | |
|                     report "ICS irq " & integer'image(reg_idx) &
 | |
|                         " set to:" & to_hstring(be_in(7 downto 0));
 | |
|                 end if;
 | |
|             end if;
 | |
|         end if;
 | |
|     end process;
 | |
| 
 | |
|     -- generate interrupt. This is a simple combinational process,
 | |
|     -- potentially wasteful in HW for large number of interrupts.
 | |
|     --
 | |
|     -- could be replaced with iterative state machines and a message
 | |
|     -- system between ICSs' (plural) and ICP  incl. reject etc...
 | |
|     --
 | |
|     irq_gen_sync: process(clk)
 | |
|     begin
 | |
|         if rising_edge(clk) then
 | |
|             icp_out <= icp_out_next;
 | |
|         end if;
 | |
|     end process;
 | |
| 
 | |
|     irq_gen: process(all)
 | |
|         variable max_idx : std_ulogic_vector(SRC_NUM_BITS - 1 downto 0);
 | |
|         variable max_pri : pri_t;
 | |
|         variable pending_pri : pri_vector_t;
 | |
|         variable pending_at_pri : std_ulogic_vector(SRC_NUM - 1 downto 0);
 | |
|     begin
 | |
|         -- 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' 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(to_integer(unsigned(max_idx))) & " pri=" & to_hstring(prio_unpack(max_pri));
 | |
|         end if;
 | |
|         icp_out_next.src <= max_idx;
 | |
|         icp_out_next.pri <= prio_unpack(max_pri);
 | |
|     end process;
 | |
| 
 | |
| end architecture rtl;
 |