mirror of
https://github.com/antonblanchard/microwatt.git
synced 2026-02-09 17:51:23 +00:00
Merge pull request #165 from mikey/xics
Implement XICS compliant interrupt controller
This commit is contained in:
3
Makefile
3
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
|
||||
|
||||
@@ -305,6 +305,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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -24,6 +24,8 @@ entity execute1 is
|
||||
|
||||
e_in : in Decode2ToExecute1Type;
|
||||
|
||||
i_in : in XicsToExecute1Type;
|
||||
|
||||
-- asynchronous
|
||||
l_out : out Execute1ToLoadstore1Type;
|
||||
f_out : out Execute1ToFetch1Type;
|
||||
@@ -370,9 +372,16 @@ begin
|
||||
ctrl_tmp.dec <= std_ulogic_vector(unsigned(ctrl.dec) - 1);
|
||||
|
||||
irq_valid := '0';
|
||||
if ctrl.msr(63 - 48) = '1' and ctrl.dec(63) = '1' then
|
||||
report "IRQ valid";
|
||||
irq_valid := '1';
|
||||
if ctrl.msr(63 - 48) = '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';
|
||||
@@ -412,11 +421,12 @@ 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' 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);
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include <stddef.h>
|
||||
|
||||
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);
|
||||
|
||||
@@ -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
|
||||
|
||||
38
soc.vhdl
38
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(
|
||||
|
||||
BIN
tests/test_xics.bin
Executable file
BIN
tests/test_xics.bin
Executable file
Binary file not shown.
3
tests/test_xics.console_out
Normal file
3
tests/test_xics.console_out
Normal file
@@ -0,0 +1,3 @@
|
||||
Test 0:PASS
|
||||
Test 1:PASS
|
||||
Test 2:PASS
|
||||
@@ -3,7 +3,7 @@
|
||||
# Script to update console related tests from source
|
||||
#
|
||||
|
||||
for i in sc illegal decrementer ; do
|
||||
for i in sc illegal decrementer xics ; do
|
||||
cd $i
|
||||
make
|
||||
cd -
|
||||
|
||||
5
tests/xics/Makefile
Normal file
5
tests/xics/Makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
TEST=xics
|
||||
|
||||
include ../Makefile.test
|
||||
|
||||
xics.o : xics.h
|
||||
186
tests/xics/head.S
Normal file
186
tests/xics/head.S
Normal file
@@ -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
|
||||
13
tests/xics/powerpc.lds
Normal file
13
tests/xics/powerpc.lds
Normal file
@@ -0,0 +1,13 @@
|
||||
SECTIONS
|
||||
{
|
||||
_start = .;
|
||||
. = 0;
|
||||
.head : {
|
||||
KEEP(*(.head))
|
||||
}
|
||||
. = 0x1000;
|
||||
.text : { *(.text) }
|
||||
. = 0x3000;
|
||||
.data : { *(.data) }
|
||||
.bss : { *(.bss) }
|
||||
}
|
||||
262
tests/xics/xics.c
Normal file
262
tests/xics/xics.c
Normal file
@@ -0,0 +1,262 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <limits.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
36
tests/xics/xics.h
Normal file
36
tests/xics/xics.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#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));
|
||||
}
|
||||
207
xics.vhdl
Normal file
207
xics.vhdl
Normal file
@@ -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;
|
||||
Reference in New Issue
Block a user