From 311b653d806ac73c8e0fc5009a2cc10f5e8a6fb3 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 17 Jun 2020 14:00:04 +1000 Subject: [PATCH 1/6] tests: Fix Makefile.test to not allow host includes xics was including the host limits.h for example Signed-off-by: Benjamin Herrenschmidt --- tests/Makefile.test | 2 +- tests/xics/xics.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/Makefile.test b/tests/Makefile.test index 3b800d0..ff0044d 100644 --- a/tests/Makefile.test +++ b/tests/Makefile.test @@ -9,7 +9,7 @@ CC = $(CROSS_COMPILE)gcc LD = $(CROSS_COMPILE)ld OBJCOPY = $(CROSS_COMPILE)objcopy -CFLAGS = -Os -g -Wall -std=c99 -msoft-float -mno-string -mno-multiple -mno-vsx -mno-altivec -mlittle-endian -fno-stack-protector -mstrict-align -ffreestanding -fdata-sections -ffunction-sections -I ../../include +CFLAGS = -Os -g -Wall -std=c99 -nostdinc -msoft-float -mno-string -mno-multiple -mno-vsx -mno-altivec -mlittle-endian -fno-stack-protector -mstrict-align -ffreestanding -fdata-sections -ffunction-sections -I ../../include -isystem $(shell $(CC) -print-file-name=include) ASFLAGS = $(CFLAGS) LDFLAGS = -T powerpc.lds diff --git a/tests/xics/xics.c b/tests/xics/xics.c index a2db3a5..46f9a01 100644 --- a/tests/xics/xics.c +++ b/tests/xics/xics.c @@ -1,7 +1,6 @@ #include #include #include -#include #include "console.h" #include "xics.h" From 0fa14f6decff1abd43f2bff87f10ad59c5aef754 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 17 Jun 2020 21:51:16 +1000 Subject: [PATCH 2/6] xics: ICP should be big endian ! That's how Linux expects it. This also simplifies the register access implementation since the bit fields now align properly regardless of the access size. Signed-off-by: Benjamin Herrenschmidt --- tests/xics/xics.h | 8 ++++-- xics.vhdl | 72 +++++++++++++++++++++++------------------------ 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/tests/xics/xics.h b/tests/xics/xics.h index bbb1f99..ce83ab2 100644 --- a/tests/xics/xics.h +++ b/tests/xics/xics.h @@ -1,5 +1,4 @@ #include - #include "microwatt_soc.h" #include "io.h" @@ -8,6 +7,8 @@ #define XICS_RESV 0x8 #define XICS_MFRR 0xC +#define bswap32(x) (uint32_t)__builtin_bswap32((uint32_t)(x)) + uint8_t xics_read8(int offset) { return readb(XICS_BASE + offset); @@ -20,10 +21,11 @@ void xics_write8(int offset, uint8_t val) uint32_t xics_read32(int offset) { - return readl(XICS_BASE + offset); + return bswap32(readl(XICS_BASE + offset)); } void xics_write32(int offset, uint32_t val) { - writel(val, XICS_BASE + offset); + writel(bswap32(val), XICS_BASE + offset); } + diff --git a/xics.vhdl b/xics.vhdl index eb41c32..413fdd9 100644 --- a/xics.vhdl +++ b/xics.vhdl @@ -86,6 +86,19 @@ begin 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); + 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); begin v := r; @@ -94,72 +107,55 @@ begin xirr_accept_rd := '0'; irq_eoi := '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(7 downto 0) is when XIRR_POLL => - report "XICS XIRR_POLL write"; - if wb_in.sel = x"f" then -- 4 bytes - v.cppr := wb_in.dat(31 downto 24); - elsif wb_in.sel = x"1" then -- 1 byte - v.cppr := wb_in.dat(7 downto 0); - end if; + 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 "XICS XIRR write word:" & to_hstring(wb_in.dat); - v.cppr := wb_in.dat(31 downto 24); + report "ICP XIRR write word (EOI) :" & to_hstring(be_in); irq_eoi := '1'; elsif wb_in.sel = x"1" then -- 1 byte - report "XICS XIRR write byte:" & to_hstring(wb_in.dat(7 downto 0)); - v.cppr := wb_in.dat(7 downto 0); + report "ICP XIRR write byte (CPPR):" & to_hstring(be_in(31 downto 24)); else - report "XICS XIRR UNSUPPORTED write ! sel=" & to_hstring(wb_in.sel); + report "ICP XIRR UNSUPPORTED write ! sel=" & to_hstring(wb_in.sel); 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 "XICS MFRR write word:" & to_hstring(wb_in.dat); - v.mfrr_pending := '1'; - v.mfrr := wb_in.dat(31 downto 24); + report "ICP MFRR write word:" & to_hstring(be_in); elsif wb_in.sel = x"1" then -- 1 byte - report "XICS MFRR write byte:" & to_hstring(wb_in.dat(7 downto 0)); - v.mfrr_pending := '1'; - v.mfrr := wb_in.dat(7 downto 0); + report "ICP MFRR write byte:" & to_hstring(be_in(31 downto 24)); else - report "XICS MFRR UNSUPPORTED write ! sel=" & to_hstring(wb_in.sel); + report "ICP MFRR UNSUPPORTED write ! sel=" & to_hstring(wb_in.sel); end if; when others => end case; else -- read - v.wb_rd_data := (others => '0'); case wb_in.adr(7 downto 0) is when XIRR_POLL => - report "XICS XIRR_POLL read"; - if wb_in.sel = x"f" then - v.wb_rd_data(23 downto 0) := r.xisr; - v.wb_rd_data(31 downto 24) := r.cppr; - elsif wb_in.sel = x"1" then - v.wb_rd_data(7 downto 0) := r.cppr; - end if; + report "ICP XIRR_POLL read"; + be_out := r.cppr & r.xisr; when XIRR => - report "XICS XIRR read"; + report "ICP XIRR read"; + be_out := r.cppr & r.xisr; if wb_in.sel = x"f" then - v.wb_rd_data(23 downto 0) := r.xisr; - v.wb_rd_data(31 downto 24) := r.cppr; xirr_accept_rd := '1'; - elsif wb_in.sel = x"1" then - v.wb_rd_data(7 downto 0) := r.cppr; end if; when MFRR => - report "XICS MFRR read"; - if wb_in.sel = x"f" then -- 4 bytes - v.wb_rd_data(31 downto 24) := r.mfrr; - elsif wb_in.sel = x"1" then -- 1 byte - v.wb_rd_data( 7 downto 0) := r.mfrr; - end if; + report "ICP MFRR read"; + be_out(31 downto 24) := r.mfrr; when others => end case; end if; @@ -203,6 +199,8 @@ begin v.cppr := r.pending_priority; end if; + v.wb_rd_data := bswap(be_out); + if irq_eoi = '1' then v.irq := '0'; end if; From 0b82024b011c77d3e7c2374b17855031db91d650 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 17 Jun 2020 22:06:28 +1000 Subject: [PATCH 3/6] tests/xics: Ensure no compiler optimisations in delay() In case it would be tempted to "read ahead" the delay function Signed-off-by: Benjamin Herrenschmidt --- tests/xics/xics.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/xics/xics.c b/tests/xics/xics.c index 46f9a01..f41c3a4 100644 --- a/tests/xics/xics.c +++ b/tests/xics/xics.c @@ -12,8 +12,8 @@ void delay(void) { static volatile int i; - for (i = 0; i < 10; ++i) - ; + for (i = 0; i < 16; ++i) + __asm__ volatile("" : : : "memory"); } void print_number(unsigned int i) // only for i = 0-999 From 80801683275d66fd9ccb1b8a165bec2c49c2d640 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 17 Jun 2020 22:07:33 +1000 Subject: [PATCH 4/6] xics/icp: MFRR starts at 0xff not 0x00 Signed-off-by: Benjamin Herrenschmidt --- xics.vhdl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xics.vhdl b/xics.vhdl index 413fdd9..a7e030b 100644 --- a/xics.vhdl +++ b/xics.vhdl @@ -53,7 +53,7 @@ architecture behaviour of xics is constant reg_internal_init : reg_internal_t := (wb_ack => '0', mfrr_pending => '0', - mfrr => x"00", -- mask everything on reset + mfrr => x"ff", -- no IPI on reset irq => '0', others => (others => '0')); From 5c2fc47e2c30f1799eed95928578313d9afcb6af Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 17 Jun 2020 22:11:58 +1000 Subject: [PATCH 5/6] 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 --- common.vhdl | 10 ++ include/microwatt_soc.h | 3 +- soc.vhdl | 68 ++++++--- tests/test_xics.bin | Bin 12384 -> 12392 bytes tests/test_xics.console_out | 1 + tests/xics/xics.c | 88 ++++++++--- tests/xics/xics.h | 26 +++- xics.vhdl | 293 ++++++++++++++++++++++++++++-------- 8 files changed, 381 insertions(+), 108 deletions(-) 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 327f98f64e2bb62ea1ca48f98456b1dae59799f6..1e0e7d4efb6797d25fb75383564b2575119dd3e2 100755 GIT binary patch literal 12392 zcmeHMeP~C@dL(S49 ze@L@5J&pWP7sH&v!ZO;dUWcheTN+rQ|E!Z4gk&4cWo0z9EB4AjA8aI)cKWD$_dEAp z#m_Hsw!uIfc>?>Md)|5XoZs)9`y~-s5u&NsrIx9rps8a*OvPq>XVWyjqQ~=NK5Hs= zRI}1VdJmD`ywu`Ppgl@aze$cU*!JH0d5p=lzuDSPO~jww^1t`bW8i;}fkxM+=f8n9 z(cF5+uT71vzXoPsy7urlIs=XM-{}0`>HZs=eeZ1A==y7T{;z)FFSn9bZl}I-2gS-d zWy=rKOnC>*mxHuaHb|@NCVyp!`YNLotC*Cn9H*Je37W4YXsKe6R(*l|)oJRh{)A%H zS14P3jb^HE(R}qCTB_zqt6e64ZGrk~A5g5eNZDGRW@^`HzE-BC+HBMPDQeZqvrVlj zL$(`I7TT{9rE;?WK#DeK<$2*R6PdVw#c}_NRx=zJaFWJBM%$};3xn`0XPc4Q2>qta1?-}031c&C;~?jIEuhg1dbwb6oI1%93|i= z0Y?cqO2AP9juLQ`fTLuz#`3ei({@7*ttIejNE=N)j_d5$N3=9-bN?? zPWOLVpe_aKQlKsc>QbOC1?o~P)!H#9bh7rtrUaW3Y)Y^xL7#_RY1Y@8f^7-5CAMRk zpK+g`u}&kShu-QBf@kxD)gL12Drgjlp{-EPhzOyF?lJ~qIt?UWZ>H3RLwcIubLQn% zV>GKV+_pR>jjoK>^^~5boE~W&4Zuf0`smtEX}UlW_Sba?@`#cTP+IDqQZ3tw6+8J1 zHh>az`>n^wbC`Ks!dwls$-9LKEE2m;{vh+i956pkH)GH&7*j6ZLq;4v;t9#gW;4~h zK0fUBd6V51OLERb-1y`=BV+_ZE7bARU(y}LS}U?1*rA8Q{Qrd$9#{-^Jq z9`4QYyQh0L_m2(H=;>hZuCXDVPD>nJ58Xbu_--kbu(qB->}JBQmF!n&vRkK@_kpu4 z>W1s*OkRk=8~7kNxhFD0k1mVBi{SsK;l*g;f$yh?tT6pn#M{UWpRK){*+4IC*J*Cz zUOL}LbfNn$I&Toob$6h@gVMoYw!~9^weN){#t!|d@~h0*4@E|!o=_W2;@zI)(6v8j zbozKgCo%E5IByIgw~Jrgh)c89x#B`y@|Yj0*u>KN83!Gaj zb^XN_RYzRgmcF{a`90{@VNCZtdgDuuCt6@^qrfJ_vppT-@!&p%?J?BxvKspjkmq5~ zx`NWN{latc;MbHr_jSC#QT63_d+=?*H{){7;YXBjjpnvHeZQy1W%%J?=z}j_npJ~;r9JD^3S+^F5e^SUG}|B$yXs?fj#T4ueW)w+u%(S1~f?<#jgZzeApfJ?jce$2=@@{vEu?{MRw!yWLI7cC8u=s2JS2xT4-; ze@P{O205Z)IOdAsoTJY%Osamp4sMl;x#s%%yPEUEmH#g1G;KsKwG1SEG=T5OjkGfd zYw>b-?bdViqKcV$i7ELW%?xgXJ?r)<9p~m6a+7uRnV08PU-B}oBd$QwX9W5>Ny!v@9_A4^Vyu;2cH*V&$_EhSNFNm<#Sm*zg;dkKhC=U;2&$}VBCIY9=CoT zo{?wX$RuGOaIPbh$|uJcQgRaVGIGnhZH|~PgQuq*edcM8>T}E*&(mBk+H8CrCyJco zb)Dx>*XOxRZ|kFh4gWr(9v7n!qXB@+E#yOD7Fz2RRYa-`*J{N=SB*Ny(-ljO#B2Vy@u z7)V=IKX@Mq2@#O%YY}U?``lFLjmO8h1!1q*wmsJ4e`U{HB8Gq4zP)aXcyTs*J}-V? zSk|++|HXq`zr{7qjqW&|~Am1^I2lBQ}V}W zK==8NUm834U2Z5b7C(99)UlJNjy&S(f6LYXw(JkXREM;yZ_nN~v_pLd4|2=nd=sm| z!vP(dolg!QVT+L`clz7d-1~S8cno+9cno+9cno+9cno+9cno+9cno+9cno+9cno+9 r{9iC&w%cLKuiPN+zT56AHT|M4tbt}hJwJSpZ7{vbZkz90{lY&1=`VvC literal 12384 zcmeHMeP~9}x?1N^Bop^0P9~4sBwE9SP_d8El z_Os$ND{N?x_u%Q?d+xdCoZs)n)4Qz-uwh4VU1x-yG;m~qBLf^6;K%?+1~@XnkpYejaAbia z3mjSC$O1rNF!tn3n?cQea++#bPs_36+eU(8)q43!N->)0Pk&s9G?MObp!KasKKNbYK{^nEt@br^K+|q)!1HQ^Iwes!yffG0x{j#AxQ?Nj1)xhm3#vQ3B(Jd|Ce8I+<_SsKMAZu)*WY{ciy) zQ7}#9X@3fsgm}91&`89)R$}-%>E1s`8^=+fhd#?rOBw5*hyHa-dkXEpmHqO!E%?^p zo8!0o-XVP>FRfPJN99=dvrX1dpdOaKAGP>m-@}$R`~IeEo8Q&oJ1Oq;ebMAQn08k9 z{*oNazW-cX;_h|SuS1_@zm>8w?)NNh_B}7#W#62yO57~#K+cge+`;^fq9ylAW$sm( zdtMJTpw(2Bx7D{i7|;7w^`a8vro1;W(Ch8 zC6hsywh$o-9OX5cpPjakk_q+!+(99_aBmC}*4INLDz7M!`u z{}rH~qNEydX`V2z1!k;|ajdFOH`kQF(b11ZUo#2+;PO7F7{R3HNehtL8~)jDPdsXLf$v_Q z5FT?4&0uf68)s}AHq)ZCE|>JN=H5%6d4&J}SiiZfi&*jeKAXasq2&8%yvIbmJO?s& zIggdkHsx5Yw(6y=&9%b=jN7#H33z#q@HvFfzCyg?FP}jay82V1&F#&YujPHM<)-Ls zq=#DheDqn)Kk~)78bf&jH{#(-F)-hw>%NCGmhSycOB?nwKCd0vp#ay7Wj#|lIvm;m z6+ES_9yPRkch9bYJ>Ljz+y2C^9(SY43Iluhk8*{jjVSw}#pUq+AK({$Fn-S{l=gH~ zw7*)>{u(u#5(N0%E^uFLS2>qz1 ziMne;knt4k&U*BH=VKwhze1;Pg^q^4U*&{2xI*XgB^@u!52DXi5MqdRx;eh1buZPfh_Sm# zcWiHLqAoMae_W~!eWt|Aag6D}EPN0{`C=a5kg~tBz_e$CmbUtI?Y0+qof{SKBm0t=r(!Y zQo?CMG{>KW%Dd3zarR|1U^8GdU^8GdU^8GdU^8GdU^8GdU^8GdU^8GdU^DQ)V?dW5 gjPR+_yw1qBENHS^xl0J6pDT6jlOL '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; From bb54af59de6c71261e4893ba7a59305c415b63ce Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Mon, 22 Jun 2020 23:38:34 +1000 Subject: [PATCH 6/6] xics: Add support for reduced priority field size This makes the ICS support less than the 8 architected bits and sets the soc to use 3 bits by default. All the supported bits set translates to "masked" (and will read back at 0xff), any small value is used as-is. Linux doesn't use priorities above 5, so this is a way to save silicon. The number of supported priority bits is exposed to the OS via the config register. Signed-off-by: Benjamin Herrenschmidt --- soc.vhdl | 3 +- tests/test_xics.bin | Bin 12392 -> 12392 bytes tests/xics/xics.c | 24 ++++++++------ xics.vhdl | 75 ++++++++++++++++++++++++++++++++------------ 4 files changed, 71 insertions(+), 31 deletions(-) diff --git a/soc.vhdl b/soc.vhdl index c096ac1..c3b47bc 100644 --- a/soc.vhdl +++ b/soc.vhdl @@ -640,7 +640,8 @@ begin xics_ics: entity work.xics_ics generic map( - SRC_NUM => 16 + SRC_NUM => 16, + PRIO_BITS => 3 ) port map( clk => system_clk, diff --git a/tests/test_xics.bin b/tests/test_xics.bin index 1e0e7d4efb6797d25fb75383564b2575119dd3e2..8ad9b3a58ee372b9966f84e7b065a6e9f1a539af 100755 GIT binary patch delta 715 zcmaEn@FHPC2&2lz&}?4D8JiFCS}^l+G1#tQVW`mxXQ*Xhoy;bvCj#b%In;t_KZRNb zh6Ibr1ws;=0|Z+b8C54=6)I=EFxgi)MS@MimLb9`QGvm$5@H5W@c{>0mdPiD)fqJ= ze-swiybRQQ@ZW!LD8iWZSUd)C#p30j+g|K&(FzK5>k@|#A|?d z_lk>aiZdoysQ!l7?aP9&ivg&w5s7aw`K-7xP!FSoxTZ8v&&FR6JvW(A_3)zTag;D; zJTbXaLYgsl@>B_Vp#H5A`hqurZj40IXE6Dzgg2)~ZngD|oG5F9$)1w)8FePVlr(32 zI9XB3nNeVJs+6;&1kgoO86mbYGW@rm@c+LzC zU<)Io>Ex$E<%|y|#|oziH7M9JM0h1CFnCo$^e9Zu7nhv;Mp%SVVltzMxaN7F)`S24 zds_kNm+TB43_!E~|4*{Hkdtg7%Yoo4Om-9zW^|Z*QABuhh6qS^t%$hY6`(FxsP0@e z-3yWU3c1M^6QO(tCZHV$9BdE#pL|I~O;Q3V6$+ICIf50+zByS?R9q0`3{_-V{ zE24R+mVrUR(tttH0u&Am4E8@SOr9v35AvXxxaK>c*ug(g53(XW^Z?{RHUuB&K{02r zYfeH{fm~AwWq+MKS4>Ls zGbUK5{)X7?%Yv}$1JImCB)-DryW++`J%SSAn&Ln`8-GFc++;@8!;7LPP{N$?#^g>3 zX~x{iOC{ui`j1NJ3!VU)9f_n*VKS?vH>X5ywe^XdC~Jkuk&^QnZ6^PeG-v!c*-*-v zkzsPFl(QrU&?i$FA+|9x{I{O)|GzgV83;_iC?(Dqv-zdeWJbmZn`>p7g(o%$OfnE) a^q4GYV6G4&42(=5m>>$FS;QeUj06CZDb4@@ diff --git a/tests/xics/xics.c b/tests/xics/xics.c index 8a0c13b..a867744 100644 --- a/tests/xics/xics.c +++ b/tests/xics/xics.c @@ -168,8 +168,8 @@ int xics_test_0(void) assert(v0 = 0xff); assert(v1 = 0xff); - ics_write_xive(0xaa, 0); - ics_write_xive(0x55, 1); + ics_write_xive(0xa, 0); + ics_write_xive(0x5, 1); v0 = ics_read_xive(0); v1 = ics_read_xive(1); #ifdef DEBUG @@ -181,11 +181,15 @@ int xics_test_0(void) print_number(v1); puts("\n"); #endif - assert(v0 = 0xaa); - assert(v1 = 0x55); + assert(v0 = 0xa); + assert(v1 = 0x5); ics_write_xive(0xff, 0); ics_write_xive(0xff, 1); + v0 = ics_read_xive(0); + v1 = ics_read_xive(1); + assert(v0 = 0xff); + assert(v1 = 0xff); return 0; } @@ -198,28 +202,28 @@ int xics_test_1(void) icp_write8(XICS_XIRR, 0x00); // mask all interrupts // trigger two interrupts - potato_uart_irq_en(); // cause 0x500 interrupt - ics_write_xive(0x80, 0); - icp_write8(XICS_MFRR, 0x05); // cause 0x500 interrupt + potato_uart_irq_en(); // cause serial interrupt + ics_write_xive(0x6, 0); // set source to prio 6 + icp_write8(XICS_MFRR, 0x04); // cause ipi interrupt at prio 5 // still masked, so shouldn't happen yet delay(); assert(isrs_run == 0); // unmask IPI only - icp_write8(XICS_XIRR, 0x40); + icp_write8(XICS_XIRR, 0x6); delay(); assert(isrs_run == ISR_IPI); // unmask UART - icp_write8(XICS_XIRR, 0xc0); + icp_write8(XICS_XIRR, 0x7); delay(); assert(isrs_run == (ISR_IPI | ISR_UART)); // cleanup icp_write8(XICS_XIRR, 0x00); // mask all interrupts potato_uart_irq_dis(); - ics_write_xive(0, 0); + ics_write_xive(0, 0xff); isrs_run = 0; return 0; diff --git a/xics.vhdl b/xics.vhdl index 9f97fec..b644051 100644 --- a/xics.vhdl +++ b/xics.vhdl @@ -211,7 +211,8 @@ use work.wishbone_types.all; entity xics_ics is generic ( - SRC_NUM : positive := 16 + SRC_NUM : integer range 1 to 256 := 16; + PRIO_BITS : integer range 1 to 8 := 8 ); port ( clk : in std_logic; @@ -227,10 +228,12 @@ end xics_ics; architecture rtl of xics_ics is - subtype pri_t is std_ulogic_vector(7 downto 0); + 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'); + type xive_array_t is array(0 to SRC_NUM-1) of xive_t; signal xives : xive_array_t; @@ -239,7 +242,7 @@ architecture rtl of xics_ics is 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 + 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); @@ -249,8 +252,35 @@ architecture rtl of xics_ics is return r; end function; - -- Register map - -- 0 : Config (currently hard wired base irq#) + 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 + begin + return pri8(PRIO_BITS-1 downto 0); + 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; + + +-- Register map + -- 0 : Config -- 4 : Debug/diagnostics -- 800 : XIVE0 -- 804 : XIVE1 ... @@ -258,6 +288,7 @@ architecture rtl of xics_ics is -- Config register format: -- -- 23.. 0 : Interrupt base (hard wired to 16) + -- 27.. 24 : #prio bits (1..8) -- -- XIVE register format: -- @@ -311,9 +342,9 @@ begin int_level_l(reg_idx) & '0' & x"00000" & - xives(reg_idx).pri; + prio_unpack(xives(reg_idx).pri); elsif reg_is_config = '1' then - be_out := std_ulogic_vector(to_unsigned(SRC_NUM, 32)); + be_out := get_config; elsif reg_is_debug = '1' then be_out := x"00000" & icp_out_next.src & icp_out_next.pri; end if; @@ -332,21 +363,22 @@ begin if rising_edge(clk) then if rst = '1' then for i in 0 to SRC_NUM - 1 loop - xives(i) <= (pri => x"ff"); + 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 <= be_in(7 downto 0); - report "ICS irq " & integer'image(reg_idx) & " set to:" & to_hstring(be_in(7 downto 0)); + 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 wasteul in HW for large number of interrupts. + -- 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... @@ -364,16 +396,19 @@ begin -- 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; + variable a_i : unsigned(PRIO_BITS-1 downto 0); + variable b_i : unsigned(PRIO_BITS-1 downto 0); begin - a_i := to_integer(unsigned(a)); - b_i := to_integer(unsigned(b)); - return a < b; + 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; begin -- XXX FIXME: Use a tree - max_pri := x"ff"; + max_pri := pri_masked; 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 @@ -381,11 +416,11 @@ begin max_idx := i; end if; end loop; - if max_pri /= x"ff" then - report "MFI: " & integer'image(max_idx) & " pri=" & to_hstring(max_pri); + if max_pri /= pri_masked then + report "MFI: " & integer'image(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.pri <= max_pri; + icp_out_next.pri <= prio_unpack(max_pri); end process; end architecture rtl;