目次: 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() もどきを実装しておきました。
続きはまた今度。
この記事にコメントする
目次: FreeRTOS
以前RTOS界の新星Zephyrを調べて、新たなRISC-Vボードの定義を作りました。今回はRTOSの老舗FreeRTOSを調べます。FreeRTOSはGPLv2で開発されていましたが、Amazonが買収した後はMITライセンスになっています。IoT分野での企業ユーザー(大抵コード公開を嫌がる)を重視したんでしょう。
$ git clone https://github.com/FreeRTOS/FreeRTOS freertos Cloning into 'freertos'... remote: Enumerating objects: 149823, done. Receiving objects: 0% (1/149823) remote: Total 149823 (delta 0), reused 0 (delta 0), pack-reused 149823 Receiving objects: 100% (149823/149823), 115.38 MiB | 8.29 MiB/s, done. Resolving deltas: 100% (107018/107018), done. Updating files: 100% (12962/12962), done. $ git submodule update --init --recursive
FreeRTOSのカーネルはFreeRTOS/Sourceに配置されており、リポジトリはhttps://github.com/FreeRTOS/FreeRTOS-Kernelです。
FreeRTOS上で動く何かを作成する場合はfreertos/FreeRTOS/Demoの下に作るルールになっているようです。たくさんのアーキテクチャ、ボード向けのコードが格納されています。統一感がなくて、どれを見たら良いのか良くわからないのが難点です。
RISC-V 32ビット用でしたら、以前Zephyr用に作成したツールチェーン(2020年1月31日の日記参照)が流用できます。ARMやそれ以外の環境でもCrosstool-NGを使えばたいてい作成できるはずです。
Demoディレクトリの下にはRISC-V QEMU向けのプロジェクト(正確にはSiFive HiFive1エミュレーション環境向け)が既に1つあります。FreeRTOS/Demo/RISC-V-Qemu-sifive_e-Eclipse-GCCです。このデモはEclipse向けになっているので、Makefile向けに作り直します。Eclipse関連のファイルを削除してMakefileを作成するだけです。
CROSS=riscv64-unknown-elf-
CC=$(CROSS)gcc
OBJCOPY=$(CROSS)objcopy
ARCH=$(CROSS)ar
RTOS_SOURCE_DIR=../../Source
DEMO_SOURCE_DIR=../Common/Minimal
LIBWRAP_SOURCE_DIR=./freedom-e-sdk/libwrap
CPPFLAGS = -g -O2 -Wall -march=rv32ima -mabi=ilp32 -mcmodel=medlow \
-fmessage-length=0 \
-ffunction-sections \
-fdata-sections \
-fno-builtin-printf \
-DportasmHANDLE_INTERRUPT=handle_trap \
-I . -I ../Common/include \
-I $(RTOS_SOURCE_DIR)/include \
-I $(RTOS_SOURCE_DIR)/portable/GCC/RISC-V \
-I $(RTOS_SOURCE_DIR)/portable/GCC/RISC-V/chip_specific_extensions/RV32I_CLINT_no_extensions \
\
-I freedom-e-sdk/include \
-I freedom-e-sdk/env \
-I freedom-e-sdk/env/freedom-e300-hifive1
CFLAGS =
ASFLAGS =
LDFLAGS = \
-march=rv32ima -mabi=ilp32 -mcmodel=medlow \
-Tfreedom-e-sdk/env/freedom-e300-hifive1/flash.lds \
-Xlinker --gc-sections \
-Xlinker --defsym=__stack_size=300
SRCS = \
main.c \
blinky_demo/main_blinky.c \
$(DEMO_SOURCE_DIR)/EventGroupsDemo.c \
$(DEMO_SOURCE_DIR)/TaskNotify.c \
$(DEMO_SOURCE_DIR)/TimerDemo.c \
$(DEMO_SOURCE_DIR)/blocktim.c \
$(DEMO_SOURCE_DIR)/dynamic.c \
$(DEMO_SOURCE_DIR)/recmutex.c \
$(RTOS_SOURCE_DIR)/event_groups.c \
$(RTOS_SOURCE_DIR)/list.c \
$(RTOS_SOURCE_DIR)/queue.c \
$(RTOS_SOURCE_DIR)/stream_buffer.c \
$(RTOS_SOURCE_DIR)/tasks.c \
$(RTOS_SOURCE_DIR)/timers.c \
$(RTOS_SOURCE_DIR)/portable/MemMang/heap_4.c \
$(RTOS_SOURCE_DIR)/portable/GCC/RISC-V/port.c
ASMS = \
$(RTOS_SOURCE_DIR)/portable/GCC/RISC-V/portASM.S \
\
freedom-e-sdk/env/start.S \
freedom-e-sdk/env/entry.S
OBJS = $(SRCS:.c=.o) $(ASMS:.S=.o)
a.out: $(OBJS) $(CRT0) Makefile
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -nostartfiles $(CRT0) $(LINKER_FLAGS) -o $@
clean:
rm -rf $(OBJS)
元のコードでは --defsym=__stack_size=350なんですが、そのまま使うとなぜか下記のリンクエラーが出るので、少しだけ減らしています。
x-tools/riscv64-unknown-elf/lib/gcc/riscv64-unknown-elf/10.2.0/../../../../riscv64-unknown-elf/bin/ld: section .stack VMA [0000000080003e00,0000000080003fff] overlaps section .bss VMA [0000000080000440,0000000080003ebb] collect2: error: ld returned 1 exit status make: *** [Makefile:105: rtosdemo.elf] Error 1
リンカースクリプトを見る限りHiFive1はRAMが16KBしかないようで、あまり大きな領域を取ろうとするとすぐに溢れてしまいます。
// freertos/FreeRTOS/Demo/RISC-V-Qemu-virt_GCC/freedom-e-sdk/env/freedom-e300-hifive1/flash.lds
MEMORY
{
flash (rxai!w) : ORIGIN = 0x20400000, LENGTH = 512M
ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 16K
}
Makefileを作ったらmakeし、動作確認します。
$ qemu-system-riscv32 -nographic -machine sifive_e -net none -chardev stdio,id=con,mux=on -serial chardev:con -mon chardev=con,mode=readline -bios none -kernel a.out StartingBlink Blink Blink Blink Blink ...
動作しました。QEMUを止めるまでBlinkという文字が延々と出続けます。最初のStartingに改行が入っていないのは元々です。理由は良くわかりません、作った人がミスっただけかな?
この記事にコメントする
目次: Zephyr
以前、RISC-V QEMUの -machine virtで、シリアルドライバ16550を使う設定を作りましたが、Zephyr 2.3.0で動かなくなってしまいました。悲しい。
変更点はレジスタアドレスシフト量の設定方法です。DT_NS16550_REG_SHIFTで設定する方式でしたが、デバイスツリーから設定するように変更されました。
commit 70a0063b69b06812c5726077646cffae3b8e199c
Author: Kumar Gala <kumar.gala@linaro.org>
Date: Fri Mar 27 06:03:59 2020 -0500
drivers: serial: uart_ns16550: Convert to new DT_INST macros
Convert older DT_INST_ macro use the new include/devicetree.h
DT_INST macro APIs.
Signed-off-by: Kumar Gala <kumar.gala@linaro.org>
// zephyr/drivers/serial/uart_ns16550.c
-#ifdef DT_INST_0_NS16550_REG_SHIFT
-#define UART_REG_ADDR_INTERVAL (1<<DT_INST_0_NS16550_REG_SHIFT)
+#if DT_INST_NODE_HAS_PROP(0, reg_shift)
+#define UART_REG_ADDR_INTERVAL (1<<DT_INST_PROP(0, reg_shift))
#endif
デバイスツリーに何を書けば良いのかは、デバイスツリーのドキュメント(ns16550.yaml)を見ましょう。プロパティ名と説明が書いてあります。
// zephyr/dts/bindings/serial/ns16550.yaml
properties:
reg:
required: true
reg-shift:
type: int
required: false
description: quantity to shift the register offsets by
// zephyr/dts/riscv/riscv32-virt.dtsi
...
uart0: serial@10000000 {
compatible = "ns16550";
reg = <0x10000000 0x100>;
clock-frequency = <3686400>;
label = "uart_0";
current-speed = <115200>;
reg-shift = <0>; /* ★これを追加★ */
};
// zephyr/soc/riscv/riscv-privilege/rv32-virt/soc.h
...
/* ★★下記定義は全て不要★★ */
#define DT_UART_NS16550_PORT_0_BASE_ADDR DT_INST_0_NS16550_BASE_ADDRESS
#define DT_UART_NS16550_PORT_0_BAUD_RATE DT_INST_0_NS16550_CURRENT_SPEED
#define DT_UART_NS16550_PORT_0_CLK_FREQ DT_INST_0_NS16550_CLOCK_FREQUENCY
#define DT_UART_NS16550_PORT_0_NAME DT_INST_0_NS16550_LABEL
#define DT_NS16550_REG_SHIFT 0
以上の修正を入れて動かします。
$ qemu-system-riscv32 -nographic -machine virt -net none -chardev stdio,id=con,mux=on -serial chardev:con -mon chardev=con,mode=readline -kernel zephyr/zephyr.elf -bios none
*** Booting Zephyr OS build zephyr-v2.3.0-2350-g2f294fcc2da8 ***
Hello World! QEMU RV32 virt board
動きました。良かった良かった。
この記事にコメントする
目次: Zephyr
Zephyr 2.3.0にバージョンアップしたところ、またHogeボードのビルドが通らなくなりました。
一点目はUARTドライバのCMakeListsです。zephyr_library_sources_if_kconfig() がなくなったため、書き方が変わりました。
commit 244f826e3c7333bb92fb53a65c50ee5cbd8a2ea0
Author: Carles Cufi <carles.cufi@nordicsemi.no>
Date: Fri Jul 31 13:52:40 2020 +0200
cmake: remove _if_kconfig() functions
This set of functions seem to be there just because of historical
reasons, stemming from Kbuild. They are non-obvious and prone to errors,
so remove them in favor of the `_ifdef()` ones with an explicit
`CONFIG_` condition.
Script used:
git grep -l _if_kconfig | xargs sed -E -i
"s/_if_kconfig\(\s*(\w*)/_ifdef(CONFIG_\U\1\E \1/g"
Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
# drivers/serial/CMakeLists.txt
zephyr_library_sources_if_kconfig(uart_spike.c)
下記に変更
zephyr_library_sources_ifdef(CONFIG_UART_SPIKE uart_spike.c)
二点目は整数型です。Zephyrはu8_t, u16_t, u32_tのような独自の整数型を持っていましたが、C99の型に置き換えられました。drivers/serial/uart_spike.cの実装を書き換える必要があります。
commit a1b77fd589dbe7284c17b029f251426a724abd47
Author: Kumar Gala <kumar.gala@linaro.org>
Date: Wed May 27 11:26:57 2020 -0500
zephyr: replace zephyr integer types with C99 types
git grep -l 'u\(8\|16\|32\|64\)_t' | \
xargs sed -i "s/u\(8\|16\|32\|64\)_t/uint\1_t/g"
git grep -l 's\(8\|16\|32\|64\)_t' | \
xargs sed -i "s/s\(8\|16\|32\|64\)_t/int\1_t/g"
Signed-off-by: Kumar Gala <kumar.gala@linaro.org>
三点目はDT_INST_0_SPIKE_UART_SPIKE_LABELマクロです。こいつは元々、訳のわからない名前で直しようがないので、SiFiveのシリアルドライバの履歴を参考に直します。履歴を見ると2回ほど変わっています。
マクロ名のルールはDT_INST_<INSTANCE>_<COMPAT>_<PROP> だったみたいです。今初めて知りました。やっぱりこの書き方は意味不明と思ったのか、DT_INST_PROP(0, label) という形式になりました。さらに今はDT_INST_LABEL(0) という形式に落ち着いています。
★★DT_INST_PROP(0, label) になったコミット
commit 8f84520130a346957ac2e2bdff1d6a51bca13af0
Author: Kumar Gala <kumar.gala@linaro.org>
Date: Tue Mar 10 17:24:43 2020 -0500
drivers: serial: uart_sifive: convert to new DT API
Use the new devicetree.h API instead of the legacy macros.
Signed-off-by: Kumar Gala <kumar.gala@linaro.org>
★★DT_INST_LABEL(0) になったコミット
commit 74d459fb66b10a5a0614a582fb0375d8b4a78c9e
Author: Kumar Gala <kumar.gala@linaro.org>
Date: Thu Apr 2 13:13:47 2020 -0500
drivers: serial: sifive: use DT_INST_LABEL macro
Replace a few cases that should have been DT_INST_LABEL instead.
Signed-off-by: Kumar Gala <kumar.gala@linaro.org>
このマクロの罠はそれだけに留まらず、ソースコードの先頭に下記マクロを定義する必要があります。依然として訳がわかりません。ZephyrはDviceTree周りの仕様が不安定です。
// zephyr/drivers/serial/uart_spike.c
#define DT_DRV_COMPAT spike_uart_spike
最後はリンカーです。これは元々のコードのコンフィグが間違っていたことに起因します。ROM領域がないのにCONFIG_XIPが有効になっていました。
$ ninja ... x-tools/riscv64-zephyr-elf/lib/gcc/riscv64-zephyr-elf/8.3.0/../../../../riscv64-zephyr-elf/bin/ld: invalid origin for memory region ROM collect2: error: ld returned 1 exit status ninja: build stopped: subcommand failed.
エラーメッセージからは何が原因か読み取れないですね。こういうときはビルドディレクトリのリンカースクリプト(zephyr/linker.cmd)をうまく行く場合と、うまく行かない場合で見比べます。
/* zephyr/build/zephyr/linker.cmd */
OUTPUT_ARCH("riscv")
OUTPUT_FORMAT("elf32-littleriscv")
MEMORY
{
ROM (rx) : ORIGIN = 541065216, LENGTH = 12582912
RAM (rwx) : ORIGIN = 0x80000000, LENGTH = ((16) << 10)
IDT_LIST (wx) : ORIGIN = 0xFFFFF7FF, LENGTH = 2K
}
/* zephyr/build/zephyr/linker.cmd */
OUTPUT_ARCH("riscv")
OUTPUT_FORMAT("elf32-littleriscv")
MEMORY
{
ROM (rx) : ORIGIN = ROM_BASE, LENGTH = ROM_SIZE /* ★★ここがおかしい★★ */
RAM (rwx) : ORIGIN = 0x80000000, LENGTH = ((32) << 10)
IDT_LIST (wx) : ORIGIN = 0xFFFFF7FF, LENGTH = 2K
}
このスクリプトは下記のファイルから生成されているようです。HogeボードはROM領域を使う前提ではないので、領域そのものが要りません。ROM領域を葬るにはCONFIG_XIPをnにすれば良さそうです。ファイルはboards/riscv/hoge/hoge_defconfigです。
// include/arch/riscv/common/linker.ld
MEMORY
{
#ifdef CONFIG_XIP
#if DT_NODE_HAS_COMPAT_STATUS(DT_CHOSEN(zephyr_flash), soc_nv_flash, okay)
#define ROM_BASE DT_REG_ADDR(DT_CHOSEN(zephyr_flash))
#define ROM_SIZE DT_REG_SIZE(DT_CHOSEN(zephyr_flash))
#elif DT_NODE_HAS_COMPAT_STATUS(DT_CHOSEN(zephyr_flash), jedec_spi_nor, okay)
/* For jedec,spi-nor we expect the spi controller to memory map the flash
* and for that mapping to be the second register property of the spi
* controller.
*/
#define SPI_CTRL DT_PARENT(DT_CHOSEN(zephyr_flash))
#define ROM_BASE DT_REG_ADDR_BY_IDX(SPI_CTRL, 1)
#define ROM_SIZE DT_REG_SIZE_BY_IDX(SPI_CTRL, 1)
#endif
ROM (rx) : ORIGIN = ROM_BASE, LENGTH = ROM_SIZE /* ★★CONFIG_XIPが無効ならこの行ごと消える★★ */
#endif
RAM (rwx) : ORIGIN = CONFIG_SRAM_BASE_ADDRESS, LENGTH = KB(CONFIG_SRAM_SIZE)
/* Used by and documented in include/linker/intlist.ld */
IDT_LIST (wx) : ORIGIN = 0xFFFFF7FF, LENGTH = 2K
}
以上の修正を入れて動かします。
$ qemu-system-riscv32 -nographic -machine spike -net none -chardev stdio,id=con,mux=on -serial chardev:con -mon chardev=con,mode=readline -kernel zephyr/zephyr.elf -bios none *** Booting Zephyr OS build zephyr-v2.3.0-2349-g0769bb760b2a *** Hello World! hoge
やっと動きました。良かった良かった。
この記事にコメントする
目次: Zephyr
最近のRISC-V向けQEMUでspike, virtを起動すると、下記のようなエラーで怒られてしまいます。
$ qemu-system-riscv32 -nographic -machine spike -net none -chardev stdio,id=con,mux=on -serial chardev:con -mon chardev=con,mode=readline -kernel zephyr/zephyr.elf qemu-system-riscv32: Unable to load the RISC-V firmware "opensbi-riscv32-spike-fw_jump.elf"
このエラーの原因はQEMU RISC-V向けの仕様変更によるものです。machineがspike, virtのときに、自由にBIOSを選べるように変わりました。ですがZephyrを起動する際には、特にBIOSは必要ないため、noneを指定すればOKです。
$ qemu-system-riscv32 -nographic -machine spike -net none -chardev stdio,id=con,mux=on -serial chardev:con -mon chardev=con,mode=readline -kernel zephyr/zephyr.elf -bios none *** Booting Zephyr OS build v2.2.0-rc1-123-gcaca3f60b012 *** Hello World! hoge
ZephyrだけでなくQEMUも日々進化しているのですね。
この記事にコメントする
目次: 車
先週、突然知らない番号から電話が掛かってきました。何だか焦った様子でした。要約すると「アパートの駐車場で隣に駐車している者です、駐車しようとして、あなたの車にぶつけてしまいました」とのことでした。車の様子を確認したところ、見事にバンパーの右前が取れていました。ああー……。
後日、保険屋さんと修理屋さん(スーパーオートバックス)から電話があり、車を引き取りに来る日時等を決めました。そして今日、レガシィさんはフロントバンパー修理のため旅立っていきました。
修理屋さん曰く「右ライトもAssy交換で新品になるだろう」とのこと。新品交換後の右ライトに対し、古ぼけた左ライトの明るさがアンバランスになるのはうまくないので、左ライトの研磨もお願いしました。残念ながら左ライトの研磨は保険が効かず自費になりそうですが、曇っていたライトが明るくなるから良しとしましょう。
代車はホンダのフィットでした。
総じて良い車だと思います。レガシィとの違いとしては、
昔のフィットに乗ったときアクセルオフ時のエンブレが急すぎて車酔いしたので、正直好きな車ではなかったのですが、今のフィットは素敵な車です。新しい車って確実に良くなってますね。
ADAS(前車の車間検知、車線逸脱の警告)も搭載されていて面白いです。たまに誤検知?するのか、何もないところで突然ピー!と警告音が鳴ったり、車線逸脱の警告とともにハンドルがンゴゴー!って言い出すのはご愛敬です。
この記事にコメントする
目次: ARM
先日KADHAS VIM2, VIM3を購入したため、ARMボードがさらに増え置き場がなくなりました。既存のボードをコンパクトに収納できないかと画策し、目を付けたのがROCK64です。現在ROCK64は純正ケースに入れて使っていますが、ギリシャの神殿を思わせる立派な柱と、無駄にでかいアクリル板のせいで、すっっごい邪魔です。
ROCK64はRaspberry Pi 3とほぼ同じ大きさですから、Raspberry Pi 3のケースを流用できるはずです。
改造のベースになるケースは、TinkerBoardやRaspberry Pi 3の格納で活躍しているPhysical Computing Labの3ple Decker Raspberry Piケース(公式サイトへのリンク)です。
Rasberry Pi 3は電源がUSB micro Bですが、ROCK64はACアダプタなので、全く形が合いません。アクリルケースの穴を削って広げる必要があります。もう一か所、ROCK64の個体差か(or単に設計の問題か?)ヘッドホンジャックがケースとズレていて、プラグが刺さらないため、これも直さないといけません。
削り方はリューターにプラスチック用のビットを付けてゴリゴリ削るだけです。厚さも面積も大したことないので、力業でどうにでもなると思います。アクリルを削ると変なにおいがしますね。焦げてるのかな……?
ケースを削り終わりました。ROCK64の端子がちゃんと外から見えています。
元よりROCK64用のケースではないので、様々な不都合が発生します。
Pi P5+ Busが使用不能になる点は諦めました。Rasberry Pi 3にはないピンヘッダなので致し方無しです。このピンヘッダから引き出していたS/PDIFが利用不可能になりました。さよならS/PDIFさん。
PWR, RESETボタンはケースを開ければ押せますが、面倒です。今後はPWRボタンに頼らない運用を考えるべきでしょう。
ボードが固定できない問題はどうにもなりませんでした。microSDの上部を抑えるケース側のツメがうまくハマりません。
もう少しきちんと調べてみると、ROCK64はRasberry Pi 3よりmicroSDスロットに厚みがあるせいで、microSDの上部を抑えるケース側のツメがうまくハマらないようです。
スロットの厚さはどうしようもないので、泣く泣くケース側のツメを折りました。
ツメがないので、microSDカードがケースに引っ掛かってギリギリケースに固定されている状態です。ROCK64の40pinのピンヘッダ(Pi-2 Bus)にジャンパケーブルを抜き差しすると、microSDにかなり力が掛かります。これは良くないですね……。
Pi-2 Busへのケーブル抜き差しは結構固いため、そのうちmicroSDスロットが変形するか、microSDが折れて壊れると思います。幸いなことに、今は頻繁にピンヘッダのケーブル抜き差しはしませんから、しばらくこの状態でも使えるでしょう。
この記事にコメントする
目次: ARM
TwitterでCavium Thunder X3はサーバ向け汎用ARM SoCではなくなるかもしれない、という話を見かけました。Cavium(今はMarvellに買収されました)は独自ARM CPUコアを作って頑張ってたメーカーです。
ARM自体はまだまだめげることなく、サーバ向けに売りたい(Armのサーバ向け戦略十年の計は実を結ぶか、新プロセッサ「Neoverse」 - MONOist)ようですが、肝心のARM系SoCメーカーやサーバベンダーがARM系サーバで成功している様子がないです。
以前Qualcomm Centriqも鳴り物入りでサーバ向けARM SoCに参入しましたが、2018年にあっさり撤退(Arm SoCの開発部門を秘かに閉鎖していたQualcomm - EE Times Japan)しています。モバイルの王者Qualcommをもってしても困難な道のようです。
サーバ向けプロセッサはIntelが9割取っているらしいので、崩すのはなかなか容易ではありませんね……。
サーバ向けとして発表されているARMメニーコア系SoCを列挙してみました。年代はチップがローンチされたおおよその時期です。間違ってたらごめんなさい。
こんな感じですかね?SC2A11はホームページでサーバ向けと謳っていたので、リストに入れてます。でもCA53コアなので、性能的にはThunder X2辺りと並べるのはちょっと厳しい……かな?
この記事にコメントする
目次: ALSA
デスクトップPCにはスピーカーを繋いでいませんが、たまに音声再生を確認したいことがあります。スピーカーを繋ぎ変えても良いですが、ALSAのループバックデバイスを使うと簡単に音声を転送したり、ファイルに記録したりできます。
ループバックデバイスとは再生した音声がそのまま戻ってきて(ループバック)録音できるデバイスのことです。ヘッドフォンの出力端子を、マイクロフォンの入力端子に繋いでループさせた状態を想像してもらうとわかりやすいと思います。
ALSAのループバックデバイス(aloop)は、ALSAの標準的な機能です。普通の環境だとロードされていないはずなので、modprobeでロードします。
# modprobe snd-aloop $ cat /proc/asound/pcm 00-03: HDMI 0 : HDMI 0 : playback 1 00-07: HDMI 1 : HDMI 1 : playback 1 00-08: HDMI 2 : HDMI 2 : playback 1 00-09: HDMI 3 : HDMI 3 : playback 1 00-10: HDMI 4 : HDMI 4 : playback 1 01-00: ALC1220 Analog : ALC1220 Analog : playback 1 : capture 1 01-01: ALC1220 Digital : ALC1220 Digital : playback 1 01-02: ALC1220 Alt Analog : ALC1220 Alt Analog : capture 1 02-00: Loopback PCM : Loopback PCM : playback 8 : capture 8 ★このデバイスhw:2を使用する 02-01: Loopback PCM : Loopback PCM : playback 8 : capture 8
ロードし終わるとPCMデバイスが1つ増えます。上記の場合は02-xx(02-00と02-01)が増えています。
ループバックデバイスはサブデバイス0が再生用、サブデバイス1が録音用となっています。先ほどの例でいうと、再生時はhw:2,0を使い、録音時はhw:2,1を使います。
$ aplay test.wav -D hw:2,0 Playing WAVE 'test.wav' : Signed 16 bit Little Endian, Rate 48000 Hz, Stereo
#### ファイルに記録する場合 $ arecord -D hw:2,1 -r 48000 -f S16_LE -c 2 test2.wav Recording WAVE 'test2.wav' : Signed 16 bit Little Endian, Rate 48000 Hz, Stereo #### ネットワーク経由で送る場合 $ arecord -D hw:2,1 -r 48000 -f S16_LE -c 2 | nc 192.168.1.10 5555 Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 48000 Hz, Stereo
私の家のスピーカーは、今のところROCK64に繋がっています。ROCK64にネットワーク経由で送って、下記のように受け取れば「ほぼ」リアルタイムでデスクトップPCの音声が確認できます。
$ nc -l 5555 | aplay -D hw:0 Playing WAVE 'stdin' : Signed 16 bit Little Endian, Rate 48000 Hz, Stereo
受け取る方は先頭のWAVヘッダでサンプリング周波数やチャネル数を知ることができるため、-rや -cを指定する必要はありません。指定しても構いませんが無視されるはずです。
「ほぼ」リアルタイムと書いた理由は、上記の方法だと人間が余裕でわかるくらいの遅延が発生してしまうからです。ちゃんと測っていませんが、0.5秒くらい遅れてるかも?これでも簡易的な音声確認としては十分ですし、気にしなくても良いでしょう。
この記事にコメントする
| < | 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 | - | - | - |
wiki
Linux JM
Java API
2002年
2003年
2004年
2005年
2006年
2007年
2008年
2009年
2010年
2011年
2012年
2013年
2014年
2015年
2016年
2017年
2018年
2019年
2020年
2021年
2022年
2023年
2024年
2025年
過去日記について
アクセス統計
サーバ一覧
サイトの情報合計:
本日: