Skip to content

Commit f0464cb

Browse files
committed
arch: microblaze: Interrupt and Exception Handling
Internal references: FWRIVERHD-4971 Signed-off-by: Alp Sayin <alpsayin@gmail.com>
1 parent 60b29df commit f0464cb

File tree

3 files changed

+510
-0
lines changed

3 files changed

+510
-0
lines changed

arch/microblaze/core/exception.S

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright (c) 2023 Advanced Micro Devices, Inc. (AMD)
3+
* Copyright (c) 2023 Alp Sayin <alpsayin@gmail.com>
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
9+
#include <zephyr/toolchain.h>
10+
#include <zephyr/linker/sections.h>
11+
#include <offsets_short.h>
12+
#include <microblaze/microblaze_regs.h>
13+
#include <microblaze/microblaze_asm.h>
14+
15+
16+
/* import */
17+
.extern stack_pointer_on_exception_entry
18+
.extern _Fault
19+
20+
/* export */
21+
.global _exception_handler_entry
22+
.global _asm_stack_failed
23+
24+
25+
.text
26+
.balign 4
27+
28+
_exception_handler_entry:
29+
30+
/* Make room for the context on the stack. */
31+
STACK_ALLOC(__z_arch_esf_t_SIZEOF)
32+
ASSERT_GT_ZERO(r1, _asm_stack_failed)
33+
34+
/* Take a copy of the stack pointer at the moment w/ context stored,
35+
storing its value prior to the function stack frame being created. */
36+
STORE_REG_TO_ADDR(r1, stack_pointer_on_exception_entry)
37+
38+
/* Stack MSR using r11(temp1) */
39+
mfs TEMP_DATA_REG, rmsr
40+
STORE_TO_STACK(TEMP_DATA_REG, ESF_OFFSET(msr))
41+
42+
#if defined(CONFIG_USE_HARDWARE_FLOAT_INSTR)
43+
/* Stack FSR using TEMP_DATA_REG(temp1) */
44+
mfs TEMP_DATA_REG, rfsr
45+
STORE_TO_STACK(TEMP_DATA_REG, ESF_OFFSET(fsr))
46+
#endif
47+
48+
PUSH_CONTEXT_TO_STACK(r31)
49+
PUSH_CONTEXT_TO_STACK(r30)
50+
PUSH_CONTEXT_TO_STACK(r29)
51+
PUSH_CONTEXT_TO_STACK(r28)
52+
PUSH_CONTEXT_TO_STACK(r27)
53+
PUSH_CONTEXT_TO_STACK(r26)
54+
PUSH_CONTEXT_TO_STACK(r25)
55+
PUSH_CONTEXT_TO_STACK(r24)
56+
PUSH_CONTEXT_TO_STACK(r23)
57+
PUSH_CONTEXT_TO_STACK(r22)
58+
PUSH_CONTEXT_TO_STACK(r21)
59+
PUSH_CONTEXT_TO_STACK(r20)
60+
PUSH_CONTEXT_TO_STACK(r19)
61+
PUSH_CONTEXT_TO_STACK(r18)
62+
PUSH_CONTEXT_TO_STACK(r17)
63+
PUSH_CONTEXT_TO_STACK(r16)
64+
PUSH_CONTEXT_TO_STACK(r15)
65+
PUSH_CONTEXT_TO_STACK(r14)
66+
PUSH_CONTEXT_TO_STACK(r13)
67+
PUSH_CONTEXT_TO_STACK(r12)
68+
PUSH_CONTEXT_TO_STACK(r11)
69+
PUSH_CONTEXT_TO_STACK(r10)
70+
PUSH_CONTEXT_TO_STACK(r9)
71+
PUSH_CONTEXT_TO_STACK(r8)
72+
PUSH_CONTEXT_TO_STACK(r7)
73+
PUSH_CONTEXT_TO_STACK(r6)
74+
PUSH_CONTEXT_TO_STACK(r5)
75+
PUSH_CONTEXT_TO_STACK(r4)
76+
PUSH_CONTEXT_TO_STACK(r3)
77+
PUSH_CONTEXT_TO_STACK(r2)
78+
79+
mfs r5, resr
80+
mfs r6, rear
81+
mfs r7, redr
82+
83+
braid _Fault
84+
nop
85+
86+
.text
87+
.balign 4
88+
89+
_asm_stack_failed:
90+
/* Should always be called with interrupts disabled
91+
* so that ISR doesn't overwrite irq stack
92+
* Currently only jumped from isr and swap entry.
93+
*/
94+
95+
/* stack has failed so we immediately need to switch to an emergency stack */
96+
SET_REG(r1, z_interrupt_stacks)
97+
braid _exception_handler_entry
98+
STACK_FREE(__z_interrupt_stack_SIZEOF)

