前回の続き
QEMUからFreeRTOSへの送信
QEMUの割り込み
QEMUが受け取ったARPパケットはFreeRTOS側で受け取れるようにする必要がある。
これには割り込みを使用する。
こんな感じ
qemuのコードを見ると32個の割り込みが使用出来るようです。
がしかし、実際に登録されているのはUART関連のみのようです。
したがって、割り込みを発生させる前に割り込みデバイスを作成する必要があります。
既存のUARTを借用するというのもあるけど、今回は別で作成しました。
なんか良さげなのがあったので借用してみた。
static void register_ether_irq(DeviceState *armv7m)
{
DeviceState *dev;
dev = qdev_new(TYPE_MV88W8618_ETH);
object_property_set_link(OBJECT(dev), "dma-memory",
OBJECT(get_system_memory()), &error_fatal);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0,
qdev_get_gpio_in(armv7m, IRQ_ETHER));
}
で割り込み発生させてみると
static void send_to_freertos(uint8_t *buf, size_t size)
{
(void)buf;
CPUState *cpu = CPU(ARM_CPU(first_cpu));
d("irq fire n:%lu", size);
cpu->interrupt_request = IRQ_ETHER;
cpu_interrupt(cpu, CPU_INTERRUPT_VIRQ);
}
怒られました
mps2.c(829) send_to_freertos irq fire n:42
**
ERROR:../qemu/accel/tcg/tcg-accel-ops.c:91:tcg_handle_interrupt: assertion failed: (qemu_mutex_iothread_locked())
Bail out! ERROR:../qemu/accel/tcg/tcg-accel-ops.c:91:tcg_handle_interrupt: assertion failed: (qemu_mutex_iothread_locked())
Aborted
この使い方ではだめらしい
gdb で見てみるとわりかし即abortしてる。
(gdb) l
86 }
87
88 /* mask must never be zero, except for A20 change call */
89 void tcg_handle_interrupt(CPUState *cpu, int mask)
90 {
91 g_assert(qemu_mutex_iothread_locked());
iothreadがlockされていないと割り込みを投げられないらしい。
前はこんなところで引っかからなかったのになぁ
とりあえずコード読んでみると
#define QEMU_DEFINE_STATIC_CO_TLS(type, var) \
static __thread type co_tls_##var; \
static __attribute__((noinline, unused)) \
type get_##var(void) \
{ asm volatile(""); return co_tls_##var; } \
static __attribute__((noinline, unused)) \
void set_##var(type v) \
{ asm volatile(""); co_tls_##var = v; } \
static __attribute__((noinline, unused)) \
type *get_ptr_##var(void) \
{ type *ptr = &co_tls_##var; asm volatile("" : "+rm" (ptr)); return ptr; }
QEMU_DEFINE_STATIC_CO_TLS(bool, iothread_locked)
bool qemu_mutex_iothread_locked(void)
{
return get_iothread_locked();
}
set_iothread_locked()
が事前に必要っぽい
set_iothread_locked
が使用されているのはここ
system/cpus.c
void qemu_mutex_lock_iothread_impl(const char *file, int line)
{
QemuMutexLockFunc bql_lock = qatomic_read(&qemu_bql_mutex_lock_func);
g_assert(!qemu_mutex_iothread_locked());
bql_lock(&qemu_global_mutex, file, line);
set_iothread_locked(true);
}
include/qemu/main-loop.h
#define qemu_mutex_lock_iothread() \
qemu_mutex_lock_iothread_impl(__FILE__, __LINE__)
void qemu_mutex_lock_iothread_impl(const char *file, int line);
なので事前にqemu_mutex_lock_iothread
呼んで終わったらqemu_mutex_unlock_iothread
呼べば良さそう?
一応落ちなくはなったけど???
なんかおかしい感じがする
mps2.c(829) send_to_freertos irq fire n:42
mps2.c(829) send_to_freertos irq fire n:42
mps2.c(829) send_to_freertos irq fire n:42
割り込みハンドラとベクタテーブル
qemuから受け取った割り込みはベクタテーブルに記載されている割り込みハンドラへとつながっています。
FreeRTOS側のコードも見てみると
こんな感じに32個の割り込みが使用出来ることが見て取れます。
SECTIONS
{
.isr_vector :
{
__vector_table = .;
KEEP(*(.isr_vector))
. = ALIGN(4);
} > FLASH
const uint32_t * isr_vector[] __attribute__( ( section( ".isr_vector" ) ) ) =
{
( uint32_t * ) &_estack,
( uint32_t * ) &Reset_Handler, /* Reset -15 */
( uint32_t * ) &Default_Handler, /* NMI_Handler -14 */
( uint32_t * ) &Default_Handler2, /* HardFault_Handler -13 */
( uint32_t * ) &Default_Handler3, /* MemManage_Handler -12 */
( uint32_t * ) &Default_Handler4, /* BusFault_Handler -11 */
( uint32_t * ) &Default_Handler5, /* UsageFault_Handler -10 */
0, /* reserved */
0, /* reserved */
0, /* reserved */
0, /* reserved -6 */
( uint32_t * ) &vPortSVCHandler, /* SVC_Handler -5 */
( uint32_t * ) &Default_Handler6, /* DebugMon_Handler -4 */
0, /* reserved */
( uint32_t * ) &xPortPendSVHandler, /* PendSV handler -2 */
( uint32_t * ) &xPortSysTickHandler, /* SysTick_Handler -1 */
0, /* uart0 receive 0 */
0, /* uart0 transmit */
0, /* uart1 receive */
0, /* uart1 transmit */
0, /* uart 2 receive */
0, /* uart 2 transmit */
0, /* GPIO 0 combined interrupt */
0, /* GPIO 2 combined interrupt */
0, /* Timer 0 */
0, /* Timer 1 */
0, /* Dial Timer */
0, /* SPI0 SPI1 */
0, /* uart overflow 1, 2,3 */
0, /* Ethernet 13 */
};
FreeRTOSのコメントより13番がEthernet関連用に使用する割り込みのようなので
そんな感じで実装していく。
diff --git a/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/init/startup.c b/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/init/startup.c
index 28c657c8..279ecc66 100644
--- a/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/init/startup.c
+++ b/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/init/startup.c
@@ -167,6 +167,8 @@ void Default_Handler6( void )
}
}
+extern void ether_rx_handler(void);
+
const uint32_t * isr_vector[] __attribute__( ( section( ".isr_vector" ) ) ) =
{
( uint32_t * ) &_estack,
@@ -198,7 +200,7 @@ const uint32_t * isr_vector[] __attribute__( ( section( ".isr_vector" ) ) ) =
0, /* Dial Timer */
0, /* SPI0 SPI1 */
0, /* uart overflow 1, 2,3 */
- 0, /* Ethernet 13 */
+ (uint32_t*) ðer_rx_handler, /* Ethernet 13 */
};
void _start( void )
割り込みハンドラの実体はNIC内に
source/portable/NetworkInterface/virtether/NetworkInterface.c
void ether_rx_handler(void)
{
d("");
}
受け取れていないっぽい
mps2.c(829) send_to_freertos irq fire n:42
mps2.c(829) send_to_freertos irq fire n:42
mps2.c(829) send_to_freertos irq fire n:42
割り込みハンドラが受け取れない原因
どうも割り込みの発生させ方が間違っているようで
割り込みハンドラに入ってきません。
割り込みの動作状況を見てみます。
-d int
のオプションをつけると割り込みの発生が表示されます。
sudo /home/voyager/workspace/oss/build_qemu/qemu-system-arm -machine mps2-an385 -m 16 -monitor null -semihosting --semihosting-config enable=on,target=native -kernel ./build/RTOSDemo.axf -serial stdio -nographic -d int 2>&1 | tee qemu.log
...
Exception return: magic PC fffffffd previous exception 15
...successful exception return
Taking exception 5 [IRQ] on CPU 0
...taking pending nonsecure exception 15
...loading from element 15 of non-secure vector table at 0x3
...
他の割り込みは動作しているようです。
なんの割り込みが動作しているか見てみます。
fgrep "Taking exception 5 [IRQ] on CPU 0" qemu.log -A4 | grep -E "PC|CPU" | fgrep -v "Taking exception 8" | sort | uniq -c
...
32 ...loaded new PC 0x379
3434 ...loaded new PC 0x3c5
1 [Taking exception 5 [IRQ] on CPU 0
3433 Taking exception 5 [IRQ] on CPU 0
2つほど動作しているようです。
これを実行ファイルで見てみます。
arm-none-eabi-objdump -d build/RTOSDemo.axf | grep -E " (37|3c).:" -B4
...
00000378 <xPortPendSVHandler>:
378: f3ef 8009 mrs r0, PSP
37c: f3bf 8f6f isb sy
--
3b8: f3af 8000 nop.w
3bc: f3af 8000 nop.w
000003c0 <pxCurrentTCBConst>:
3c0: 20006158 .word 0x20006158
000003c4 <xPortSysTickHandler>:
3c4: b508 push {r3, lr}
3c6: f04f 03bf mov.w r3, #191 @ 0xbf
3ca: f383 8811 msr BASEPRI, r3
3ce: f3bf 8f6f isb sy
xPortPendSVHandler
とxPortSysTickHandler
が動作しているようです。
どちらもベクタテーブルに存在しているものです。
const uint32_t * isr_vector[] __attribute__( ( section( ".isr_vector" ) ) ) =
{
...
( uint32_t * ) &Default_Handler6, /* DebugMon_Handler -4 */ 0, /* reserved */
( uint32_t * ) &xPortPendSVHandler, /* PendSV handler -2 */
( uint32_t * ) &xPortSysTickHandler, /* SysTick_Handler -1 */
FreeRTOS側にログ出力を埋め込んで確認してみましょう。
どちらもFreeRTOS/Source/portable/GCC/ARM_CM3/port.cにあります。
diff --git a/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c b/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c
index 8d07379d..e7291f74 100644
--- a/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c
+++ b/FreeRTOS/Source/portable/GCC/ARM_CM3/port.c
@@ -34,6 +34,8 @@
#include "FreeRTOS.h"
#include "task.h"
+#include "dlog.h"
+
/* For backward compatibility, ensure configKERNEL_INTERRUPT_PRIORITY is
* defined. The value should also ensure backward compatibility.
* FreeRTOS.org versions prior to V4.4.0 did not include this definition. */
@@ -400,7 +402,7 @@ void vPortExitCritical( void )
void xPortPendSVHandler( void )
{
/* This is a naked function. */
-
+d("");
__asm volatile
(
" mrs r0, psp \n"
@@ -436,6 +438,7 @@ void xPortPendSVHandler( void )
void xPortSysTickHandler( void )
{
+ d("");
/* The SysTick runs at the lowest interrupt priority, so when this interrupt
* executes all interrupts must be unmasked. There is therefore no need to
* save and then restore the interrupt mask value as its value is already
割り込み内での処理が長すぎて詰まってるっぽいですが
一応入ってはきています。
$ sudo /home/voyager/workspace/oss/build_qemu/qemu-system-arm -machine mps2-an385 -m 16 -monitor null -semihosting --semihosting-config enable=on,target=native -kernel ./build/RTOSDemo.axf -serial stdio -nographic 2>&1 | tee -a qemu.log
mps2.c(655) add_extra_mmio_an385 added to 28000000
mps2.c(783) udp_recvfromdummy
[0000] main_tcp.c(22) main_tcp 09:18:17, Dec 20 2023(51a0cff96d5468ad2355cf219d72f0f524ecdb2b-dirty) fr:0x1c7
[0000] NetworkInterface.c(139) pxFillInterfaceDescriptor mac:0 fr:0x5583
[01fc] port.c(441) xPortSysTickHandler fr:0xfffffffd
[01fc] port.c(441) xPortSysTickHandler fr:0xfffffffd
[01fc] port.c(441) xPortSysTickHandler fr:0xfffffffd
[01fc] port.c(441) xPortSysTickHandler fr:0xfffffffd
[01fc] port.c(441) xPortSysTickHandler fr:0xfffffffd
[01fc] port.c(441) xPortSysTickHandler fr:0xfffffffd
[01fc] port.c(441) xPortSysTickHandler fr:0xfffffffd
[01fc] port.c(441) xPortSysTickHandler fr:0xfffffffd
[01fc] port.c(441) xPortSysTickHandler fr:0xfffffffd
[01fc] port.c(441) xPortSysTickHandler fr:0xfffffffd
[01fc] port.c(441) xPortSysTickHandler fr:0xfffffffd
[01fc] port.c(441) xPortSysTickHandler fr:0xfffffffd
[01fc] port.c(441) xPortSysTickHandler fr:0xfffffffd
[01fc] port.c(405) xPortPendSVHandler fr:0xfffffffd
qemu内の割り込み発生の仕組み
qemuをデバッグしてこれらの割り込みが発生するときに
どのような動作になっているのか調べてみます。
$ qemu$ fgrep "Taking exception" . -wnrI
./target/arm/helper.c:10399: qemu_log_mask(CPU_LOG_INT, "Taking exception %d [%s] on CPU %d\n",
10360 void arm_log_exception(CPUState *cs)
10361 {
10362 int idx = cs->exception_index;
gdbで見るときはそれ専用のgdbinitを作っておくと楽です。
.gdbinit: FreeRTOSをデバッグする時
target remote localhost:12345
set pagination off
set logging on
file build/RTOSDemo.axf
set confirm off
define es
stepi
disas $pc,+10
end
.qemugdbinit: QEMU自体をデバッグするとき
set pagination off
set logging on
file /home/voyager/workspace/oss/build_qemu/qemu-system-arm
#b main
b mps2_common_init
r -machine mps2-an385 -m 16 -monitor null -semihosting --semihosting-config enable=on,target=native -kernel ./build/RTOSDemo.axf -serial stdio -nographic
こうすることで以下の手順で別々のデバッグができます。
FreeRTOSをデバッグする時
sudo /home/voyager/workspace/oss/build_qemu/qemu-system-arm -machine mps2-an385 -m 16 -monitor null -semihosting --semihosting-config enable=on,target=native -kernel ./build/RTOSDemo.axf -serial stdio -nographic 2>&1 | tee -a qemu.log
別ターミナルで
gdb
QEMUをデバッグする時
sudo gdb -ix=.qemugdbinit
今回はQEMU自体をデバッグしたいので以下のような感じでデバッグを行います。
$ sudo gdb -ix=.qemugdbinit
...
(gdb) l arm_log_exception
10356
10357 return target_el;
10358 }
10359
10360 void arm_log_exception(CPUState *cs)
10361 {
10362 int idx = cs->exception_index;
10363
10364 if (qemu_loglevel_mask(CPU_LOG_INT)) {
10365 const char *exc = NULL;
(gdb) b 10362 if (cs->exception_index == 5)
Breakpoint 2 at 0x555555da3a48: file ../qemu/target/arm/helper.c, line 10362.
(gdb) c
...
Thread 4 "qemu-system-arm" hit Breakpoint 2, arm_log_exception (cs=0x555557945c00) at ../qemu/target/arm/helper.c:10362
10362 int idx = cs->exception_index;
(gdb) bt
#0 arm_log_exception (cs=0x555557945c00) at ../qemu/target/arm/helper.c:10362
#1 0x0000555555e4dedc in arm_v7m_cpu_do_interrupt (cs=0x555557945c00) at ../qemu/target/arm/tcg/m_helper.c:2189
#2 0x0000555555dae464 in arm_v7m_cpu_exec_interrupt (cs=0x555557945c00, interrupt_request=2) at ../qemu/target/arm/tcg/cpu32.c:120
#3 0x0000555555f61164 in cpu_handle_interrupt (cpu=0x555557945c00, last_tb=0x7fff6fffe930) at ../qemu/accel/tcg/cpu-exec.c:865
#4 0x0000555555f61677 in cpu_exec_loop (cpu=0x555557945c00, sc=0x7fff6fffe9b0) at ../qemu/accel/tcg/cpu-exec.c:974
#5 0x0000555555f6170f in cpu_exec_setjmp (cpu=0x555557945c00, sc=0x7fff6fffe9b0) at ../qemu/accel/tcg/cpu-exec.c:1058
#6 0x0000555555f61796 in cpu_exec (cpu=0x555557945c00) at ../qemu/accel/tcg/cpu-exec.c:1084
#7 0x0000555555f87dae in tcg_cpus_exec (cpu=0x555557945c00) at ../qemu/accel/tcg/tcg-accel-ops.c:76
#8 0x0000555555f88497 in mttcg_cpu_thread_fn (arg=0x555557945c00) at ../qemu/accel/tcg/tcg-accel-ops-mttcg.c:95
#9 0x000055555618ec4b in qemu_thread_start (args=0x55555799bb70) at ../qemu/util/qemu-thread-posix.c:541
#10 0x00007ffff7884897 in start_thread () at /lib64/libc.so.6
#11 0x00007ffff790b6bc in clone3 () at /lib64/libc.so.6
(gdb) f 2
#2 0x0000555555dae464 in arm_v7m_cpu_exec_interrupt (cs=0x555557945c00, interrupt_request=2) at ../qemu/target/arm/tcg/cpu32.c:120
120 cc->tcg_ops->do_interrupt(cs);
cc->tcg_ops->do_interrupt(cs)がコールされて割り込みが発生しています。
次に割り込みハンドラの決定を見ていきます。
qemu$ fgrep "loaded new PC" . -wnrI
./target/arm/tcg/m_helper.c:712: qemu_log_mask(CPU_LOG_INT, "...loaded new PC 0x%x\n", *pvec);
655 static bool arm_v7m_load_vector(ARMCPU *cpu, int exc, bool targets_secure,
656 uint32_t *pvec)
...
712 qemu_log_mask(CPU_LOG_INT, "...loaded new PC 0x%x\n", *pvec);
arm_v7m_load_vector
で決定されるようです。
ブレークポイントを仕掛けてみると
(gdb) b arm_v7m_load_vector
Breakpoint 3 at 0x555555e4a0bd: file ../qemu/target/arm/tcg/m_helper.c, line 657.
(gdb) c
Continuing.
mps2.c(798) udp_recvfromdummy epoll_wait
[Thread 0x7ffff59ff6c0 (LWP 197519) exited]
Thread 4 "qemu-system-arm" hit Breakpoint 3, arm_v7m_load_vector (cpu=0x555557945c00, exc=15, targets_secure=false, pvec=0x7fff6fffe81c)
at ../qemu/target/arm/tcg/m_helper.c:657
657 {
なにやら怪しい引数があります。
試しに変更してみましょう。
(gdb) set exc=29
(gdb) del
Delete all breakpoints? (y or n) y
(gdb) c
Continuing.
[01fc] NetworkInterface.c(301) ether_rx_handler fr:0xfffffffd
ビンゴです。
つまり、割り込みが発生し、かつarm_v7m_load_vector
の第2引数が29で呼ばれるようにすれば
意図した割り込みハンドラが動作することがわかります。
バックトレースを見て順に調べていきます。
#0 arm_v7m_load_vector (cpu=0x555557945c00, exc=15, targets_secure=false, pvec=0x7fff6fffe81c) at ../qemu/target/arm/tcg/m_helper.c:657
#1 0x0000555555e4a9c7 in v7m_exception_taken (cpu=0x555557945c00, lr=4294967293, dotailchain=false, ignore_stackfaults=false)
at ../qemu/target/arm/tcg/m_helper.c:950
#2 0x0000555555e4e898 in arm_v7m_cpu_do_interrupt (cs=0x555557945c00) at ../qemu/target/arm/tcg/m_helper.c:2420
#3 0x0000555555dae464 in arm_v7m_cpu_exec_interrupt (cs=0x555557945c00, interrupt_request=2) at ../qemu/target/arm/tcg/cpu32.c:120
#4 0x0000555555f61164 in cpu_handle_interrupt (cpu=0x555557945c00, last_tb=0x7fff6fffe930) at ../qemu/accel/tcg/cpu-exec.c:865
#5 0x0000555555f61677 in cpu_exec_loop (cpu=0x555557945c00, sc=0x7fff6fffe9b0) at ../qemu/accel/tcg/cpu-exec.c:974
#6 0x0000555555f6170f in cpu_exec_setjmp (cpu=0x555557945c00, sc=0x7fff6fffe9b0) at ../qemu/accel/tcg/cpu-exec.c:1058
#7 0x0000555555f61796 in cpu_exec (cpu=0x555557945c00) at ../qemu/accel/tcg/cpu-exec.c:1084
#8 0x0000555555f87dae in tcg_cpus_exec (cpu=0x555557945c00) at ../qemu/accel/tcg/tcg-accel-ops.c:76
#9 0x0000555555f88497 in mttcg_cpu_thread_fn (arg=0x555557945c00) at ../qemu/accel/tcg/tcg-accel-ops-mttcg.c:95
#10 0x000055555618ec4b in qemu_thread_start (args=0x55555799bb70) at ../qemu/util/qemu-thread-posix.c:541
#11 0x00007ffff7884897 in start_thread () at /lib64/libc.so.6
#12 0x00007ffff790b6bc in clone3 () at /lib64/libc.so.6
該当の関数(v7m_exception_taken
)でexcの決定は以下のみです。
843 int exc;
844 bool push_failed = false;
845
846 armv7m_nvic_get_pending_irq_info(env->nvic, &exc, &targets_secure);
armv7m_nvic_get_pending_irq_info
810 void armv7m_nvic_get_pending_irq_info(NVICState *s,
811 int *pirq, bool *ptargets_secure)
812 {
813 const int pending = s->vectpending;
...
823 *pirq = pending;
824 }
env->nvic->vectpendingがハンドラを決定する要因のようです。
構造体まるごとコピーとかでなければ普通に=
で設定しているはずなのでgrepして調べていきます。
qemu$ grep vectpending . -wnrI | grep -E "vectpending = "
./hw/intc/armv7m_nvic.c:279: s->vectpending = pend_irq;
./hw/intc/armv7m_nvic.c:329: s->vectpending = pend_irq;
secureかno secureかの違いでどちらも同じ用途の関数の様です。
どちらが使われているか見ていきます。
(gdb) del
Delete all breakpoints? (y or n) y
(gdb) b nvic_recompute_state
Breakpoint 5 at 0x555555eb050a: file ../qemu/hw/intc/armv7m_nvic.c, line 293.
(gdb) b nvic_recompute_state_secure
Breakpoint 6 at 0x555555eb02d3: file ../qemu/hw/intc/armv7m_nvic.c, line 230.
(gdb) set $bcnt=0
(gdb) commands 5
Type commands for breakpoint(s) 5, one per line.
End with a line saying just "end".
>set $bcnt=$bcnt+1
>if ($bcnt < 100)
>c
>end
>end
(gdb) commands 6
Type commands for breakpoint(s) 6, one per line.
End with a line saying just "end".
>set $bcnt=$bcnt+1
>if ($bcnt < 100)
>c
>end
>end
(gdb) c
Continuing.
[Switching to Thread 0x7ffff7600ac0 (LWP 197474)]
Thread 1 "qemu-system-arm" hit Breakpoint 5, nvic_recompute_state (s=0x555557815a90) at ../qemu/hw/intc/armv7m_nvic.c:293
293 int pend_prio = NVIC_NOEXC_PRIO;
[Switching to Thread 0x7fff6ffff6c0 (LWP 197520)]
...
nosecureのみしか使われていないようです。
$ grep nvic_recompute_state gdb.txt| sort |uniq -c
34 Thread 1 "qemu-system-arm" hit Breakpoint 5, nvic_recompute_state (s=0x555557815a90) at ../qemu/hw/intc/armv7m_nvic.c:293
66 Thread 4 "qemu-system-arm" hit Breakpoint 5, nvic_recompute_state (s=0x555557815a90) at ../qemu/hw/intc/armv7m_nvic.c:293
NVICState->vectors[IRQ番号]
に情報が必要なようです。
290 static void nvic_recompute_state(NVICState *s)
...
310 VecInfo *vec = &s->vectors[i];
311
312 if (vec->enabled && vec->pending && vec->prio < pend_prio) {
313 pend_prio = vec->prio;
314 pend_irq = i;
gdbで見てみても
タイマー関連は情報が設定されているようですが
ethernet側は設定されていません。
(gdb) p env->nvic->vectors[29]
$3 = {prio = 0, enabled = 0 '\000', pending = 0 '\000', active = 0 '\000', level = 0 '\000'}
(gdb) p env->nvic->vectors[15]
$4 = {prio = 255, enabled = 1 '\001', pending = 0 '\000', active = 1 '\001', level = 0 '\000'}
設定は以下の1箇所だけのようです。
qemu$ grep "enabled = " hw/intc/armv7m_nvic.c -n
...
2366: s->vectors[startvec + i].enabled = setval;...
2339 static MemTxResult nvic_sysreg_write(void *opaque, hwaddr addr,
2340 uint64_t value, unsigned size,
2341 MemTxAttrs attrs)
2342 {
2343 NVICState *s = (NVICState *)opaque;
2344 uint32_t offset = addr;
...
2355 switch (offset) {
2360 case 0x180 ... 0x1bf: /* NVIC Clear enable */
2361 startvec = 8 * (offset - 0x180) + NVIC_FIRST_IRQ;
2362
2363 for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) {
2364 if (value & (1 << i) &&
2365 (attrs.secure || s->itns[startvec + i])) {
2366 s->vectors[startvec + i].enabled = setval;
2367 }
2368 }
2369 nvic_irq_update(s);
2363 にブレークを仕掛けてみるが入ってこない。
自分で設定する必要があるっぽい
ちなみに他の割り込みは以下で設定されているみたい
2578 static void armv7m_nvic_reset(DeviceState *dev)
...
2593 s->vectors[ARMV7M_EXCP_PENDSV].enabled = 1;
2594 s->vectors[ARMV7M_EXCP_SYSTICK].enabled = 1;
FreeRTOSから有効にする必要があるっぽい???
これにもそんなふうに書いてあるな
IRQをFreeRTOSから有効にしてみる。
29ではなく13っぽい
diff --git a/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/init/startup.c b/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/init/startup.c
index 28c657c8..e1c4b1db 100644
--- a/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/init/startup.c
+++ b/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/init/startup.c
@@ -167,6 +167,8 @@ void Default_Handler6( void )
}
}
+extern void ether_rx_handler(void);
+
const uint32_t * isr_vector[] __attribute__( ( section( ".isr_vector" ) ) ) =
{
( uint32_t * ) &_estack,
@@ -198,12 +200,13 @@ const uint32_t * isr_vector[] __attribute__( ( section( ".isr_vector" ) ) ) =
0, /* Dial Timer */
0, /* SPI0 SPI1 */
0, /* uart overflow 1, 2,3 */
- 0, /* Ethernet 13 */
+ (uint32_t*) ðer_rx_handler, /* Ethernet 13 */
};
void _start( void )
{
uart_init();
+ NVIC_EnableIRQ(13);
main( 0, 0 );
exit( 0 );
}
書き込み先は0xe000e100らしい
(gdb) b __NVIC_EnableIRQ
Breakpoint 1 at 0xe4: file /home/voyager/workspace/freertos/freertos_cm3/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/CMSIS/core_cm3.h, line 1510.
(gdb) c
Continuing.
...
Breakpoint 1, __NVIC_EnableIRQ (IRQn=ETHERNET_IRQn) at /home/voyager/workspace/freertos/freertos_cm3/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/CMSIS/core_cm3.h:1510
1510 if ((int32_t)(IRQn) >= 0)
(gdb) n
1513 NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
(gdb) p/x &NVIC->ISER[(((uint32_t)IRQn) >> 5UL)]
$3 = 0xe000e100
enabledは1になった。
(gdb) bt
#0 nvic_sysreg_write (opaque=0x555557815a90, addr=256, value=8192, size=4, attrs=...) at ../qemu/hw/intc/armv7m_nvic.c:2363
#1 0x0000555555f1e2fb in memory_region_write_with_attrs_accessor
(mr=0x555557816c50, addr=256, value=0x7fff6fffdf08, size=4, shift=0, mask=4294967295, attrs=...) at ../qemu/system/memory.c:518
#2 0x0000555555f1e517 in access_with_adjusted_size
(addr=256, value=0x7fff6fffdf08, size=4, access_size_min=1, access_size_max=4, access_fn=0x555555f1e1ff <memory_region_write_with_attrs_accessor>, mr=0x555557816c50, attrs=...) at ../qemu/system/memory.c:573
...
(gdb) l
2358 setval = 1;
2359 /* fall through */
2360 case 0x180 ... 0x1bf: /* NVIC Clear enable */
2361 startvec = 8 * (offset - 0x180) + NVIC_FIRST_IRQ;
2362
2363 for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) {
2364 if (value & (1 << i) &&
2365 (attrs.secure || s->itns[startvec + i])) {
2366 s->vectors[startvec + i].enabled = setval;
2367 }
(gdb) p s
$22 = (NVICState *) 0x555557815a90
(gdb) p s->vectors[29]
$23 = {prio = 0, enabled = 1 '\001', pending = 0 '\000', active = 0 '\000', level = 0 '\000'}
ちなみにここのstartvec
が16なので13を設定すると29になる。
次にpendingを設定している箇所を探す。
qemu$ grep "pending = " -n hw/intc/armv7m_nvic.c | grep -v ARMV7M_EXCP_ | grep -w pending
519: vec->pending = 0;
659: vec->pending = 1;
756: vec->pending = 1;
773: const int pending = s->vectpending;
793: vec->pending = 0;
813: const int pending = s->vectpending;
901: vec->pending = 1;
2391: s->vectors[startvec + i].pending = setval;
候補は4箇所なので順に見ていく
524 static void do_armv7m_nvic_set_pending(void *opaque, int irq, bool secure,
525 bool derived)
...
674 void armv7m_nvic_set_pending_lazyfp(NVICState *s, int irq, bool secure)
...
826 int armv7m_nvic_complete_irq(NVICState *s, int irq, bool secure)
...
2339 static MemTxResult nvic_sysreg_write(void *opaque, hwaddr addr,
2340 uint64_t value, unsigned size,
2341 MemTxAttrs attrs)
nvic_sysreg_write
はソフトからの割り込み発生っぽいので残りにブレークかけてみる
Thread 1 "qemu-system-arm" hit Breakpoint 1, do_armv7m_nvic_set_pending (opaque=0x555557815a90, irq=29, secure=false, derived=false)
at ../qemu/hw/intc/armv7m_nvic.c:543
543 NVICState *s = (NVICState *)opaque;
...
(gdb)
658 if (!vec->pending) {
659 vec->pending = 1;
660 nvic_irq_update(s);
661 }
(gdb) b 659
Breakpoint 4 at 0x555555eb110a: file ../qemu/hw/intc/armv7m_nvic.c, line 659.
(gdb) c
Continuing.
mps2.c(802) udp_recvfromdummy epoll_wait
[Thread 0x7ffff59ff6c0 (LWP 207444) exited]
Thread 1 "qemu-system-arm" hit Breakpoint 4, do_armv7m_nvic_set_pending (opaque=0x555557815a90, irq=29, secure=false, derived=false)
at ../qemu/hw/intc/armv7m_nvic.c:659
659 vec->pending = 1;
(gdb) n
660 nvic_irq_update(s);
(gdb) p s->vectors[29].pending
$3 = 1 '\001'
何もしなくても勝手に1になったぞ
で、その後0初期化。めっちゃ意味ない
Old value = 1 '\001'
New value = 0 '\000'
0x00007ffff795528a in __memset_avx2_unaligned_erms () from /lib64/libc.so.6
(gdb) bt
#0 0x00007ffff795528a in __memset_avx2_unaligned_erms () at /lib64/libc.so.6
#1 0x0000555555eb5d84 in armv7m_nvic_reset (dev=0x555557815a90) at ../qemu/hw/intc/armv7m_nvic.c:2583
#2 0x0000555555f92fff in device_transitional_reset (obj=0x555557815a90) at ../qemu/hw/core/qdev.c:780
#3 0x0000555555f94d56 in resettable_phase_hold (obj=0x555557815a90, opaque=0x0, type=RESET_TYPE_COLD) at ../qemu/hw/core/resettable.c:182
#4 0x0000555555f8dc9e in bus_reset_child_foreach (obj=0x55555781b5d0, cb=0x555555f94c1f <resettable_phase_hold>, opaque=0x0, type=RESET_TYPE_COLD)
at ../qemu/hw/core/bus.c:97
#5 0x0000555555f94a42 in resettable_child_foreach
(rc=0x5555575d4b10, obj=0x55555781b5d0, cb=0x555555f94c1f <resettable_phase_hold>, opaque=0x0, type=RESET_TYPE_COLD) at ../qemu/hw/core/resettable.c:96
#6 0x0000555555f94cdc in resettable_phase_hold (obj=0x55555781b5d0, opaque=0x0, type=RESET_TYPE_COLD) at ../qemu/hw/core/resettable.c:173
#7 0x0000555555f948e1 in resettable_assert_reset (obj=0x55555781b5d0, type=RESET_TYPE_COLD) at ../qemu/hw/core/resettable.c:60
#8 0x0000555555f94813 in resettable_reset (obj=0x55555781b5d0, type=RESET_TYPE_COLD) at ../qemu/hw/core/resettable.c:45
#9 0x0000555555f950b5 in resettable_cold_reset_fn (opaque=0x55555781b5d0) at ../qemu/hw/core/resettable.c:268
#10 0x0000555555f934ec in qemu_devices_reset (reason=SHUTDOWN_CAUSE_NONE) at ../qemu/hw/core/reset.c:84
#11 0x0000555555c61633 in qemu_system_reset (reason=SHUTDOWN_CAUSE_NONE) at ../qemu/system/runstate.c:494
(gdb) f 1
#1 0x0000555555eb5d84 in armv7m_nvic_reset (dev=0x555557815a90) at ../qemu/hw/intc/armv7m_nvic.c:2583
2583 memset(s->vectors, 0, sizeof(s->vectors));
(gdb) p s->vectors[29]
$8 = {prio = 0, enabled = 0 '\000', pending = 0 '\000', active = 0 '\000', level = 0 '\000'}
他のはその後来た
vPortSVCHandler
向けはソフトウェア割り込みみたい
Thread 4 "qemu-system-arm" hit Breakpoint 1, do_armv7m_nvic_set_pending (opaque=0x555557815a90, irq=11, secure=false, derived=false)
at ../qemu/hw/intc/armv7m_nvic.c:543
543 NVICState *s = (NVICState *)opaque;
(gdb) bt
#0 do_armv7m_nvic_set_pending (opaque=0x555557815a90, irq=11, secure=false, derived=false) at ../qemu/hw/intc/armv7m_nvic.c:543
#1 0x0000555555eb1153 in armv7m_nvic_set_pending (s=0x555557815a90, irq=11, secure=false) at ../qemu/hw/intc/armv7m_nvic.c:666
#2 0x0000555555e4e286 in arm_v7m_cpu_do_interrupt (cs=0x55555792cc80) at ../qemu/target/arm/tcg/m_helper.c:2241
#3 0x0000555555f60f91 in cpu_handle_exception (cpu=0x55555792cc80, ret=0x7fff6fffe91c) at ../qemu/accel/tcg/cpu-exec.c:753
#4 0x0000555555f6170b in cpu_exec_loop (cpu=0x55555792cc80, sc=0x7fff6fffe9b0) at ../qemu/accel/tcg/cpu-exec.c:970
#5 0x0000555555f61782 in cpu_exec_setjmp (cpu=0x55555792cc80, sc=0x7fff6fffe9b0) at ../qemu/accel/tcg/cpu-exec.c:1058
#6 0x0000555555f61809 in cpu_exec (cpu=0x55555792cc80) at ../qemu/accel/tcg/cpu-exec.c:1084
#7 0x0000555555f87e21 in tcg_cpus_exec (cpu=0x55555792cc80) at ../qemu/accel/tcg/tcg-accel-ops.c:76
#8 0x0000555555f8850a in mttcg_cpu_thread_fn (arg=0x55555792cc80) at ../qemu/accel/tcg/tcg-accel-ops-mttcg.c:95
#9 0x000055555618ecbe in qemu_thread_start (args=0x555557932a10) at ../qemu/util/qemu-thread-posix.c:541
#10 0x00007ffff7884897 in start_thread () at /lib64/libc.so.6
#11 0x00007ffff790b6bc in clone3 () at /lib64/libc.so.6
タイマーはqemuから来てる
(gdb) bt
#0 do_armv7m_nvic_set_pending (opaque=0x555557815a90, irq=15, secure=false, derived=false) at ../qemu/hw/intc/armv7m_nvic.c:553
#1 0x0000555555eb1153 in armv7m_nvic_set_pending (s=0x555557815a90, irq=15, secure=false) at ../qemu/hw/intc/armv7m_nvic.c:666
#2 0x0000555555eb5fc0 in nvic_systick_trigger (opaque=0x555557815a90, n=0, level=1) at ../qemu/hw/intc/armv7m_nvic.c:2664
#3 0x0000555555f9521d in qemu_set_irq (irq=0x5555579294d0, level=1) at ../qemu/hw/core/irq.c:44
#4 0x0000555555b93204 in qemu_irq_pulse (irq=0x5555579294d0) at /home/voyager/workspace/oss/qemu/include/hw/irq.h:22
#5 0x0000555555b9361d in systick_timer_tick (opaque=0x555557817b30) at ../qemu/hw/timer/armv7m_systick.c:55
#6 0x0000555555917b05 in ptimer_trigger (s=0x5555579f1bf0) at ../qemu/hw/core/ptimer.c:48
#7 0x0000555555917eba in ptimer_tick (opaque=0x5555579f1bf0) at ../qemu/hw/core/ptimer.c:195
#8 0x00005555561ac512 in timerlist_run_timers (timer_list=0x555557511840) at ../qemu/util/qemu-timer.c:576
#9 0x00005555561ac5bc in qemu_clock_run_timers (type=QEMU_CLOCK_VIRTUAL) at ../qemu/util/qemu-timer.c:590
コードはこんな感じ
(gdb) f 4
#4 0x0000555555b93204 in qemu_irq_pulse (irq=0x5555579294d0) at /home/voyager/workspace/oss/qemu/include/hw/irq.h:22
22 qemu_set_irq(irq, 1);
(gdb) l
17 qemu_set_irq(irq, 0);
18 }
19
20 static inline void qemu_irq_pulse(qemu_irq irq)
21 {
22 qemu_set_irq(irq, 1);
23 qemu_set_irq(irq, 0);
24 }
25
26 /* Returns an array of N IRQs. Each IRQ is assigned the argument handler and
のでタイマーと同じような感じでやればできそう?
こんな感じにしてみると
830 static void send_to_freertos(uint8_t *buf, size_t size)
831 {
832 (void)buf;
833 CPUState *cpu = CPU(ARM_CPU(first_cpu));
834 NVICState *nvic;
835 (void)nvic;
836
837 if (eth_irq)
838 {
839 d("irq fire cpu:%p n:%lu", cpu, size);
840 qemu_mutex_lock_iothread();
841
842 qemu_set_irq(eth_irq, 1);
843 cpu_interrupt(cpu, CPU_INTERRUPT_HARD);
844 qemu_set_irq(eth_irq, 0);
845 qemu_mutex_unlock_iothread();
846 }
847 }
通った
mps2.c(839) send_to_freertos irq fire cpu:0x562f1234d880 n:42
[2d50] NetworkInterface.c(301) ether_rx_handler fr:0xfffffffd
mps2.c(839) send_to_freertos irq fire cpu:0x562f1234d880 n:42
[2d50] NetworkInterface.c(301) ether_rx_handler fr:0xfffffffd
mps2.c(839) send_to_freertos irq fire cpu:0x562f1234d880 n:42
[2d50] NetworkInterface.c(301) ether_rx_handler fr:0xfffffffd
ここまでのコード
参考
https://sebastienbourdelin.com/2021/06/16/writing-a-custom-device-for-qemu/
https://tekuteku-embedded.xyz/2021/10/04/%E5%89%B2%E3%82%8A%E8%BE%BC%E3%81%BF%E5%87%A6%E7%90%86%E3%81%AE%E4%BB%95%E7%B5%84%E3%81%BF%E3%82%92%E7%90%86%E8%A7%A3%E3%81%99%E3%82%8B/