目次: Zephyr
Zephyr RISC-V SMP対応にチャレンジしてます。
Zephyrは既に他のアーキテクチャでSMP対応されていますから、真似するだけの何がチャレンジなの?簡単でしょ?と思われるかもしれません。その考え方は間違ってないんですが、少なくとも私にとっては「余裕」と言えるほど簡単ではなさそうです。
RISC-V向けの実装はAArch32をパクっているようで「お前、シングルコアだろ?な?」的な実装がそこら中にあり、SMPを有効にした途端、猛烈にコンパイルエラーが出ます。
一個一個見てますが、先ほど、コンテキストスイッチから直さないとダメなことに気づきました。こりゃ先は長そうだ〜……。
目次: Windows
今に始まったことではないのですが、Windows 10が突然高負荷に陥ってしまうことがあります。大抵Windows Updateが裏で動いているだけのことが多いですが、今日は様子が違ったのでメモしておきます。
いつもと違う点、その1は、Systemプロセスが高負荷に陥っていることです。Windows Updateが裏で実行されている場合、マウスを動かしたりキーボードに触ったりすると負荷が下がります。今回はマウスを触っても負荷が下がりません。
Process Explorerで見るとPsReturnProcessNonPagedPoolQuota() という関数がめちゃくちゃ頑張っていました。なんでしょうね?これ?
NVIDIAがArm全株式買収、ソフトバンクGから最大400億ドルで - ケータイWatch を読んで。
NVIDIAがArm買収です。買収額の2/3は株式交換で、ソフトバンクはNVIDIAの大株主になります。買収が成立すれば、ソフトバンクは1兆円超の利益を得ます。
ArmがNVIDIA配下となれば、NVIDIAと思い切り食い合う、Ethos(エッジAIプロセッサ)とMali(モバイル向けGPU)はゴミ箱行きですかね?ディスコンにしなくても、まともに開発しなくなりそうです。
さらにNVIDIAはNVIDIA GPUをライセンスするメリットがない(自分で作れる)ので、組み込み業界はImagination PowerVRとArm Maliという2大モバイルGPU IPを揃って失ってしまう悲しい未来になりそうです。
他陣営(RISC-Vとか)はまともなGPU IPがありませんし、Armがどんな地獄になろうとも、グラフィック必須のスマホ系SoCベンダーに逃げ場はなく、Armで作り続けるしかありません。
Armの築いたエコシステムがArmごとひっくり返されるとは思いませんでしたね。耐震構造のビルを建てていたら、いきなり一面の海になるような……バグったゲームみたいですが、現実なんですよね。
中国はImaginationごとPowerVRを買ってるので、ArmもしくはRISC-V用にPowerVRを売り出す可能性はあります(もう売ってる?)けど、PowerVRって性能的に今の時代のGPUに追いついているんでしょうか??
メモ: 技術系?の話はFacebookから転記しておくことにした。
目次: FreeRTOS
FreeRTOSにRISC-V QEMU virtpcのパッチをぶん投げてみました。やり方はGitHubでプルリクエストを送れば良いみたいです(説明へのリンク(FreeRTOS/CONTRIBUTING.md))。
FreeRTOSには既にQMEUのプロジェクトはある(SiFive HiFive1エミュレーション環境向けのEclipseプロジェクトがある)から要らないよ!?と言われることが目に浮かびますが、それならそれで良し。今後なにかの役に立つでしょう。
FreeRTOSはインデントやコード記法が非常に特徴的で、とても書きにくかったです。
void vFunc( int aaa )
{
int xBbb;
if( aaa )
{
/* do something */
xBbb = 0;
}
}
クセが強すぎる。何でこんな記法にしたのやら……??
メモ: 技術系?の話はFacebookから転記しておくことにした。色々と加筆。
自動車業界(だけじゃないですが)で重宝されているMATLABを家で使えることになったので、インストールしてみました。
ダウンロードは Mathworksのサイトからできますが、たぶん会社や学校からもらえるアカウント、アクティベーション番号が必要です。MATLABは個人でも買えますがメチャクチャ値段が高く、私は買う気は起きません……。
MATLABはDebian 9, Debian 10には対応しています(System Requirements for MATLAB R2020a - MATLAB & Simulink)が、Debian Testingには対応していません。インストーラを起動した瞬間にクラッシュします。
$ cd matlab_archive $ unzip matlab_R2020a_glnxa64.zip $ ./install terminate called after throwing an instance of 'std::runtime_error' what(): Unable to launch the MATLABWindow application Aborted
ありがたいことに、Linux向けのMATLABインストーラは通常版とlegacy版が同梱されています。
$ cd matlab_archive $ unzip matlab_R2020a_glnxa64.zip $ bin/glnxa64/install_unix_legacy
推奨された使い方ではないと思いますがlegacy版ならばインストーラが動きます。質問にはハイハイ答えておけば、そんなに問題ないはずです。
MATLABを起動するときに変なエラーが出ます。
$ matlab MATLAB is selecting SOFTWARE OPENGL rendering. matlab_install_dir/bin/glnxa64/jcef_helper: symbol lookup error: /usr/lib/x86_64-linux-gnu/libpango-1.0.so.0: undefined symbol: g_ptr_array_copy
このエラーはMATLABの動的ライブラリの構成がおかしいことが原因です。libglib-2.0.soをMATLAB内部に抱えているのですが、libpango-1.0.soはシステム側を使うため、バージョンの非互換が発生します。libpango-1.0.soも内部に抱えれば良いのに??何だか中途半端な作りですね。
$ ldd bin/glnxa64/jcef_helper | grep glib libglib-2.0.so.0 => matlab_install_dir/bin/glnxa64/../../cefclient/sys/os/glnxa64/libglib-2.0.so.0 (0x00007f43ac828000) ★MATLAB内部に抱えているライブラリをダイナミックリンクする $ cd matlab_install_dir/cefclient/sys/os/glnxa64 $ mv libglib-2.0.so _libglib-2.0.so $ mv libglib-2.0.so.0 _libglib-2.0.so.0 $ mv libglib-2.0.so.0.5600.1 _libglib-2.0.so.0.5600.1 $ ldd bin/glnxa64/jcef_helper | grep glib libglib-2.0.so.0 => /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0 (0x00007f10b2e6d000) ★システム側のライブラリをダイナミックリンクする
MATLABの内部で抱えているlibglib-2.0.soを無視して、システム側のlibglib-2.0.soをダイナミックリンクすればエラーは出ません。これも推奨された使い方ではないと思いますが、とりあえず動いたのでめでたしめでたし。
目次: FreeRTOS
前回UARTドライバとmain() 関数を実装しました。今回はmain() に至るまでのブート部分を実装します。ざっくり言うとアセンブラで書いたスタートコードと、リンカスクリプトが必要です。
RISC-V QEMU virtマシンのメモリマップは下記のようになっています。使わないハードウェアは載せていません。
今回はRAMの一部をROMの代わりとして使います。0x80000000は本来RAMですがROMの代わりとして扱います。0x80080000以降をRAMとして扱います。本当はSPI Flash ROMを使ったほうが良いですが、手抜き実装です。リンカスクリプトは下記のようにしました。
OUTPUT_ARCH( "riscv" )
ENTRY( _start )
MEMORY
{
rom (rxa) : ORIGIN = 0x80000000, LENGTH = 512K
ram (wxa) : ORIGIN = 0x80080000, LENGTH = 512K
}
SECTIONS
{
.init :
{
_text = .;
KEEP (*(SORT_NONE(.init)))
} >rom AT>rom
.text :
{
*(.text.unlikely .text.unlikely.*)
*(.text.startup .text.startup.*)
*(.text .text.*)
*(.gnu.linkonce.t.*)
} >rom AT>rom
.fini :
{
KEEP (*(SORT_NONE(.fini)))
_etext = .;
} >rom AT>rom
.rodata.align :
{
. = ALIGN(4);
_rodata = .;
} >rom AT>rom
.rodata.start :
{
_rodata_lma = LOADADDR(.rodata.start);
} >rom AT>rom
.rodata :
{
*(.rdata)
*(.rodata .rodata.*)
*(.gnu.linkonce.r.*)
. = ALIGN(4);
_erodata = .;
} >rom AT>rom
.data.align :
{
. = ALIGN(4);
_data = .;
} >ram AT>rom
.data.start :
{
_data_lma = LOADADDR(.data.start);
} >ram AT>rom
.data :
{
*(.data .data.*)
*(.gnu.linkonce.d.*)
. = ALIGN(8);
PROVIDE( __global_pointer$ = . + 0x800 );
*(.sdata .sdata.*)
*(.sdata2 .sdata2.*)
*(.gnu.linkonce.s.*)
. = ALIGN(8);
*(.srodata.cst16)
*(.srodata.cst8)
*(.srodata.cst4)
*(.srodata.cst2)
*(.srodata .srodata.*)
. = ALIGN(4);
_edata = .;
} >ram AT>rom
.bss.align :
{
. = ALIGN(4);
_bss = .;
} >ram AT>rom
.bss.start :
{
_bss_lma = LOADADDR(.bss.start);
} >ram AT>rom
.bss :
{
*(.sbss*)
*(.gnu.linkonce.sb.*)
*(.bss .bss.*)
*(.gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} >ram AT>rom
. = ALIGN(8);
_end = .;
.stack :
{
. = ALIGN(16);
_stack0_bottom = .;
. += __stack_size;
_stack0_top = .;
} >ram AT>ram
}
ビルドしたバイナリをnmやreadelfで見るときわかりやすくするために、あえて変なセクション(.*.align, .*.start)をいくつか作っています。このように見えます。
$ riscv64-unknown-elf-nm -n a.out | less ★.bss.alignにALIGN(4) と書いたとき 80002ea8 R __clz_tab 80002fa8 A _data_lma 80002fa8 R _erodata ★.rodataの終わり(ROM領域を0x80000000としている) 80002fb8 A _bss_lma 80080000 D _data ★.dataの始まり(RAM領域を0x80080000としている) 80080000 D pullNextTime 80080008 D uxTimerIncrementsForOneTick 8008000c D xISRStackTop 80080010 B _bss ★.bssの始まり ★4bytes alignになっている(.dataの終わりと連続している) 80080010 D _edata ★.dataの終わり 80080010 b xQueue ... ★.data.alignにALIGN(2048) と書いたとき 80002ca8 R __clz_tab 80002da8 A _data_lma 80002da8 R _erodata ★.rodataの終わり(ROM領域を0x80000000としている) 80002db8 A _bss_lma 80080000 D _data ★.dataの始まり(RAM領域を0x80080000としている) 80080000 D pullNextTime 80080008 D uxTimerIncrementsForOneTick 8008000c D xISRStackTop 80080010 D _edata ★.dataの終わり 80080800 D __global_pointer$ 80080800 B _bss ★.bssの始まり ★2KB alignになっている(.dataの終わりと連続して「いない」) 80080800 b xQueue ... $ riscv64-unknown-elf-readelf -a a.out | less ★.bss.alignにALIGN(4) と書いたとき Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .init PROGBITS 80000000 001000 00006c 00 AX 0 0 1 [ 2] .text PROGBITS 80000100 001100 002d2c 00 AX 0 0 256 [ 3] .rodata.align PROGBITS 80002e2c 004010 000000 00 WA 0 0 1 [ 4] .rodata PROGBITS 80002e2c 003e2c 00017c 00 A 0 0 4 [ 5] .data.align PROGBITS 80080000 004010 000000 00 WA 0 0 1 [ 6] .data PROGBITS 80080000 004000 000010 00 WA 0 0 4 [ 7] .bss.align NOBITS 80080010 000000 000000 00 WA 0 0 1 [ 8] .bss NOBITS 80080010 004010 0040c0 00 WA 0 0 16 ★4bytes alignになっている [ 9] .stack NOBITS 800840d0 0040d0 00012c 00 WA 0 0 1 ★.data.alignにALIGN(2048) と書いたとき ection Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .init PROGBITS 80000000 001000 000068 00 AX 0 0 1 [ 2] .text PROGBITS 80000100 001100 002b2c 00 AX 0 0 256 [ 3] .rodata.align PROGBITS 80002c2c 004010 000000 00 WA 0 0 1 [ 4] .rodata PROGBITS 80002c2c 003c2c 00017c 00 A 0 0 4 [ 5] .data.align PROGBITS 80080000 004010 000000 00 WA 0 0 1 [ 6] .data PROGBITS 80080000 004000 000010 00 WA 0 0 4 [ 7] .bss.align NOBITS 80080010 004010 0007f0 00 WA 0 0 1 [ 8] .bss NOBITS 80080800 004800 0040c0 00 WA 0 0 16 ★2KB alignになっている [ 9] .stack NOBITS 800848c0 0048c0 00012c 00 WA 0 0 1
ALIGN(4) をALIGN(2048) など、大きめの値に変えたときの様子を載せました。変なセクション .*.alignがアドレスのアラインメントをしている様子が nmでもreadelfでもわかりやすいですよね?だめ?上記は .bssの例ですが、他のセクションでも同様です。
ブートコードはアセンブラで書く必要があります。
// freertos/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/start.S
#include "riscv-reg.h"
#if __riscv_xlen == 32
#define REGSIZE 4
#define LOAD lw
#define STOR sw
#elif __riscv_xlen == 64
#define REGSIZE 8
#define LOAD ld
#define STOR sd
#endif /* __riscv_xlen */
.section .init
.globl _start
.type _start,@function
_start:
.cfi_startproc
.cfi_undefined ra
.option push
.option norelax
la gp, __global_pointer$
.option pop
la sp, _stack0_top
# Load data section
la a0, _data_lma
la a1, _data
la a2, _edata
bgeu a1, a2, 2f
1:
LOAD t0, (a0)
STOR t0, (a1)
addi a0, a0, REGSIZE
addi a1, a1, REGSIZE
bltu a1, a2, 1b
2:
# Clear bss section
la a0, _bss
la a1, _ebss
bgeu a0, a1, 2f
1:
STOR zero, (a0)
addi a0, a0, REGSIZE
bltu a0, a1, 1b
2:
/* argc, argv, envp is 0 */
li a0, 0
li a1, 0
li a2, 0
j main
.cfi_endproc
実装は非常に単純です。.dataをROM領域からRAM領域にコピーし、.bssを0クリアしてmain() に飛ぶだけです。
以上の実装でビルドして(makeするだけ)、動かします。
$ qemu-system-riscv32 -nographic -machine virt -net none -chardev stdio,id=con,mux=on -serial chardev:con -mon chardev=con,mode=readline -bios none -kernel a.out Hello FreeRTOS! Blink Blink Blink ...
動きましたね。良かった良かった。 改行が余計に入っちゃってるのが気になる場合はmain.cの "Blink\r\n" を "Blink\n" にすると治ります。
目次: FreeRTOS
前回は既にあるデモアプリのビルドシステムを組み替えてRISC-V QEMU sifive_eマシン(SiFive HiFive1相当)上でFreeRTOSを動かしました。今回はRISC-V QEMU virtマシン上でFreeRTOSを動かします。
マシンの違いですが、まずUARTが違います。HiFive1はSiFive UART、virtは16550です。UARTを動かすための簡易的なドライバを書きます。
// freertos/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/ns16550.c
#include <stdint.h>
#include "ns16550.h"
/* register definitions */
#define REG_RBR 0x00 /* Receiver buffer reg. */
#define REG_THR 0x00 /* Transmitter holding reg. */
#define REG_IER 0x01 /* Interrupt enable reg. */
#define REG_IIR 0x02 /* Interrupt ID reg. */
#define REG_FCR 0x02 /* FIFO control reg. */
#define REG_LCR 0x03 /* Line control reg. */
#define REG_MCR 0x04 /* Modem control reg. */
#define REG_LSR 0x05 /* Line status reg. */
#define REG_MSR 0x06 /* Modem status reg. */
#define REG_SCR 0x07 /* Scratch reg. */
#define REG_BRDL 0x00 /* Divisor latch (LSB) */
#define REG_BRDH 0x01 /* Divisor latch (MSB) */
/* Line status */
#define LSR_DR 0x01 /* Data ready */
#define LSR_OE 0x02 /* Overrun error */
#define LSR_PE 0x04 /* Parity error */
#define LSR_FE 0x08 /* Framing error */
#define LSR_BI 0x10 /* Break interrupt */
#define LSR_THRE 0x20 /* Transmitter holding register empty */
#define LSR_TEMT 0x40 /* Transmitter empty */
#define LSR_EIRF 0x80 /* Error in RCVR FIFO */
uint8_t readb( uintptr_t addr )
{
return *((uint8_t *) addr );
}
void writeb( uint8_t b, uintptr_t addr )
{
*((uint8_t *) addr ) = b;
}
void ns16550_out( struct device *dev, unsigned char c )
{
uintptr_t addr = dev->addr;
while ( (readb( addr + REG_LSR ) & LSR_THRE) == 0 ) {
/* busy wait */
}
writeb( c, addr + REG_THR );
}
このドライバは初期化も設定も何もせず、いきなり出力だけ行う手抜き実装です。QEMUでは動きますが、おそらく実機では動かないでしょう。
元のコードはmain.cにSiFive UART用のシリアルの出力コードが入っているので、これを削ります。またmain.cとmain_blinky.c, main_full.cに別れていますが、あまり複雑なデモは要りません。main_full.cの方は削って、main.cに統合します。
// freertos/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/main.c
static void prvQueueSendTask( void *pvParameters )
{
TickType_t xNextWakeTime;
const unsigned long ulValueToSend = 100UL;
BaseType_t xReturned;
/* Remove compiler warning about unused parameter. */
( void ) pvParameters;
/* Initialise xNextWakeTime - this only needs to be done once. */
xNextWakeTime = xTaskGetTickCount();
for( ;; )
{
/* Place this task in the blocked state until it is time to run again. */
vTaskDelayUntil( &xNextWakeTime, mainQUEUE_SEND_FREQUENCY_MS );
/* Send to the queue - causing the queue receive task to unblock and
toggle the LED. 0 is used as the block time so the sending operation
will not block - it shouldn't need to block as the queue should always
be empty at this point in the code. */
xReturned = xQueueSend( xQueue, &ulValueToSend, 0U );
configASSERT( xReturned == pdPASS );
}
}
/*-----------------------------------------------------------*/
static void prvQueueReceiveTask( void *pvParameters )
{
unsigned long ulReceivedValue;
const unsigned long ulExpectedValue = 100UL;
const char * const pcPassMessage = "Blink\r\n";
const char * const pcFailMessage = "Unexpected value received\r\n";
/* Remove compiler warning about unused parameter. */
( void ) pvParameters;
for( ;; )
{
/* Wait until something arrives in the queue - this task will block
indefinitely provided INCLUDE_vTaskSuspend is set to 1 in
FreeRTOSConfig.h. */
xQueueReceive( xQueue, &ulReceivedValue, portMAX_DELAY );
/* To get here something must have been received from the queue, but
is it the expected value? If it is, toggle the LED. */
if( ulReceivedValue == ulExpectedValue )
{
puts( pcPassMessage );
ulReceivedValue = 0U;
}
else
{
puts( pcFailMessage );
}
}
}
/*-----------------------------------------------------------*/
int main( void )
{
puts( "Hello FreeRTOS!" );
/* Create the queue. */
xQueue = xQueueCreate( mainQUEUE_LENGTH, sizeof( uint32_t ) );
if( xQueue != NULL )
{
/* Start the two tasks as described in the comments at the top of this
file. */
xTaskCreate( prvQueueReceiveTask, "Rx", configMINIMAL_STACK_SIZE * 2U, NULL,
mainQUEUE_RECEIVE_TASK_PRIORITY, NULL );
xTaskCreate( prvQueueSendTask, "TX", configMINIMAL_STACK_SIZE * 2U, NULL,
mainQUEUE_SEND_TASK_PRIORITY, NULL );
}
vTaskStartScheduler();
return 0;
}
// freertos/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/riscv-virt.c
int puts( const char *s )
{
struct device dev;
size_t i;
dev.addr = NS16550_ADDR;
for (i = 0; i < strlen(s); i++)
{
ns16550_out( &dev, s[i] );
}
ns16550_out( &dev, '\n' );
return 0;
}
別にPOSIX信者というわけでもないんですが、ついでにputs() もどきを実装しておきました。
続きはまた今度。
< | 2020 | > | ||||
<< | < | 09 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | - | - | - |
合計:
本日: