前回の続き
受信が怪しいけど一旦つなげてみる
の前に各種パラメータを確認する。
設定内容
IP関連
IPアドレスなどは図の通り
macアドレスは潰れた会社のを利用
FreeRTOS <-> QEMU間
前にも書いたこれ
name | addr | size | details | remarks |
---|---|---|---|---|
wlock | 0x28000000 | 4 | 0:none 1:writing | FreeRTOS -> qemu |
wsize | 0x28000004 | 4 | ||
wdata | 0x28000010 | 0x100000 - 16 | to 0x280fffff | |
rlock | 0x28100000 | 4 | 0:none 1:writing | qemu -> FreeRTOS |
rsize | 0x28100004 | 4 | ||
rdata | 0x28100010 | 0x100000 - 16 | to 0x281fffff |
FreeRTOS -> QEMU
- 0x28000000 を1にする
- 0x28000004 にデータサイズを記載する
- 0x28000010 の位置からデータを貼り付ける
- 0x28000000 を0にする
- QEMU内の仮想デバイスは0x28000000 が 1から0になったタイミングで
0x28000004に記載のサイズ分をdummyプロセスのUDPサーバ(7145)へ送信する。
QEMU -> FreeRTOS
- 0x28100004 に相当するバッファにデータサイズを記載する
- 0x28100010 に相当するバッファにデータを貼り付ける
- IRQ(13)(*1)を発火させて割り込みを行う
- FreeRTOSの割り込みハンドラが0x28100004のサイズ分のデータを
0x28100010から読み出す
(*1) qemu管理だと29だが、FreeRTOS上では13
Linux上のアプリとダミープロセス間
アプリ <-> デバイスドライバは標準API
デバイスドライバ -> ダミープロセス
システムコールされてデバドラに入って来たデータはUDPのポート7146へ送信する。
ダミープロセス -> デバイスドライバ
- NICデバイスは別途キャラクタデバイスを持っており
open -> write -> closeでデータを書き込む。 - closeタイミングでデータを確定させる。
具体的にはnetif_receive_skb
をコールする。
QEMUとダミープロセス間
待ち受けはANYIPで、送信先はLoopback(127.0.0.1)にする。
direction | source port | target port |
---|---|---|
QEMU -> dummy process | ephemeral port | 7145 |
dummy process -> QEMU | ephemeral port | 7144 |
Linuxのデバドラから直でQEMUに送れば2回もUDP挟む必要がないのだけど
所詮ロケハンなので良しとしよう。
その他
- IPv6には非対応
- 並列送信には非対応
実装
実行結果
先にLinux側を立ち上げて
virtether$ ./run.sh 1
make -C /lib/modules/6.5.6-300.fc39.x86_64/build M=/home/voyager/workspace/freertos/virtether modules
make[1]: Entering directory '/usr/src/kernels/6.5.6-300.fc39.x86_64'
warning: the compiler differs from the one used to build the kernel
The kernel was built by: gcc (GCC) 13.2.1 20230918 (Red Hat 13.2.1-3)
You are using: gcc (GCC) 13.2.1 20231205 (Red Hat 13.2.1-6)
make[1]: Leaving directory '/usr/src/kernels/6.5.6-300.fc39.x86_64'
EXTRA_CLAGS="-g -DDEBUG"
insmod: ERROR: could not insert module virtether.ko: File exists
RTNETLINK answers: File exists
try:
ping -w1 192.168.100.2
sudo echo "aiueo" > /dev/veth_cdev
virtether$ echoback.c(83) main queue:(nil)
echoback.c(144) recv_main bind
FreeRTOSを起動すると
CORTEX_M3_MPS2_QEMU_GCC$ 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(666) add_extra_mmio_an385 added to 28000000
mps2.c(794) udp_recvfromdummy
mps2.c(282) mps2_common_init armv7m:0x5611aa220760 mms->armv7m:0x5611aa220760
[0000] main_tcp.c(23) main_tcp Build: 14:08:07, Dec 21 2023(9d5637f290cda3dd8bc60f4be6af90f882f63851) fr:0x1e7
[0000] NetworkInterface.c(138) pxFillInterfaceDescriptor mac:0 fr:0x53c7
[1c80] NetworkInterface.c(214) ether_task smph:0x20112d50 fr:0x2a1
[67ac] FreeRTOS_IP.c(1398) xSendEventStructToIPTask et:0 fr:0x573d
[67ac] FreeRTOS_IP.c(1418) xSendEventStructToIPTask fr:0x573d
[67ac] FreeRTOS_IP.c(1428) xSendEventStructToIPTask xReturn:1 fr:0x573d
[24b8] app_server.c(14) server_task fr:0x2a1
[24b8] app_server.c(31) vCreateTCPServerSocket sock:537996712 fr:0xda0d
[24b8] FreeRTOS_IP.c(1398) xSendEventStructToIPTask et:9 fr:0x995d
[24b8] FreeRTOS_IP.c(1418) xSendEventStructToIPTask fr:0x995d
[2cf0] app_client.c(28) client_task fr:0x2a1
[24b8] FreeRTOS_IP.c(1428) xSendEventStructToIPTask xReturn:1 fr:0x995d
[24b8] app_server.c(46) vCreateTCPServerSocket bind fr:0xda0d
[24b8] app_server.c(49) vCreateTCPServerSocket listen fr:0xda0d
勝手になんか飛んでるぞ
mps2.c(751) udp_send2dummy sz:42
[67ac] NetworkInterface.c(180) pfOutput ip:264a8c0 po:0 bp:0 len:42 r:1 data:
ff ff ff ff ff ff aa 00 02 aa bb cc 08 06 00 01
08 00 06 04 00 01 aa 00 02 aa bb cc c0 a8 64 02
00 00 00 00 00 00 c0 a8 64 02
fr:0x3d4d
Linux側でも受け取れてるっぽい
echoback.c(243) send_callback write:42
echoback.c(83) main queue:(nil)
echoback.c(179) recv_main cond signal
echoback.c(83) main queue:0x7f65b4000c60
echoback.c(87) main ff ff ff ff ff ff aa 00 02 aa bb cc 08 06 00 01
08 00 06 04 00 01 aa 00 02 aa bb cc c0 a8 64 02
00 00 00 00 00 00 c0 a8 64 02
よし、pingを飛ばすぞ
virtether$ ping -w1 192.168.100.2
PING 192.168.100.2 (192.168.100.2) 56(84) bytes of data.
echoback.c(262) send_udp sendto(98): 98
echoback.c(179) recv_main cond signal
echoback.c(83) main queue:0x7f3ce4000df0
echoback.c(87) main aa 00 02 11 22 33 aa 00 02 aa bb cc 08 00 45 00
00 54 91 ec 00 00 40 01 9f 68 c0 a8 64 02 c0 a8
64 01 00 00 e5 83 00 44 00 01 e2 d7 83 65 00 00
00 00 e8 26 0d 00 00 00 00 00 10 11 12 13 14 15
16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25
26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35
36 37
echoback.c(243) send_callback write:98
echoback.c(83) main queue:(nil)
64 bytes from 192.168.100.2: icmp_seq=1 ttl=64 time=31.0 ms
--- 192.168.100.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 30.959/30.959/30.959/0.000 ms
通った
小ネタ
semaphoreで起こすやり方は割り込みハンドラの優先度がconfigMAX_SYSCALL_INTERRUPT_PRIORITY
以上でないとだめらしい
#define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), ( pxHigherPriorityTaskWoken ) )
160 #ifdef configASSERT
161 void vPortValidateInterruptPriority( void );
162 #define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() vPortValidateInterruptPriority()
163 #endif
1212 BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue,
1213 BaseType_t * const pxHigherPriorityTaskWoken )
...
1250 portASSERT_IF_INTERRUPT_PRIORITY_INVALID();
240 ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue;
...
607 void vPortValidateInterruptPriority( void )
608 {
609 extern uint32_t ulPortGetIPSR( void );
610 uint32_t ulCurrentInterrupt;
611 uint8_t ucCurrentPriority;
612
613 ulCurrentInterrupt = ulPortGetIPSR();
614
615 /* Is the interrupt number a user defined interrupt? */
616 if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER )
617 {
618 /* Look up the interrupt's priority. */
619 ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ];
...
644 configASSERT( ucCurrentPriority >= ucMaxSysCallPriority );
なのでこんな感じにしました。
static void set_eth_irq(void)
{
NVIC_EnableIRQ(ETHERNET_IRQn);
NVIC->IP[ETHERNET_IRQn] = configMAX_SYSCALL_INTERRUPT_PRIORITY;
}
void _start(void)
{
uart_init();
set_eth_irq();
main(0, 0);
exit(0);
}
参考