FreeRTOSを動かしてみる(11)

[前回](https://qiita.com/t-yano-tryhard/items/fd31955287b81d97dc0c)の続き
受信が怪しいけど一旦つなげてみる

の前に各種パラメータを確認する。

# 設定内容

![images](https://kroki.io/graphviz/svg/eNp9VN9vgjAQfvevaPqsBtC5DYJPZsmSJVs296RmKXAKkV-2xWmM__to-SFUWB9Ie9939931rnjBjpLURwt0GaB8URLvvYAiGy0XlrTE8CuMNqcZWIPClHiwYj5JwcZOcsJDxPg5zA80yWIPPLyRNJY5RXA3zBgH-vMWxNmpFBIrJA6ENpZmPKjNd35LQnfAG44NZ15gJE0bESQepKuS8_qxjvVnY6zPnsa6po31MsFbqJoK3AcaA1_HhJiaZmqGqeumYZiTieoUpKO58MyvS1wD9xuEa71zferBsb82L4uic3dpBZTSxAXGcIvBkgiqlL8XeXlh4pLQTxg3H_Xpg3KedWZ2l8oBoqw7E4G0E9j2q09ztXayqtALBfhcvn8pYg3BioHvCLS_rYbSoWKe_2ttvnEc03XxBnUJjebCfdMPFa0n7l5RvirXXXzzYUGjuezd4DYacojKtyfxbetQZR_Qw5D5EUR4qKqKgDfH-rTq4zVGUg6xHKbyBQfxMWBVAy_yd2AzEoElqJao3CqraYNEgKQbFPEttBXY9Q_QTzTV)

## IP関連
IPアドレスなどは図の通り
macアドレスは潰れた会社のを利用

## FreeRTOS <-> QEMU間

[前](https://qiita.com/t-yano-tryhard/items/f6992531359a914c13d9#%E3%83%9B%E3%82%B9%E3%83%88%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9%E3%81%A8%E3%81%AE%E9%80%9A%E4%BF%A1%E9%83%A8%E5%88%86)にも書いたこれ
| 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
1. 0x28000000 を1にする
1. 0x28000004 にデータサイズを記載する
1. 0x28000010 の位置からデータを貼り付ける
1. 0x28000000 を0にする
2. QEMU内の仮想デバイスは0x28000000 が 1から0になったタイミングで
0x28000004に記載のサイズ分をdummyプロセスのUDPサーバ(7145)へ送信する。

### QEMU -> FreeRTOS
1. 0x28100004 に相当するバッファにデータサイズを記載する
1. 0x28100010 に相当するバッファにデータを貼り付ける
1. IRQ(13)(*1)を発火させて割り込みを行う
1. FreeRTOSの割り込みハンドラが0x28100004のサイズ分のデータを
0x28100010から読み出す

(*1) qemu管理だと29だが、FreeRTOS上では13

## Linux上のアプリとダミープロセス間
アプリ <-> デバイスドライバは標準API

### デバイスドライバ -> ダミープロセス
システムコールされてデバドラに入って来たデータはUDPのポート<font color="red">7146</font>へ送信する。

### ダミープロセス -> デバイスドライバ
1. NICデバイスは別途キャラクタデバイスを持っており
open -> write -> closeでデータを書き込む。
2. 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に送れば２回もUDP挟む必要がないのだけど
所詮ロケハンなので良しとしよう。

## その他
- IPv6には非対応
- 並列送信には非対応


# 実装
- [改造QEMU](https://github.com/tyano461/qemu_an385_ex/commit/d0c293423fd960f4f9c491b31522e2fd69525105)
- [FreeRTOS](https://github.com/tyano461/freertos_cm3/commit/d10e49003395d85500f66304a17a68b0573b843e)
- [FreeRTOSのドライバ](https://github.com/tyano461/freertos_tcpv4/commit/7754d35c00712749d52fd1c3aa1fb801fc237895)
- [Linuxのデバイスドライバ](https://github.com/tyano461/virtether/commit/2d4495e43d84d76eca2930678acec2f236120dbe)

# 実行結果
先にLinux側を立ち上げて
```shell
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を起動すると
```shell
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`以上でないとだめらしい

```c
#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 );

```
なのでこんな感じにしました。
```c
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);
}
```

----
# 参考

http://www.picfun.com/RTOS/FreeRTOS09.html