行時(shí)環(huán)境:結(jié)合 riscv/npc 與 platform/npc.mk 的代碼分析)
講義主題你已經(jīng)知道程序需要什么樣的運(yùn)行時(shí)環(huán)境現(xiàn)在要把這套環(huán)境提供給 NPC。AM 項(xiàng)目已經(jīng)提供了riscv64-npc/riscv32e-npc的基本框架你只需要補(bǔ)上設(shè)備等細(xì)節(jié)。下面逐個(gè)文件分析這個(gè)“NPC 運(yùn)行時(shí)環(huán)境”是怎么搭起來(lái)的。一、NPC 運(yùn)行時(shí)環(huán)境由誰(shuí)提供AM 的 riscv/npc 目錄 scripts/platform/npc.mk NPC 仿真器 Vrv32AM 給 NPC 提供TRM啟動(dòng)、putch、halt IOE定時(shí)器、輸入 CTE異常處理NPC 仿真器提供CPU 物理內(nèi)存 MMIO 設(shè)備串口、定時(shí)器二、構(gòu)建腳本platform/npc.mk文件abstract-machine/scripts/platform/npc.mkAM_SRCS : riscv/npc/start.S \ riscv/npc/trm.c \ riscv/npc/ioe.c \ riscv/npc/timer.c \ riscv/npc/input.c \ riscv/npc/cte.c \ riscv/npc/trap.S \ platform/dummy/vme.c \ platform/dummy/mpe.c LDFLAGS --defsym_pmem_start0x80000000 \ --defsym_entry_offset0x0 LDFLAGS --gc-sections -e _start NPC_EXEC : /home/xuhaitao/ysyx-workbench/npc/obj_dir/Vrv32 run: insert-arg $(NPC_EXEC) -b $(IMAGE).bin分析AM_SRCS說(shuō)明 NPC 的 AM 運(yùn)行環(huán)境由這些文件組成start.S 啟動(dòng) trm.c TRM ioe.c IOE timer.c 定時(shí)器 input.c 輸入 cte.c CTE trap.S 異常匯編--defsym_pmem_start0x80000000內(nèi)存從0x80000000開(kāi)始和 NEMU 一致。-e _start入口是_start。run: $(NPC_EXEC) -b $(IMAGE).bin最終不是啟動(dòng) NEMU而是啟動(dòng)npc/obj_dir/Vrv32三、啟動(dòng)start.S文件abstract-machine/am/src/riscv/npc/start.S.section entry, ax .globl _start _start: mv s0, zero la sp, _stack_pointer call _trm_init分析和 NEMU 完全一樣_start - 設(shè)置棧指針 - call _trm_initNPC 復(fù)位后從0x80000000開(kāi)始執(zhí)行_start。四、TRMtrm.c文件abstract-machine/am/src/riscv/npc/trm.cextern char _pmem_start; #define PMEM_SIZE (128 * 1024 * 1024) #define PMEM_END ((uintptr_t)_pmem_start PMEM_SIZE) Area heap RANGE(_heap_start, PMEM_END); static const char mainargs[MAINARGS_MAX_LEN] TOSTRING(MAINARGS_PLACEHOLDER); #define SERIAL_DATA_PORT 0xa00003f8 void putch(char ch) { outb(SERIAL_DATA_PORT, ch); } void halt(int code) { asm volatile ( mv a0, %0\n ebreak\n : : r(code) : a0 ); __builtin_unreachable(); } void _trm_init() { int ret main(mainargs); halt(ret); }分析1. 堆范圍Area heap RANGE(_heap_start, PMEM_END);NPC 內(nèi)存0x80000000 ~ 0x88000000共 128MB。2. 串口輸出#define SERIAL_DATA_PORT 0xa00003f8 void putch(char ch) { outb(SERIAL_DATA_PORT, ch); }這和 NEMU 使用相同的地址0xa00003f8所以NPC 的串口 MMIO 地址和 NEMU 一致 上層程序不用改3. 停機(jī)asm volatile (mv a0, %0; ebreak)把退出碼放到a0然后ebreak。NPC 仿真器捕獲ebreak讀取a0判斷是否正常退出。五、IOEioe.c文件abstract-machine/am/src/riscv/npc/ioe.cstatic void *lut[128] { [AM_TIMER_CONFIG] __am_timer_config, [AM_TIMER_RTC ] __am_timer_rtc, [AM_TIMER_UPTIME] __am_timer_uptime, [AM_INPUT_CONFIG] __am_input_config, [AM_INPUT_KEYBRD] __am_input_keybrd, [AM_UART_CONFIG] __am_uart_config, }; bool ioe_init() { for (int i 0; i LENGTH(lut); i) if (!lut[i]) lut[i] fail; __am_timer_init(); return true; }分析NPC 目前只支持定時(shí)器 鍵盤(pán) UART沒(méi)有GPU 音頻 磁盤(pán) 網(wǎng)絡(luò)所以NPC 階段只跑字符程序不跑圖形/音頻六、定時(shí)器timer.c文件abstract-machine/am/src/riscv/npc/timer.c#define RTC_UPTIME_LO 0xa0000048 #define RTC_UPTIME_HI 0xa000004c #define RTC_YEAR 0xa0000100 #define RTC_MONTH 0xa0000104 #define RTC_DAY 0xa0000108 #define RTC_HOUR 0xa000010c #define RTC_MINUTE 0xa0000110 #define RTC_SECOND 0xa0000114 void __am_timer_uptime(AM_TIMER_UPTIME_T *uptime) { uint32_t hi1, lo, hi2; do { hi1 inl(RTC_UPTIME_HI); lo inl(RTC_UPTIME_LO); hi2 inl(RTC_UPTIME_HI); } while (hi1 ! hi2); uptime-us ((uint64_t)hi1 32) | lo; } void __am_timer_rtc(AM_TIMER_RTC_T *rtc) { rtc-year inl(RTC_YEAR); rtc-month inl(RTC_MONTH); rtc-day inl(RTC_DAY); rtc-hour inl(RTC_HOUR); rtc-minute inl(RTC_MINUTE); rtc-second inl(RTC_SECOND); }分析1. uptime 地址0xa0000048 低 32 位 0xa000004c 高 32 位和 NEMU 一致。2. 為什么讀三次do { hi1 讀高32位 lo 讀低32位 hi2 讀高32位 } while (hi1 ! hi2);這是為了處理“進(jìn)位”如果讀高32位后低32位剛好進(jìn)位 那 hi1 和 hi2 會(huì)不同 重新讀一遍3. RTC 日歷NPC 的 RTC 地址0xa0000100 年 0xa0000104 月 0xa0000108 日 0xa000010c 時(shí) 0xa0000110 分 0xa0000114 秒這比 NEMU 更完整。七、鍵盤(pán)input.c文件abstract-machine/am/src/riscv/npc/input.cvoid __am_input_keybrd(AM_INPUT_KEYBRD_T *kbd) { kbd-keydown 0; kbd-keycode AM_KEY_NONE; }分析NPC 的鍵盤(pán)當(dāng)前是空實(shí)現(xiàn)永遠(yuǎn)返回?zé)o按鍵因?yàn)镹PC 當(dāng)前沒(méi)有實(shí)現(xiàn)鍵盤(pán)設(shè)備八、CTEcte.c文件abstract-machine/am/src/riscv/npc/cte.cContext* __am_irq_handle(Context *c) { if (user_handler) { Event ev {0}; switch (c-mcause) { case 11: ev.event EVENT_YIELD; c-mepc 4; break; default: ev.event EVENT_ERROR; break; } c user_handler(ev, c); assert(c ! NULL); } return c; } bool cte_init(Context*(*handler)(Event, Context*)) { asm volatile(csrw mtvec, %0 : : r(__am_asm_trap)); user_handler handler; return true; } Context *kcontext(Area kstack, void (*entry)(void *), void *arg) { Context *ctx (Context *)((uintptr_t)kstack.end - sizeof(Context)); memset(ctx, 0, sizeof(Context)); ctx-mepc (uintptr_t)entry; ctx-gpr[10] (uintptr_t)arg; ctx-gpr[2] (uintptr_t)kstack.end; ctx-mstatus 0x00001880; return ctx; }分析1. 只處理ecallcase 11: ev.event EVENT_YIELD; c-mepc 4;NPC 當(dāng)前只處理ecall把它當(dāng)成yield。2. 沒(méi)有處理定時(shí)器中斷NPC 的 CTE 還沒(méi)有處理0x80000007 定時(shí)器中斷3.kcontext創(chuàng)建上下文ctx-mepc entry; ctx-gpr[10] arg; ctx-gpr[2] kstack.end; ctx-mstatus 0x00001880;和 NEMU 版本相比多了ctx-gpr[2] (uintptr_t)kstack.end;設(shè)置了棧指針sp。九、異常匯編trap.S文件abstract-machine/am/src/riscv/npc/trap.S和 NEMU 的trap.S基本一樣但多了li a0, (1 17) or t1, t1, a0 csrw mstatus, t1分析mstatus.MPRV 位設(shè)置MPRV讓 load/store 使用mstatus里的特權(quán)模式進(jìn)行地址翻譯。這是為了和 Difftest 對(duì)齊。十、NPC 仿真器側(cè)怎么配合文件npc/obj_dir/Vrv32run目標(biāo)最終執(zhí)行/home/xuhaitao/ysyx-workbench/npc/obj_dir/Vrv32 -b image.binNPC 仿真器會(huì)1. 讀取 image.bin 2. 加載到 0x80000000 3. 復(fù)位 CPU 4. 從 0x80000000 執(zhí)行 _start 5. 遇到 ebreak 停止內(nèi)存訪問(wèn)由 DPI-C 調(diào)用pmem_read pmem_write設(shè)備地址0xa00003f8 串口 0xa0000048 定時(shí)器由 NPC 仿真?zhèn)萷addr.cpp處理。十一、NPC 和 NEMU 運(yùn)行時(shí)環(huán)境對(duì)比項(xiàng)目NEMUNPC入口_start_start內(nèi)存基址0x800000000x80000000串口地址0xa00003f80xa00003f8uptime 地址0xa00000480xa0000048RTC 日歷未實(shí)現(xiàn)已實(shí)現(xiàn)鍵盤(pán)支持空實(shí)現(xiàn)GPU支持未支持CTE支持 yield只支持 ecall十二、總結(jié)NPC 的運(yùn)行時(shí)環(huán)境 AM riscv/npc platform/npc.mk Vrv32 仿真器它已經(jīng)提供了? 啟動(dòng) _start ? putch 串口 ? halt ebreak ? 定時(shí)器 uptime / RTC ? 異常入口 cte_init ? 上下文 kcontext還缺? 鍵盤(pán) ? GPU ? 定時(shí)器中斷所以 NPC 當(dāng)前能跑hello cpu-tests klib-tests但還不能跑圖形和交互程序。