diff --git a/Makefile b/Makefile index a13fdcc..ed74176 100644 --- a/Makefile +++ b/Makefile @@ -70,7 +70,8 @@ rotator.o: common.o rotator_tb.o: common.o glibc_random.o ppc_fx_insns.o insn_helpers.o rotator.o sim_console.o: sim_uart.o: wishbone_types.o sim_console.o -soc.o: common.o wishbone_types.o core.o wishbone_arbiter.o sim_uart.o wishbone_bram_wrapper.o dmi_dtm_xilinx.o wishbone_debug_master.o +xics.o: wishbone_types.o common.o +soc.o: common.o wishbone_types.o core.o wishbone_arbiter.o sim_uart.o wishbone_bram_wrapper.o dmi_dtm_xilinx.o wishbone_debug_master.o xics.o wishbone_arbiter.o: wishbone_types.o wishbone_types.o: writeback.o: common.o crhelpers.o diff --git a/common.vhdl b/common.vhdl index 61252bd..ed97e0c 100644 --- a/common.vhdl +++ b/common.vhdl @@ -316,6 +316,11 @@ package common is constant WritebackToCrFileInit : WritebackToCrFileType := (write_cr_enable => '0', write_xerc_enable => '0', write_xerc_data => xerc_init, others => (others => '0')); + + type XicsToExecute1Type is record + irq : std_ulogic; + end record; + end common; package body common is diff --git a/core.vhdl b/core.vhdl index d535f7a..0e60905 100644 --- a/core.vhdl +++ b/core.vhdl @@ -29,6 +29,8 @@ entity core is dmi_wr : in std_ulogic; dmi_ack : out std_ulogic; + xics_in : in XicsToExecute1Type; + terminated_out : out std_logic ); end core; @@ -237,6 +239,7 @@ begin flush_out => flush, stall_out => ex1_stall_out, e_in => decode2_to_execute1, + i_in => xics_in, l_out => execute1_to_loadstore1, f_out => execute1_to_fetch1, e_out => execute1_to_writeback, diff --git a/execute1.vhdl b/execute1.vhdl index abd4a18..1846488 100644 --- a/execute1.vhdl +++ b/execute1.vhdl @@ -24,6 +24,8 @@ entity execute1 is e_in : in Decode2ToExecute1Type; + i_in : in XicsToExecute1Type; + -- asynchronous l_out : out Execute1ToLoadstore1Type; f_out : out Execute1ToFetch1Type; @@ -403,9 +405,16 @@ begin ctrl_tmp.dec <= std_ulogic_vector(unsigned(ctrl.dec) - 1); irq_valid := '0'; - if ctrl.msr(MSR_EE) = '1' and ctrl.dec(63) = '1' then - report "IRQ valid"; - irq_valid := '1'; + if ctrl.msr(MSR_EE) = '1' then + if ctrl.dec(63) = '1' then + ctrl_tmp.irq_nia <= std_logic_vector(to_unsigned(16#900#, 64)); + report "IRQ valid: DEC"; + irq_valid := '1'; + elsif i_in.irq = '1' then + ctrl_tmp.irq_nia <= std_logic_vector(to_unsigned(16#500#, 64)); + report "IRQ valid: External"; + irq_valid := '1'; + end if; end if; terminate_out <= '0'; @@ -451,7 +460,6 @@ begin -- Don't deliver the interrupt until we have a valid instruction -- coming in, so we have a valid NIA to put in SRR0. exception := e_in.valid; - ctrl_tmp.irq_nia <= std_logic_vector(to_unsigned(16#900#, 64)); ctrl_tmp.srr1 <= msr_copy(ctrl.msr); elsif e_in.valid = '1' and ctrl.msr(MSR_PR) = '1' and @@ -466,6 +474,8 @@ begin elsif e_in.valid = '1' and e_in.unit = ALU then + report "execute nia " & to_hstring(e_in.nia); + v.e.valid := '1'; v.e.write_reg := e_in.write_reg; v.slow_op_dest := gspr_to_gpr(e_in.write_reg); diff --git a/hello_world/console.c b/hello_world/console.c index 8dca8cb..9abd6b9 100644 --- a/hello_world/console.c +++ b/hello_world/console.c @@ -91,6 +91,16 @@ void potato_uart_init(void) potato_uart_reg_write(POTATO_CONSOLE_CLOCK_DIV, potato_uart_divisor(PROC_FREQ, UART_FREQ)); } +void potato_uart_irq_en(void) +{ + potato_uart_reg_write(POTATO_CONSOLE_IRQ_EN, 0xff); +} + +void potato_uart_irq_dis(void) +{ + potato_uart_reg_write(POTATO_CONSOLE_IRQ_EN, 0x00); +} + int getchar(void) { while (potato_uart_rx_empty()) diff --git a/hello_world/console.h b/hello_world/console.h index fc64b91..5cf303a 100644 --- a/hello_world/console.h +++ b/hello_world/console.h @@ -1,6 +1,8 @@ #include void potato_uart_init(void); +void potato_uart_irq_en(void); +void potato_uart_irq_dis(void); int getchar(void); void putchar(unsigned char c); void putstr(const char *str, unsigned long len); diff --git a/rust_lib_demo/README.md b/rust_lib_demo/README.md index 1b60548..e95f21a 100644 --- a/rust_lib_demo/README.md +++ b/rust_lib_demo/README.md @@ -8,7 +8,7 @@ Steps: $ rustup default nightly $ rustup target add powerpc64le-unknown-linux-gnu -``` + $ rustup component add rust-src $ cargo install xargo diff --git a/sim_uart.vhdl b/sim_uart.vhdl index 5881560..6641da5 100644 --- a/sim_uart.vhdl +++ b/sim_uart.vhdl @@ -43,6 +43,9 @@ entity pp_soc_uart is txd : out std_logic; rxd : in std_logic; + -- Interrupt signal: + irq : out std_logic; + -- Wishbone ports: wb_adr_in : in std_logic_vector(11 downto 0); wb_dat_in : in std_logic_vector( 7 downto 0); @@ -70,6 +73,10 @@ begin wb_ack_out <= wb_ack and wb_cyc_in and wb_stb_in; + -- For the sim console, the transmit buffer is always empty, so always + -- interrupt if enabled. No recieve interrupt. + irq <= irq_tx_ready_enable; + wishbone: process(clk) variable sim_tmp : std_logic_vector(63 downto 0); begin diff --git a/soc.vhdl b/soc.vhdl index 9b45b5d..604c6d5 100644 --- a/soc.vhdl +++ b/soc.vhdl @@ -12,6 +12,7 @@ use work.wishbone_types.all; -- 0x00000000: Main memory (1 MB) -- 0xc0002000: UART0 (for host communication) +-- 0xc0004000: XICS ICP entity soc is generic ( MEMORY_SIZE : positive; @@ -55,6 +56,13 @@ architecture behaviour of soc is signal wb_uart0_out : wishbone_slave_out; signal uart_dat8 : std_ulogic_vector(7 downto 0); + -- XICS0 signals: + signal wb_xics0_in : wishbone_master_out; + signal wb_xics0_out : wishbone_slave_out; + signal int_level_in : std_ulogic_vector(15 downto 0); + + signal xics_to_execute1 : XicsToExecute1Type; + -- Main memory signals: signal wb_bram_in : wishbone_master_out; signal wb_bram_out : wishbone_slave_out; @@ -95,7 +103,8 @@ begin dmi_din => dmi_dout, dmi_wr => dmi_wr, dmi_ack => dmi_core_ack, - dmi_req => dmi_core_req + dmi_req => dmi_core_req, + xics_in => xics_to_execute1 ); -- Wishbone bus master arbiter & mux @@ -122,6 +131,7 @@ begin -- Selected slave type slave_type is (SLAVE_UART_0, SLAVE_MEMORY, + SLAVE_ICP_0, SLAVE_NONE); variable slave : slave_type; begin @@ -133,6 +143,9 @@ begin if wb_master_out.adr(23 downto 12) = x"002" then slave := SLAVE_UART_0; end if; + if wb_master_out.adr(23 downto 12) = x"004" then + slave := SLAVE_ICP_0; + end if; end if; -- Wishbone muxing. Defaults: @@ -140,6 +153,12 @@ begin wb_bram_in.cyc <= '0'; wb_uart0_in <= wb_master_out; wb_uart0_in.cyc <= '0'; + + -- Only give xics 8 bits of wb addr + wb_xics0_in <= wb_master_out; + wb_xics0_in.adr <= (others => '0'); + wb_xics0_in.adr(7 downto 0) <= wb_master_out.adr(7 downto 0); + wb_xics0_in.cyc <= '0'; case slave is when SLAVE_MEMORY => wb_bram_in.cyc <= wb_master_out.cyc; @@ -147,6 +166,9 @@ begin when SLAVE_UART_0 => wb_uart0_in.cyc <= wb_master_out.cyc; wb_master_in <= wb_uart0_out; + when SLAVE_ICP_0 => + wb_xics0_in.cyc <= wb_master_out.cyc; + wb_master_in <= wb_xics0_out; when others => wb_master_in.dat <= (others => '1'); wb_master_in.ack <= wb_master_out.stb and wb_master_out.cyc; @@ -170,6 +192,7 @@ begin reset => rst, txd => uart0_txd, rxd => uart0_rxd, + irq => int_level_in(0), wb_adr_in => wb_uart0_in.adr(11 downto 0), wb_dat_in => wb_uart0_in.dat(7 downto 0), wb_dat_out => uart_dat8, @@ -181,6 +204,19 @@ begin wb_uart0_out.dat <= x"00000000000000" & uart_dat8; wb_uart0_out.stall <= '0' when wb_uart0_in.cyc = '0' else not wb_uart0_out.ack; + xics0: entity work.xics + generic map( + LEVEL_NUM => 16 + ) + port map( + clk => system_clk, + rst => rst, + wb_in => wb_xics0_in, + wb_out => wb_xics0_out, + int_level_in => int_level_in, + e_out => xics_to_execute1 + ); + -- BRAM Memory slave bram0: entity work.wishbone_bram_wrapper generic map( diff --git a/tests/test_xics.bin b/tests/test_xics.bin new file mode 100755 index 0000000..6dd993c Binary files /dev/null and b/tests/test_xics.bin differ diff --git a/tests/test_xics.console_out b/tests/test_xics.console_out new file mode 100644 index 0000000..8c7ae53 --- /dev/null +++ b/tests/test_xics.console_out @@ -0,0 +1,3 @@ +Test 0:PASS +Test 1:PASS +Test 2:PASS diff --git a/tests/update_console_tests b/tests/update_console_tests index bd012d9..94e74d1 100755 --- a/tests/update_console_tests +++ b/tests/update_console_tests @@ -3,7 +3,7 @@ # Script to update console related tests from source # -for i in sc illegal decrementer privileged ; do +for i in sc illegal decrementer xics privileged ; do cd $i make cd - diff --git a/tests/xics/Makefile b/tests/xics/Makefile new file mode 100644 index 0000000..8224a99 --- /dev/null +++ b/tests/xics/Makefile @@ -0,0 +1,5 @@ +TEST=xics + +include ../Makefile.test + +xics.o : xics.h diff --git a/tests/xics/head.S b/tests/xics/head.S new file mode 100644 index 0000000..c513a02 --- /dev/null +++ b/tests/xics/head.S @@ -0,0 +1,186 @@ +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define STACK_TOP 0x4000 + +/* Load an immediate 64-bit value into a register */ +#define LOAD_IMM64(r, e) \ + lis r,(e)@highest; \ + ori r,r,(e)@higher; \ + rldicr r,r, 32, 31; \ + oris r,r, (e)@h; \ + ori r,r, (e)@l; + + .section ".head","ax" + + /* Microwatt currently enters in LE mode at 0x0 */ + . = 0 +.global _start +_start: + LOAD_IMM64(%r12, 0x000000000ffffff) + mtdec %r12 + LOAD_IMM64(%r12, 0x9000000000008003) + mtmsrd %r12 // EE on + /* setup stack */ + LOAD_IMM64(%r1, STACK_TOP - 0x100) + LOAD_IMM64(%r12, main) + mtctr %r12 + bctrl + attn // terminate on exit + b . + +#define EXCEPTION(nr) \ + .= nr ;\ + b . + + /* More exception stubs */ + EXCEPTION(0x300) + EXCEPTION(0x380) + EXCEPTION(0x400) + EXCEPTION(0x480) + . = 0x500 + b __isr + + EXCEPTION(0x600) + EXCEPTION(0x700) + EXCEPTION(0x800) + EXCEPTION(0x900) + EXCEPTION(0x980) + EXCEPTION(0xa00) + EXCEPTION(0xb00) + EXCEPTION(0xc00) + EXCEPTION(0xd00) + +// ISR data + +#define REDZONE_SIZE (512) +#define REG_SAVE_SIZE ((32 + 5)*8) +#define STACK_FRAME_C_MINIMAL 64 + +#define SAVE_NIA (32*8) +#define SAVE_LR (33*8) +#define SAVE_CTR (34*8) +#define SAVE_CR (35*8) +#define SAVE_SRR1 (36*8) + +__isr: +/* + * Assume where we are coming from has a stack and can save there. + * We save the full register set. Since we are calling out to C, we + * could just save the ABI volatile registers + */ + stdu %r1,-(REG_SAVE_SIZE+REDZONE_SIZE)(%r1) + std %r0, 1*8(%r1) +// std %r1, 1*8(%r1) + std %r2, 2*8(%r1) + std %r3, 3*8(%r1) + std %r4, 4*8(%r1) + std %r5, 5*8(%r1) + std %r6, 6*8(%r1) + std %r7, 7*8(%r1) + std %r8, 8*8(%r1) + std %r9, 9*8(%r1) + std %r10, 10*8(%r1) + std %r11, 11*8(%r1) + std %r12, 12*8(%r1) + std %r13, 13*8(%r1) + std %r14, 14*8(%r1) + std %r15, 15*8(%r1) + std %r16, 16*8(%r1) + std %r17, 17*8(%r1) + std %r18, 18*8(%r1) + std %r19, 19*8(%r1) + std %r20, 20*8(%r1) + std %r21, 21*8(%r1) + std %r22, 22*8(%r1) + std %r23, 23*8(%r1) + std %r24, 24*8(%r1) + std %r25, 25*8(%r1) + std %r26, 26*8(%r1) + std %r27, 27*8(%r1) + std %r28, 28*8(%r1) + std %r29, 29*8(%r1) + std %r30, 30*8(%r1) + std %r31, 31*8(%r1) + mfsrr0 %r0 + std %r0, SAVE_NIA*8(%r1) + mflr %r0 + std %r0, SAVE_LR*8(%r1) + mfctr %r0 + std %r0, SAVE_CTR*8(%r1) + mfcr %r0 + std %r0, SAVE_CR*8(%r1) + mfsrr1 %r0 + std %r0, SAVE_SRR1*8(%r1) + + stdu %r1,-STACK_FRAME_C_MINIMAL(%r1) + LOAD_IMM64(%r3, isr) + mtctr %r3, + bctrl + nop + ld %r1, 0(%r1) + + ld %r0, 1*8(%r1) +// ld %r1, 1*8(%r1) // do this at rfid + ld %r2, 2*8(%r1) +// ld %r3, 3*8(%r1) // do this at rfid + ld %r4, 4*8(%r1) + ld %r5, 5*8(%r1) + ld %r6, 6*8(%r1) + ld %r7, 7*8(%r1) + ld %r8, 8*8(%r1) + ld %r9, 9*8(%r1) + ld %r10, 10*8(%r1) + ld %r11, 11*8(%r1) + ld %r12, 12*8(%r1) + ld %r13, 13*8(%r1) + ld %r14, 14*8(%r1) + ld %r15, 15*8(%r1) + ld %r16, 16*8(%r1) + ld %r17, 17*8(%r1) + ld %r18, 18*8(%r1) + ld %r19, 19*8(%r1) + ld %r20, 20*8(%r1) + ld %r21, 21*8(%r1) + ld %r22, 22*8(%r1) + ld %r23, 23*8(%r1) + ld %r24, 24*8(%r1) + ld %r25, 25*8(%r1) + ld %r26, 26*8(%r1) + ld %r27, 27*8(%r1) + ld %r28, 28*8(%r1) + ld %r29, 29*8(%r1) + ld %r30, 30*8(%r1) + ld %r31, 31*8(%r1) + + ld %r3, SAVE_LR*8(%r1) + mtlr %r3 + ld %r3, SAVE_CTR*8(%r1) + mtctr %r3 + ld %r3, SAVE_CR*8(%r1) + mtcr %r3 + ld %r3, SAVE_SRR1*8(%r1) + mtsrr1 %r3 + ld %r3, SAVE_NIA*8(%r1) + mtsrr0 %r3 + + /* restore %r3 */ + ld %r3, 3*8(%r1) + + /* do final fixup r1 */ + ld %r1, 0*8(%r1) + + rfid diff --git a/tests/xics/powerpc.lds b/tests/xics/powerpc.lds new file mode 100644 index 0000000..c4bff13 --- /dev/null +++ b/tests/xics/powerpc.lds @@ -0,0 +1,13 @@ +SECTIONS +{ + _start = .; + . = 0; + .head : { + KEEP(*(.head)) + } + . = 0x1000; + .text : { *(.text) } + . = 0x3000; + .data : { *(.data) } + .bss : { *(.bss) } +} diff --git a/tests/xics/xics.c b/tests/xics/xics.c new file mode 100644 index 0000000..6652cbf --- /dev/null +++ b/tests/xics/xics.c @@ -0,0 +1,262 @@ +#include +#include +#include +#include + +#include "console.h" +#include "xics.h" + +#undef DEBUG +//#define DEBUG 1 + +void print_number(unsigned int i) // only for i = 0-999 +{ + unsigned int j, k, m; + bool zeros = false; + + k = 1000000000; + + for (m = 0; m < 10 ; m++) { + j = i/k; + if (m == 9) zeros = true; + if (zeros || (j != 0)) { + putchar(48 + j); + zeros = true; + } + i = i % k; + k = k / 10; + } +} + +#ifdef DEBUG +#define DEBUG_STR "\r\nDEBUG: " +void debug_print(int i) +{ + putstr(DEBUG_STR, strlen(DEBUG_STR)); + print_number(i); + putstr("\r\n", 2); +} + +#define debug_putstr(a, b) putstr(a,b) +#else +#define debug_putstr(a, b) +#define debug_print(i) +#endif + +#define ASSERT_FAIL "() ASSERT_FAILURE!\r\n " +#define assert(cond) \ + if (!(cond)) { \ + putstr(__FILE__, strlen(__FILE__)); \ + putstr(":", 1); \ + print_number(__LINE__); \ + putstr(":", 1); \ + putstr(__FUNCTION__, strlen(__FUNCTION__));\ + putstr(ASSERT_FAIL, strlen(ASSERT_FAIL)); \ + __asm__ ("attn"); \ + } + + +volatile uint64_t isrs_run; + +#define ISR_IPI 0x0000000000000001 +#define ISR_UART 0x0000000000000002 +#define ISR_SPURIOUS 0x0000000000000004 + +#define IPI "IPI\r\n" +void ipi_isr(void) { + debug_putstr(IPI, strlen(IPI)); + + isrs_run |= ISR_IPI; +} + + +#define UART "UART\r\n" +void uart_isr(void) { + debug_putstr(UART, strlen(UART)); + + potato_uart_irq_dis(); // disable interrupt to ack it + + isrs_run |= ISR_UART; +} + +// The hardware doesn't support this but it's part of XICS so add it. +#define SPURIOUS "SPURIOUS\r\n" +void spurious_isr(void) { + debug_putstr(SPURIOUS, strlen(SPURIOUS)); + + isrs_run |= ISR_SPURIOUS; +} + +struct isr_op { + void (*func)(void); + int source_id; +}; + +struct isr_op isr_table[] = { + { .func = ipi_isr, .source_id = 2 }, + { .func = uart_isr, .source_id = 16 }, + { .func = spurious_isr, .source_id = 0 }, + { .func = NULL, .source_id = 0 } +}; + +bool ipi_running; + +#define ISR "ISR XISR=" +void isr(void) +{ + struct isr_op *op; + uint32_t xirr; + + assert(!ipi_running); // check we aren't reentrant + ipi_running = true; + + xirr = xics_read32(XICS_XIRR); // read hardware irq source + +#ifdef DEBUG + putstr(ISR, strlen(ISR)); + print_number(xirr & 0xff); + putstr("\r\n", 2); +#endif + + op = isr_table; + while (1) { + assert(op->func); // didn't find isr + if (op->source_id == (xirr & 0x00ffffff)) { + op->func(); + break; + } + op++; + } + + xics_write32(XICS_XIRR, xirr); // EOI + + ipi_running = false; +} + +/*****************************************/ + +int xics_test_0(void) +{ + // setup + xics_write8(XICS_XIRR, 0x00); // mask all interrupts + isrs_run = 0; + + xics_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 + + // still masked, so shouldn't happen yet + assert(isrs_run == 0); + + // unmask IPI only + xics_write8(XICS_XIRR, 0x40); + assert(isrs_run == ISR_IPI); + + // unmask UART + xics_write8(XICS_XIRR, 0xc0); + assert(isrs_run == (ISR_IPI | ISR_UART)); + + // cleanup + xics_write8(XICS_XIRR, 0x00); // mask all interrupts + isrs_run = 0; + + return 0; +} + +int xics_test_1(void) +{ + // setup + xics_write8(XICS_XIRR, 0x00); // mask all interrupts + isrs_run = 0; + + xics_write8(XICS_XIRR, 0xff); // allow all interrupts + + // should be none pending + assert(isrs_run == 0); + + // trigger both + potato_uart_irq_en(); // cause 0x500 interrupt + xics_write8(XICS_MFRR, 0x05); // cause 0x500 interrupt + + assert(isrs_run == (ISR_IPI | ISR_UART)); + + // cleanup + xics_write8(XICS_XIRR, 0x00); // mask all interrupts + isrs_run = 0; + + return 0; +} + +void mtmsrd(uint64_t val) +{ + __asm__ volatile("mtmsrd %0" : : "r" (val)); +} + +int xics_test_2(void) +{ + // setup + xics_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 + + // trigger an IPI + xics_write8(XICS_MFRR, 0x05); // cause 0x500 interrupt + + assert(isrs_run == 0); + + mtmsrd(0x9000000000008003); // EE on + assert(isrs_run == ISR_IPI); + + // cleanup + xics_write8(XICS_XIRR, 0x00); // mask all interrupts + isrs_run = 0; + + return 0; +} + +#define TEST "Test " +#define PASS "PASS\r\n" +#define FAIL "FAIL\r\n" + +int (*tests[])(void) = { + xics_test_0, + xics_test_1, + xics_test_2, + NULL +}; + +int main(void) +{ + int fail = 0; + int i = 0; + int (*t)(void); + + potato_uart_init(); + ipi_running = false; + + /* run the tests */ + while (1) { + t = tests[i]; + if (!t) + break; + + putstr(TEST, strlen(TEST)); + print_number(i); + putstr(": ", 1); + if (t() != 0) { + fail = 1; + putstr(FAIL, strlen(FAIL)); + } else + putstr(PASS, strlen(PASS)); + + i++; + } + + return fail; +} diff --git a/tests/xics/xics.h b/tests/xics/xics.h new file mode 100644 index 0000000..09238cc --- /dev/null +++ b/tests/xics/xics.h @@ -0,0 +1,36 @@ +#include + +#define XICS_BASE 0xc0004000 + +static uint64_t xics_base = XICS_BASE; + +#define XICS_XIRR_POLL 0x0 +#define XICS_XIRR 0x4 +#define XICS_RESV 0x8 +#define XICS_MFRR 0xC + +uint8_t xics_read8(int offset) +{ + uint32_t val; + + __asm__ volatile("lbzcix %0,%1,%2" : "=r" (val) : "b" (xics_base), "r" (offset)); + return val; +} + +void xics_write8(int offset, uint8_t val) +{ + __asm__ volatile("stbcix %0,%1,%2" : : "r" (val), "b" (xics_base), "r" (offset)); +} + +uint32_t xics_read32(int offset) +{ + uint32_t val; + + __asm__ volatile("lwzcix %0,%1,%2" : "=r" (val) : "b" (xics_base), "r" (offset)); + return val; +} + +void xics_write32(int offset, uint32_t val) +{ + __asm__ volatile("stwcix %0,%1,%2" : : "r" (val), "b" (xics_base), "r" (offset)); +} diff --git a/xics.vhdl b/xics.vhdl new file mode 100644 index 0000000..09a1ba6 --- /dev/null +++ b/xics.vhdl @@ -0,0 +1,207 @@ +-- +-- This is a simple XICS compliant interrupt controller. This is a +-- Presenter (ICP) and Source (ICS) in a single unit 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 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 is + generic ( + LEVEL_NUM : positive := 16 + ); + port ( + clk : in std_logic; + rst : in std_logic; + + wb_in : in wishbone_master_out; + wb_out : out wishbone_slave_out; + + int_level_in : in std_ulogic_vector(LEVEL_NUM - 1 downto 0); + + e_out : out XicsToExecute1Type + ); +end xics; + +architecture behaviour of xics 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 : wishbone_data_type; + wb_ack : std_ulogic; + end record; + constant reg_internal_init : reg_internal_t := + (wb_ack => '0', + mfrr_pending => '0', + mfrr => x"00", -- mask everything on reset + irq => '0', + others => (others => '0')); + + signal r, r_next : reg_internal_t; + + -- hardwire the hardware IRQ priority + constant HW_PRIORITY : std_ulogic_vector(7 downto 0) := x"80"; + + -- 32 bit offsets for each presentation + constant XIRR_POLL : std_ulogic_vector(31 downto 0) := x"00000000"; + constant XIRR : std_ulogic_vector(31 downto 0) := x"00000004"; + constant RESV0 : std_ulogic_vector(31 downto 0) := x"00000008"; + constant MFRR : std_ulogic_vector(31 downto 0) := x"0000000c"; + +begin + + regs : process(clk) + begin + if rising_edge(clk) then + r <= r_next; + end if; + end process; + + wb_out.dat <= r.wb_rd_data; + wb_out.ack <= r.wb_ack; + wb_out.stall <= '0'; -- never stall wishbone + e_out.irq <= r.irq; + + comb : process(all) + variable v : reg_internal_t; + variable xirr_accept_rd : std_ulogic; + variable irq_eoi : std_ulogic; + begin + v := r; + + v.wb_ack := '0'; + + xirr_accept_rd := '0'; + irq_eoi := '0'; + + if wb_in.cyc = '1' and wb_in.stb = '1' then + -- wishbone addresses we get are 64 bit alligned, so we + -- need to use the sel bits to get 32 bit chunks. + v.wb_ack := '1'; -- always ack + if wb_in.we = '1' then -- write + -- writes to both XIRR are the same + if wb_in.adr = XIRR_POLL then + report "XICS XIRR_POLL/XIRR write"; + if wb_in.sel = x"0f" then -- 4 bytes + v.cppr := wb_in.dat(31 downto 24); + elsif wb_in.sel = x"f0" then -- 4 byte + v.cppr := wb_in.dat(63 downto 56); + irq_eoi := '1'; + elsif wb_in.sel = x"01" then -- 1 byte + v.cppr := wb_in.dat(7 downto 0); + elsif wb_in.sel = x"10" then -- 1 byte + v.cppr := wb_in.dat(39 downto 32); + end if; + + elsif wb_in.adr = RESV0 then + report "XICS MFRR write"; + if wb_in.sel = x"f0" then -- 4 bytes + v.mfrr_pending := '1'; + v.mfrr := wb_in.dat(63 downto 56); + elsif wb_in.sel = x"10" then -- 1 byte + v.mfrr_pending := '1'; + v.mfrr := wb_in.dat(39 downto 32); + end if; + + end if; + + else -- read + v.wb_rd_data := (others => '0'); + + if wb_in.adr = XIRR_POLL then + report "XICS XIRR_POLL/XIRR read"; + if wb_in.sel = x"0f" then + v.wb_rd_data(23 downto 0) := r.xisr; + v.wb_rd_data(31 downto 24) := r.cppr; + elsif wb_in.sel = x"f0" then + v.wb_rd_data(55 downto 32) := r.xisr; + v.wb_rd_data(63 downto 56) := r.cppr; + xirr_accept_rd := '1'; + elsif wb_in.sel = x"01" then + v.wb_rd_data(7 downto 0) := r.cppr; + elsif wb_in.sel = x"10" then + v.wb_rd_data(39 downto 32) := r.cppr; + end if; + + elsif wb_in.adr = RESV0 then + report "XICS MFRR read"; + if wb_in.sel = x"f0" then -- 4 bytes + v.wb_rd_data(63 downto 56) := r.mfrr; + elsif wb_in.sel = x"10" then -- 1 byte + v.wb_rd_data( 7 downto 0) := r.mfrr; + end if; + end if; + 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; + + -- 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; + + -- 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; + end if; + + if irq_eoi = '1' then + v.irq := '0'; + end if; + + if rst = '1' then + v := reg_internal_init; + end if; + + r_next <= v; + + end process; + +end architecture behaviour;