目次: Zephyr
CONFIG_SMP有効、1コア、HART ID != 0の動作確認をしました。以前書いたとおり、SMP対応は下記の手順で進めていますので、再掲します。
現在3番目の項目が終わったところです。いよいよ最後です。SMP対応の本丸である、マルチコアブート、IPIの対応を進めます。
前回(2020年10月10日の日記参照)、空関数で実装したarch_start_cpu() を真面目に実装するときが来ました。HART 0をマスターコア、それ以外をスレーブコアとします。マスターコアはarch_start_cpu() を呼びスレーブコアを1つずつ起床します。
// zephyr/kernel/smp.c
void z_smp_init(void)
{
(void)atomic_clear(&start_flag);
#if defined(CONFIG_SMP) && (CONFIG_MP_NUM_CPUS > 1)
for (int i = 1; i < CONFIG_MP_NUM_CPUS; i++) {
arch_start_cpu(i, z_interrupt_stacks[i], CONFIG_ISR_STACK_SIZE,
smp_init_top, &start_flag); //★スレーブコアの数だけarch_start_cpu() を呼ぶ★
}
#endif
(void)atomic_set(&start_flag, 1);
}
// zephyr/arch/riscv/core/cpu_smp.c
static volatile struct {
arch_cpustart_t fn;
void *arg;
} riscv_cpu_cfg[CONFIG_MP_NUM_CPUS];
volatile uintptr_t riscv_init_flag;
volatile void *riscv_init_sp;
//★マスターコアが実行★
void arch_start_cpu(int cpu_num, k_thread_stack_t *stack, int sz,
arch_cpustart_t fn, void *arg)
{
riscv_cpu_cfg[cpu_num].fn = fn;
riscv_cpu_cfg[cpu_num].arg = arg;
/* Signal to slave core with initial sp. */
riscv_init_sp = Z_THREAD_STACK_BUFFER(stack) + sz; //★スタックポインタの初期値★
riscv_init_flag = cpu_num; //★スレーブコアを起床★
/* Wait for slave core */
while (riscv_init_flag == cpu_num) { //★スレーブコアが起床するまでビジーウェイト★
;
}
}
引数の意味はCPU番号cpu_num、スタックの先頭アドレスstack、スタックのサイズsz、スレーブコアが実行する関数のポインタfn、関数の引数argです。fnとargは後でスレーブコアが使うので配列riscv_cpu_cfg[] に保存します。
スタックポインタとCPU番号はスレーブコアのブート部分で参照するので、グローバル変数に保存します。riscv_init_flag, riscv_init_spは配列にしなくても上書きされる心配はありません。マスターコアはスレーブコアを一度に1コアずつ起こすように実装するので、複数のスレーブコアが同時に同じスタックを使って異常動作する事態は発生し得ないからです。スレーブコア側の実装も見ていただければわかるはず、です。
リセット後、スレーブコアは一度に全コアが起動します。ブートコードの途中で、マスターコアから設定されるフラグを待つように実装します。下記コードでいえばboot_slave_coreのところです
SECTION_FUNC(TEXT, __initialize)
/*
* This will boot master core, just halt other cores.
* Note: need to be updated for complete SMP support
*/
csrr a0, mhartid
beqz a0, boot_master_core //★HART 0はマスターコア★
li a1, CONFIG_MP_NUM_CPUS //★CONFIG_MP_NUM_CPUSより小さいHART IDならスレーブコア★
blt a0, a1, boot_slave_core
loop_slave_core: //★CONFIG_MP_NUM_CPUS以上のHART IDがあったら、wfiでスリープ状態にさせる★
wfi
j loop_slave_core
boot_slave_core:
/* Wait for signal from master core */
la t0, riscv_init_flag
RV_OP_LOADREG t1, (t0)
bne a0, t1, boot_slave_core //★riscv_init_flagに自分のHART IDが設定されるまで待つ★
/* Setup stack */
la t1, riscv_init_sp
RV_OP_LOADREG sp, (t1) //★スタックポインタ初期化★
/* Notify to master core */
RV_OP_STOREREG x0, (t0) //★マスターコアにブート完了を知らせる★
j z_riscv_slave_start
...
// zephyr/arch/riscv/core/cpu_smp.c
//★スレーブコアが実行★
void z_riscv_slave_start(int cpu_num)
{
#if defined(CONFIG_RISCV_SOC_INTERRUPT_INIT)
soc_interrupt_init();
#endif
riscv_cpu_cfg[cpu_num].fn(riscv_cpu_cfg[cpu_num].arg); //★arch_start_cpu() で指定された関数と引数★
}
スレーブコアは全てが同時にriscv_init_flagをチェックしますが、riscv_init_flag == 自身のHART IDと一致しない限り永久に待つため、flagチェック以降の処理に進むことはありません。この機構により同じスタックを2つ以上のスレーブコアが同時に使ってしまうことを避けています。
以上で、マルチコアが動き始めました。続きは次回。
< | 2020 | > | ||||
<< | < | 10 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | - | - | 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 | 31 |
合計:
本日:
管理者: Katsuhiro Suzuki(katsuhiro( a t )katsuster.net)
This is Simple Diary 1.0
Copyright(C) Katsuhiro Suzuki 2006-2023.
Powered by PHP 8.2.15.
using GD bundled (2.1.0 compatible)(png support.)