From: Benjamin Herrenschmidt Date: Wed, 17 Jun 2020 12:11:58 +0000 (+1000) Subject: xics: Add simple ICS X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=5c2fc47e2c30f1799eed95928578313d9afcb6af;p=microwatt.git xics: Add simple ICS Move the external interrupt generation to a separate module "ICS" (source controller) which a register per source containing currently only the priority control. Signed-off-by: Benjamin Herrenschmidt --- diff --git a/common.vhdl b/common.vhdl index 52222c3..f80593b 100644 --- a/common.vhdl +++ b/common.vhdl @@ -78,6 +78,16 @@ package common is type irq_state_t is (WRITE_SRR0, WRITE_SRR1); + -- For now, fixed 16 sources, make this either a parametric + -- package of some sort or an unconstrainted array. + type ics_to_icp_t is record + -- Level interrupts only, ICS just keeps prsenting the + -- highest priority interrupt. Once handling edge, something + -- smarter involving handshake & reject support will be needed + src : std_ulogic_vector(3 downto 0); + pri : std_ulogic_vector(7 downto 0); + end record; + -- This needs to die... type ctrl_t is record tb: std_ulogic_vector(63 downto 0); diff --git a/include/microwatt_soc.h b/include/microwatt_soc.h index 39820e6..fd83840 100644 --- a/include/microwatt_soc.h +++ b/include/microwatt_soc.h @@ -11,7 +11,8 @@ #define SYSCON_BASE 0xc0000000 /* System control regs */ #define UART_BASE 0xc0002000 /* UART */ -#define XICS_BASE 0xc0004000 /* Interrupt controller */ +#define XICS_ICP_BASE 0xc0004000 /* Interrupt controller */ +#define XICS_ICS_BASE 0xc0005000 /* Interrupt controller */ #define SPI_FCTRL_BASE 0xc0006000 /* SPI flash controller registers */ #define DRAM_CTRL_BASE 0xc8000000 /* LiteDRAM control registers */ #define SPI_FLASH_BASE 0xf0000000 /* SPI Flash memory map */ diff --git a/soc.vhdl b/soc.vhdl index 6cf9a7f..c096ac1 100644 --- a/soc.vhdl +++ b/soc.vhdl @@ -21,6 +21,7 @@ use work.wishbone_types.all; -- 0xc0000000: SYSCON -- 0xc0002000: UART0 -- 0xc0004000: XICS ICP +-- 0xc0005000: XICS ICS -- 0xc0006000: SPI Flash controller -- 0xc8nnnnnn: External IO bus -- 0xf0000000: Flash "ROM" mapping @@ -130,12 +131,14 @@ architecture behaviour of soc is signal wb_spiflash_is_reg : std_ulogic; signal wb_spiflash_is_map : std_ulogic; - -- XICS0 signals: - signal wb_xics0_in : wb_io_master_out; - signal wb_xics0_out : wb_io_slave_out; - signal int_level_in : std_ulogic_vector(15 downto 0); - - signal core_ext_irq : std_ulogic; + -- XICS signals: + signal wb_xics_icp_in : wb_io_master_out; + signal wb_xics_icp_out : wb_io_slave_out; + signal wb_xics_ics_in : wb_io_master_out; + signal wb_xics_ics_out : wb_io_slave_out; + signal int_level_in : std_ulogic_vector(15 downto 0); + signal ics_to_icp : ics_to_icp_t; + signal core_ext_irq : std_ulogic; -- Main memory signals: signal wb_bram_in : wishbone_master_out; @@ -171,7 +174,8 @@ architecture behaviour of soc is -- IO branch split: type slave_io_type is (SLAVE_IO_SYSCON, SLAVE_IO_UART, - SLAVE_IO_ICP_0, + SLAVE_IO_ICP, + SLAVE_IO_ICS, SLAVE_IO_SPI_FLASH_REG, SLAVE_IO_SPI_FLASH_MAP, SLAVE_IO_EXTERNAL, @@ -441,7 +445,8 @@ begin -- IO wishbone slave intercon. -- slave_io_intercon: process(wb_sio_out, wb_syscon_out, wb_uart0_out, - wb_ext_io_out, wb_xics0_out, wb_spiflash_out) + wb_ext_io_out, wb_xics_icp_out, wb_xics_ics_out, + wb_spiflash_out) variable slave_io : slave_io_type; variable match : std_ulogic_vector(31 downto 12); @@ -462,7 +467,9 @@ begin elsif std_match(match, x"C8---") then slave_io := SLAVE_IO_EXTERNAL; elsif std_match(match, x"C0004") then - slave_io := SLAVE_IO_ICP_0; + slave_io := SLAVE_IO_ICP; + elsif std_match(match, x"C0005") then + slave_io := SLAVE_IO_ICS; elsif std_match(match, x"C0006") then slave_io := SLAVE_IO_SPI_FLASH_REG; end if; @@ -474,11 +481,15 @@ begin wb_spiflash_is_reg <= '0'; wb_spiflash_is_map <= '0'; - -- Only give xics 8 bits of wb addr - wb_xics0_in <= wb_sio_out; - wb_xics0_in.adr <= (others => '0'); - wb_xics0_in.adr(7 downto 0) <= wb_sio_out.adr(7 downto 0); - wb_xics0_in.cyc <= '0'; + -- Only give xics 8 bits of wb addr (for now...) + wb_xics_icp_in <= wb_sio_out; + wb_xics_icp_in.adr <= (others => '0'); + wb_xics_icp_in.adr(7 downto 0) <= wb_sio_out.adr(7 downto 0); + wb_xics_icp_in.cyc <= '0'; + wb_xics_ics_in <= wb_sio_out; + wb_xics_ics_in.adr <= (others => '0'); + wb_xics_ics_in.adr(11 downto 0) <= wb_sio_out.adr(11 downto 0); + wb_xics_ics_in.cyc <= '0'; wb_ext_io_in <= wb_sio_out; wb_ext_io_in.cyc <= '0'; @@ -521,9 +532,12 @@ begin when SLAVE_IO_UART => wb_uart0_in.cyc <= wb_sio_out.cyc; wb_sio_in <= wb_uart0_out; - when SLAVE_IO_ICP_0 => - wb_xics0_in.cyc <= wb_sio_out.cyc; - wb_sio_in <= wb_xics0_out; + when SLAVE_IO_ICP => + wb_xics_icp_in.cyc <= wb_sio_out.cyc; + wb_sio_in <= wb_xics_icp_out; + when SLAVE_IO_ICS => + wb_xics_ics_in.cyc <= wb_sio_out.cyc; + wb_sio_in <= wb_xics_ics_out; when SLAVE_IO_SPI_FLASH_MAP => -- Clear top bits so they don't make their way to the -- fash chip. @@ -614,17 +628,27 @@ begin wb_spiflash_out.stall <= wb_spiflash_in.cyc and not wb_spiflash_out.ack; end generate; - xics0: entity work.xics + xics_icp: entity work.xics_icp + port map( + clk => system_clk, + rst => rst_xics, + wb_in => wb_xics_icp_in, + wb_out => wb_xics_icp_out, + ics_in => ics_to_icp, + core_irq_out => core_ext_irq + ); + + xics_ics: entity work.xics_ics generic map( - LEVEL_NUM => 16 + SRC_NUM => 16 ) port map( clk => system_clk, rst => rst_xics, - wb_in => wb_xics0_in, - wb_out => wb_xics0_out, + wb_in => wb_xics_ics_in, + wb_out => wb_xics_ics_out, int_level_in => int_level_in, - core_irq_out => core_ext_irq + icp_out => ics_to_icp ); -- Assign external interrupts diff --git a/tests/test_xics.bin b/tests/test_xics.bin index 327f98f..1e0e7d4 100755 Binary files a/tests/test_xics.bin and b/tests/test_xics.bin differ diff --git a/tests/test_xics.console_out b/tests/test_xics.console_out index 8c7ae53..b93c3ca 100644 --- a/tests/test_xics.console_out +++ b/tests/test_xics.console_out @@ -1,3 +1,4 @@ Test 0:PASS Test 1:PASS Test 2:PASS +Test 3:PASS diff --git a/tests/xics/xics.c b/tests/xics/xics.c index f41c3a4..8a0c13b 100644 --- a/tests/xics/xics.c +++ b/tests/xics/xics.c @@ -74,6 +74,8 @@ void ipi_isr(void) { debug_puts(IPI); isrs_run |= ISR_IPI; + + icp_write8(XICS_MFRR, 0xff); } @@ -109,6 +111,8 @@ struct isr_op isr_table[] = { bool ipi_running; #define ISR "ISR XISR=" +#define PRIO " PRIO=" +#define CPPR " CPPR=" void isr(void) { struct isr_op *op; @@ -117,11 +121,15 @@ void isr(void) assert(!ipi_running); // check we aren't reentrant ipi_running = true; - xirr = xics_read32(XICS_XIRR); // read hardware irq source + xirr = icp_read32(XICS_XIRR); // read hardware irq source #ifdef DEBUG puts(ISR); print_number(xirr & 0xff); + puts(PRIO); + print_number(xirr >> 24); + puts(CPPR); + print_number(icp_read8(XICS_XIRR_POLL)); puts("\n"); #endif @@ -135,7 +143,7 @@ void isr(void) op++; } - xics_write32(XICS_XIRR, xirr); // EOI + icp_write32(XICS_XIRR, xirr); // EOI ipi_running = false; } @@ -143,45 +151,87 @@ void isr(void) /*****************************************/ int xics_test_0(void) +{ + uint32_t v0, v1; + + v0 = ics_read_xive(0); + v1 = ics_read_xive(1); +#ifdef DEBUG + puts("\n"); + puts("xive(0) bfr: "); + print_number(v0); + puts("\n"); + puts("xive(1) bfr: "); + print_number(v1); + puts("\n"); +#endif + assert(v0 = 0xff); + assert(v1 = 0xff); + + ics_write_xive(0xaa, 0); + ics_write_xive(0x55, 1); + v0 = ics_read_xive(0); + v1 = ics_read_xive(1); +#ifdef DEBUG + puts("\n"); + puts("xive(0) aft: "); + print_number(v0); + puts("\n"); + puts("xive(1) aft: "); + print_number(v1); + puts("\n"); +#endif + assert(v0 = 0xaa); + assert(v1 = 0x55); + + ics_write_xive(0xff, 0); + ics_write_xive(0xff, 1); + return 0; +} + +int xics_test_1(void) { // setup - xics_write8(XICS_XIRR, 0x00); // mask all interrupts + icp_write8(XICS_XIRR, 0x00); // mask all interrupts isrs_run = 0; - xics_write8(XICS_XIRR, 0x00); // mask all interrupts + icp_write8(XICS_XIRR, 0x00); // mask all interrupts // trigger two interrupts potato_uart_irq_en(); // cause 0x500 interrupt - xics_write8(XICS_MFRR, 0x05); // cause 0x500 interrupt + ics_write_xive(0x80, 0); + icp_write8(XICS_MFRR, 0x05); // cause 0x500 interrupt // still masked, so shouldn't happen yet delay(); assert(isrs_run == 0); // unmask IPI only - xics_write8(XICS_XIRR, 0x40); + icp_write8(XICS_XIRR, 0x40); delay(); assert(isrs_run == ISR_IPI); // unmask UART - xics_write8(XICS_XIRR, 0xc0); + icp_write8(XICS_XIRR, 0xc0); delay(); assert(isrs_run == (ISR_IPI | ISR_UART)); // cleanup - xics_write8(XICS_XIRR, 0x00); // mask all interrupts + icp_write8(XICS_XIRR, 0x00); // mask all interrupts + potato_uart_irq_dis(); + ics_write_xive(0, 0); isrs_run = 0; return 0; } -int xics_test_1(void) +int xics_test_2(void) { // setup - xics_write8(XICS_XIRR, 0x00); // mask all interrupts + icp_write8(XICS_XIRR, 0x00); // mask all interrupts isrs_run = 0; - xics_write8(XICS_XIRR, 0xff); // allow all interrupts + icp_write8(XICS_XIRR, 0xff); // allow all interrupts // should be none pending delay(); @@ -189,13 +239,14 @@ int xics_test_1(void) // trigger both potato_uart_irq_en(); // cause 0x500 interrupt - xics_write8(XICS_MFRR, 0x05); // cause 0x500 interrupt + icp_write8(XICS_MFRR, 0x05); // cause 0x500 interrupt delay(); assert(isrs_run == (ISR_IPI | ISR_UART)); // cleanup - xics_write8(XICS_XIRR, 0x00); // mask all interrupts + icp_write8(XICS_XIRR, 0x00); // mask all interrupts + potato_uart_irq_dis(); isrs_run = 0; return 0; @@ -206,19 +257,19 @@ void mtmsrd(uint64_t val) __asm__ volatile("mtmsrd %0" : : "r" (val)); } -int xics_test_2(void) +int xics_test_3(void) { // setup - xics_write8(XICS_XIRR, 0x00); // mask all interrupts + icp_write8(XICS_XIRR, 0x00); // mask all interrupts isrs_run = 0; // trigger interrupts with MSR[EE]=0 and show they are not run mtmsrd(0x9000000000000003); // EE off - xics_write8(XICS_XIRR, 0xff); // allow all interrupts + icp_write8(XICS_XIRR, 0xff); // allow all interrupts // trigger an IPI - xics_write8(XICS_MFRR, 0x05); // cause 0x500 interrupt + icp_write8(XICS_MFRR, 0x05); // cause 0x500 interrupt delay(); assert(isrs_run == 0); @@ -228,7 +279,7 @@ int xics_test_2(void) assert(isrs_run == ISR_IPI); // cleanup - xics_write8(XICS_XIRR, 0x00); // mask all interrupts + icp_write8(XICS_XIRR, 0x00); // mask all interrupts isrs_run = 0; return 0; @@ -242,6 +293,7 @@ int (*tests[])(void) = { xics_test_0, xics_test_1, xics_test_2, + xics_test_3, NULL }; diff --git a/tests/xics/xics.h b/tests/xics/xics.h index ce83ab2..6936e33 100644 --- a/tests/xics/xics.h +++ b/tests/xics/xics.h @@ -9,23 +9,33 @@ #define bswap32(x) (uint32_t)__builtin_bswap32((uint32_t)(x)) -uint8_t xics_read8(int offset) +uint8_t icp_read8(int offset) { - return readb(XICS_BASE + offset); + return readb(XICS_ICP_BASE + offset); } -void xics_write8(int offset, uint8_t val) +void icp_write8(int offset, uint8_t val) { - writeb(val, XICS_BASE + offset); + writeb(val, XICS_ICP_BASE + offset); } -uint32_t xics_read32(int offset) +uint32_t icp_read32(int offset) { - return bswap32(readl(XICS_BASE + offset)); + return bswap32(readl(XICS_ICP_BASE + offset)); } -void xics_write32(int offset, uint32_t val) +static inline void icp_write32(int offset, uint32_t val) { - writel(bswap32(val), XICS_BASE + offset); + writel(bswap32(val), XICS_ICP_BASE + offset); +} + +uint32_t ics_read_xive(int irq) +{ + return bswap32(readl(XICS_ICS_BASE + 0x800 + (irq << 2))); +} + +void ics_write_xive(uint32_t val, int irq) +{ + writel(bswap32(val), XICS_ICS_BASE + 0x800 + (irq << 2)); } diff --git a/xics.vhdl b/xics.vhdl index a7e030b..9f97fec 100644 --- a/xics.vhdl +++ b/xics.vhdl @@ -1,11 +1,13 @@ -- -- This is a simple XICS compliant interrupt controller. This is a --- Presenter (ICP) and Source (ICS) in a single unit with no routing --- layer. +-- Presenter (ICP) and Source (ICS) in two small units directly +-- connected to each other with no routing layer. -- --- The sources have a fixed IRQ priority set by HW_PRIORITY. The --- source id starts at 16 for int_level_in(0) and go up from --- there (ie int_level_in(1) is source id 17). +-- 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 @@ -22,38 +24,31 @@ library work; use work.common.all; use work.wishbone_types.all; -entity xics is - generic ( - LEVEL_NUM : positive := 16 - ); +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; - - int_level_in : in std_ulogic_vector(LEVEL_NUM - 1 downto 0); + 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; +end xics_icp; -architecture behaviour of xics is +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); - pending_priority : std_ulogic_vector(7 downto 0); mfrr : std_ulogic_vector(7 downto 0); - mfrr_pending : std_ulogic; 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_pending => '0', - mfrr => x"ff", -- no IPI on reset + mfrr => x"ff", -- mask everything on reset irq => '0', others => (others => '0')); @@ -74,18 +69,19 @@ begin 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 - core_irq_out <= r.irq; comb : process(all) variable v : reg_internal_t; variable xirr_accept_rd : std_ulogic; - variable irq_eoi : 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); @@ -99,13 +95,14 @@ begin 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'; - irq_eoi := '0'; be_in := bswap(wb_in.dat); be_out := (others => '0'); @@ -122,7 +119,6 @@ begin 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); - irq_eoi := '1'; elsif wb_in.sel = x"1" then -- 1 byte report "ICP XIRR write byte (CPPR):" & to_hstring(be_in(31 downto 24)); else @@ -130,7 +126,6 @@ begin end if; when MFRR => v.mfrr := be_in(31 downto 24); - v.mfrr_pending := '1'; 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 @@ -161,49 +156,40 @@ begin end if; end if; - -- generate interrupt - if r.irq = '0' then - -- Here we just present any interrupt that's valid and - -- below cppr. For ordering, we ignore hardware - -- priorities. - if unsigned(HW_PRIORITY) < unsigned(r.cppr) then -- - -- lower HW sources are higher priority - for i in LEVEL_NUM - 1 downto 0 loop - if int_level_in(i) = '1' then - v.irq := '1'; - v.xisr := std_ulogic_vector(to_unsigned(16 + i, 24)); - v.pending_priority := HW_PRIORITY; -- hardware HW IRQs - end if; - end loop; - end if; + pending_priority := x"ff"; + v.xisr := x"000000"; + v.irq := '0'; - -- Do mfrr as a higher priority so mfrr_pending is cleared - if unsigned(r.mfrr) < unsigned(r.cppr) then -- - report "XICS: MFRR INTERRUPT"; - -- IPI - if r.mfrr_pending = '1' then - v.irq := '1'; - v.xisr := x"000002"; -- special XICS MFRR IRQ source number - v.pending_priority := r.mfrr; - v.mfrr_pending := '0'; - end if; - end if; - end if; + 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: ACCEPT" & - " cppr:" & to_hstring(r.cppr) & - " xisr:" & to_hstring(r.xisr) & - " mfrr:" & to_hstring(r.mfrr); - v.cppr := r.pending_priority; + 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 irq_eoi = '1' then - v.irq := '0'; - end if; + 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; @@ -214,3 +200,192 @@ begin 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.wishbone_types.all; + +entity xics_ics is + generic ( + SRC_NUM : positive := 16 + ); + 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 + + subtype pri_t is std_ulogic_vector(7 downto 0); + type xive_t is record + pri : pri_t; + end record; + 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; + + -- Register map + -- 0 : Config (currently hard wired base irq#) + -- 4 : Debug/diagnostics + -- 800 : XIVE0 + -- 804 : XIVE1 ... + -- + -- Config register format: + -- + -- 23.. 0 : Interrupt base (hard wired to 16) + -- + -- 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(11); + reg_is_config <= '1' when wb_in.adr(11 downto 0) = x"000" else '0'; + reg_is_debug <= '1' when wb_in.adr(11 downto 0) = x"004" else '0'; + + -- Register index XX FIXME: figure out bits from SRC_NUM + reg_idx <= to_integer(unsigned(wb_in.adr(5 downto 2))); + + -- 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" & + xives(reg_idx).pri; + elsif reg_is_config = '1' then + be_out := std_ulogic_vector(to_unsigned(SRC_NUM, 32)); + 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 => x"ff"); + 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 <= 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 wasteul 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 : integer range 0 to SRC_NUM-1; + 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 : integer range 0 to 255; + variable b_i : integer range 0 to 255; + begin + a_i := to_integer(unsigned(a)); + b_i := to_integer(unsigned(b)); + return a < b; + end function; + begin + -- XXX FIXME: Use a tree + max_pri := x"ff"; + max_idx := 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; + end if; + end loop; + if max_pri /= x"ff" then + report "MFI: " & integer'image(max_idx) & " pri=" & to_hstring(max_pri); + end if; + icp_out_next.src <= std_ulogic_vector(to_unsigned(max_idx, 4)); + icp_out_next.pri <= max_pri; + end process; + +end architecture rtl;