arch/microblaze/core/irq_manage.c

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/*
2+
* Copyright (c) 2023 Advanced Micro Devices, Inc. (AMD)
3+
* Copyright (c) 2023 Alp Sayin <alpsayin@gmail.com>
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
9+
#include <zephyr/arch/cpu.h>
10+
#include <zephyr/irq.h>
11+
#include <zephyr/kernel.h>
12+
#include <zephyr/kernel_structs.h>
13+
#include <zephyr/logging/log.h>
14+
#include <zephyr/sw_isr_table.h>
15+
#include <zephyr/tracing/tracing.h>
16+
17+
#include <ksched.h>
18+
#include <kswap.h>
19+
#include <microblaze/mb_interface.h>
20+
21+
LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
22+
23+
static uint32_t emulated_irq_pending;
24+
25+
FUNC_NORETURN void z_irq_spurious(const void *param)
26+
{
27+
LOG_ERR("Spurious interrupt detected!\n"
28+
"\tmsr: %x\n"
29+
"\tesr: %x\n"
30+
"\tear: %x\n"
31+
"\tedr: %x\n"
32+
"\tparam: %x\n",
33+
(unsigned int)mfmsr(), (unsigned int)mfesr(), (unsigned int)mfear(),
34+
(unsigned int)mfedr(), (unsigned int)param);
35+
36+
z_microblaze_fatal_error(K_ERR_SPURIOUS_IRQ, NULL);
37+
CODE_UNREACHABLE;
38+
}
39+
40+
/**
41+
* @brief Returns if IE bit is enabled
42+
* Defined weak so soc/boards can override it.
43+
*/
44+
__weak int arch_irq_is_enabled(unsigned int irq)
45+
{
46+
ARG_UNUSED(irq);
47+
return mfmsr() & MSR_IE_MASK;
48+
}
49+
50+
/**
51+
* @brief Simply unlocks all IRQs.
52+
* Defined weak so soc/boards can override it.
53+
*/
54+
__weak void arch_irq_enable(unsigned int irq)
55+
{
56+
ARG_UNUSED(irq);
57+
arch_irq_unlock(1);
58+
};
59+
60+
/**
61+
* @brief Simply locks all IRQs.
62+
* Defined weak so soc/boards can override it.
63+
*/
64+
__weak void arch_irq_disable(unsigned int irq)
65+
{
66+
ARG_UNUSED(irq);
67+
arch_irq_lock();
68+
};
69+
70+
/**
71+
* @brief Returns the currently pending interrupts.
72+
* This function should be overridden if an AXI interrupt
73+
* controller is placed inside the SoC.
74+
* Since there's no way to tell if a barebones MicroBlaze is
75+
* pending on an interrupt signal, this function will return 1 on first call,
76+
* and returns 0 on second call, which is enough to
77+
* make the _enter_irq break its while(ipending) loop.
78+
* This is a logically correct hack to make _enter_irq below work for
79+
* barebones MicroBlaze without introducing extra logic to the _enter_irq logic.
80+
* Obviously, this function shouldn't be used for generic purposes and is merely
81+
* a weak stub for soc/boards to override with more meaningful implementations.
82+
*
83+
* @return Pending IRQ bitmask. Pending IRQs will have their bitfield set to 1. 0 if no interrupt is
84+
* pending.
85+
*/
86+
__weak uint32_t arch_irq_pending(void)
87+
{
88+
static uint32_t call_count;
89+
90+
/* xor with 1 should simply invert the value */
91+
call_count ^= 1;
92+
return call_count;
93+
};
94+
95+
__weak uint32_t arch_irq_pending_vector(uint32_t irq_pending)
96+
{
97+
return find_lsb_set(irq_pending) - 1;
98+
}
99+
100+
/**
101+
* @brief * Even in the presence of an interrupt controller, once the real mode
102+
* is enabled, there is no way to emulate hw interrupts. So this routine provides
103+
* a software "triggering" capability to MicroBlaze. This routine MUST be called
104+
* either with IRQs locked or interrupts disabled otherwise a real IRQ could fire.
105+
* Also see emulate_isr.h
106+
*
107+
* @param irq IRQ number to enable.
108+
* @return uint32_t returns the final emulated irq_pending mask.
109+
*/
110+
ALWAYS_INLINE uint32_t arch_irq_set_emulated_pending(uint32_t irq)
111+
{
112+
return emulated_irq_pending |= BIT(irq);
113+
}
114+
115+
/**
116+
* @brief Called by _interrupt_handler in isr.S
117+
*/
118+
void _enter_irq(void)
119+
{
120+
_kernel.cpus[0].nested++;
121+
122+
#ifdef CONFIG_IRQ_OFFLOAD
123+
z_irq_do_offload();
124+
#endif
125+
126+
uint32_t real_irq_pending = 0;
127+
128+
while (true) {
129+
struct _isr_table_entry *ite;
130+
131+
real_irq_pending = arch_irq_pending();
132+
133+
if (real_irq_pending == 0 && emulated_irq_pending == 0) {
134+
break;
135+
}
136+
137+
#ifdef CONFIG_TRACING_ISR
138+
sys_trace_isr_enter();
139+
#endif
140+
141+
/* From pg099 AXI Interrupt Controller (INTC) product guide:
142+
* The least significant bit (LSB, in this case bit 0) has the highest priority.
143+
*/
144+
const uint32_t index = (real_irq_pending != 0)
145+
? arch_irq_pending_vector(real_irq_pending)
146+
: find_lsb_set(emulated_irq_pending) - 1;
147+
148+
ite = &_sw_isr_table[index];
149+
150+
ite->isr(ite->arg);
151+
152+
/* In this implementation it's the ISR's responsibility to clear irq flags.
153+
* But _enter_irq does clear the emulated IRQs automatically since this is a port
154+
* provided functionality and also needed to pass unit tests without altering tests.
155+
*/
156+
emulated_irq_pending &= ~BIT(index);
157+
158+
#ifdef CONFIG_TRACING_ISR
159+
sys_trace_isr_exit();
160+
#endif
161+
}
162+
163+
_kernel.cpus[0].nested--;
164+
#ifdef CONFIG_STACK_SENTINEL
165+
z_check_stack_sentinel();
166+
#endif
167+
}
168+
169+
#ifdef CONFIG_DYNAMIC_INTERRUPTS
170+
int arch_irq_connect_dynamic(unsigned int irq, unsigned int priority,
171+
void (*routine)(const void *parameter), const void *parameter,
172+
uint32_t flags)
173+
{
174+
ARG_UNUSED(flags);
175+
ARG_UNUSED(priority);
176+
177+
z_isr_install(irq, routine, parameter);
178+
return irq;
179+
}
180+
#endif /* CONFIG_DYNAMIC_INTERRUPTS */

0 commit comments

Comments
 (0)