RISC-VのLinuxブート環境を構築したので、次にこれをRTL環境で実行してみようと思う。
まずは、ブートローダをロードする前のBootROMを作成する必要がある。 SpikeやChipyardでは、BootROMが定義されており、CPUは最初にそこから命令をロードして実行する。
Chipyardの場合は、0x4000にBootROMが存在している。
L23: soc { #address-cells = <1>; #size-cells = <1>; compatible = "freechips,rocketchip-unknown-soc", "simple-bus"; ranges; L16: boot-address-reg@4000 { reg = <0x4000 0x1000>; reg-names = "control"; };
Spikeの場合は、0x1000にBootROMが存在している。
boot_rom.reset(new rom_device_t(rom)); bus.add_device(DEFAULT_RSTVEC, boot_rom.get());
これをRTLシミュレーションで実現しなければならない。まずは外部インタフェースに、L2と並行して仮想的なBootROMを実装した。
localparam WORDS = SIZE / (DATA_W / 8); logic [DATA_W-1: 0] r_rom[WORDS]; logic [DATA_W-1: 0] r_rom_raw[WORDS]; assign o_req_ready = 1'b1; logic [RD_LAT-1: 0] r_resp_valid; logic [DATA_W-1: 0] r_resp_data[RD_LAT]; logic [TAG_W-1 : 0] r_resp_tag [RD_LAT]; always_ff @ (posedge i_clk, negedge i_reset_n) begin if (!i_reset_n) begin r_resp_valid[0] <= 'h0; r_resp_data [0] <= 'h0; r_resp_tag [0] <= 'h0; end else begin r_resp_valid[0] <= i_req_valid; r_resp_data [0] <= r_rom[i_req_addr[$clog2(WORDS)-1: $clog2(DATA_W / 8)]]; r_resp_tag [0] <= i_req_tag; end end generate for (genvar p_idx = 1; p_idx < RD_LAT; p_idx ++) begin : rd_loop always_ff @ (posedge i_clk, negedge i_reset_n) begin if (!i_reset_n) begin r_resp_valid[p_idx] <= 'h0; r_resp_tag [p_idx] <= 'h0; r_resp_data [p_idx] <= 'h0; end else begin r_resp_valid[p_idx] <= r_resp_valid[p_idx-1]; r_resp_tag [p_idx] <= r_resp_tag [p_idx-1]; r_resp_data [p_idx] <= r_resp_data [p_idx-1]; end end // always_ff @ (posedge i_clk, negedge i_reset_n) end // block: rd_loop endgenerate assign o_resp_valid = r_resp_valid[RD_LAT-1]; assign o_resp_tag = r_resp_tag [RD_LAT-1]; assign o_resp_data = r_resp_data [RD_LAT-1];
次にBootROMの中身だが、Spikeのほうはこのようになっている。
uint32_t reset_vec[reset_vec_size] = { 0x297, // auipc t0,0x0 0x28593 + (reset_vec_size * 4 << 20), // addi a1, t0, &dtb 0xf1402573, // csrr a0, mhartid get_core(0)->get_xlen() == 32 ? 0x0182a283u : // lw t0,24(t0) 0x0182b283u, // ld t0,24(t0) 0x28067, // jr t0 0, (uint32_t) (start_pc & 0xffffffff), (uint32_t) (start_pc >> 32) };
最初のアセンブリ命令列の後に、DTBがバイナリ形式で置かれている。 まずはa1(第2引数)にDTBの位置を設定する。 次にa0(第1引数)はコア番号となる。
さらに、t0にエントリポイント(0x8000_0000)を格納してジャンプする。という要領だ。
0x8000_0000から先は当該アプリケーション(Linuxの場合はブートローダ)が走り始めることになる。
これと全く同じ環境を作成して自作のBootROMに格納し、自作CPUを立ち上げてみる。
1000 : 262 : IPC(recent) = 0.26, IPC(total) = 0.26 2000 : 598 : IPC(recent) = 0.34, IPC(total) = 0.30 3000 : 930 : IPC(recent) = 0.33, IPC(total) = 0.31 4000 : 1264 : IPC(recent) = 0.33, IPC(total) = 0.32 5000 : 1599 : IPC(recent) = 0.34, IPC(total) = 0.32 6000 : 1930 : IPC(recent) = 0.33, IPC(total) = 0.32 7000 : 2266 : IPC(recent) = 0.34, IPC(total) = 0.32 8000 : 2596 : IPC(recent) = 0.33, IPC(total) = 0.32 9000 : 2932 : IPC(recent) = 0.34, IPC(total) = 0.33 10000 : 3262 : IPC(recent) = 0.33, IPC(total) = 0.33 11000 : 3598 : IPC(recent) = 0.34, IPC(total) = 0.33 12000 : 3930 : IPC(recent) = 0.33, IPC(total) = 0.33 13000 : 4264 : IPC(recent) = 0.33, IPC(total) = 0.33 14000 : 4599 : IPC(recent) = 0.34, IPC(total) = 0.33 15000 : 4930 : IPC(recent) = 0.33, IPC(total) = 0.33 16000 : 5266 : IPC(recent) = 0.34, IPC(total) = 0.33 17000 : 5596 : IPC(recent) = 0.33, IPC(total) = 0.33 18000 : 5932 : IPC(recent) = 0.34, IPC(total) = 0.33 linux : ERROR
6000命令くらいのところまで行った。どうもDTBのロード値がおかしいらしい?RTLのロードした値が正しく、Spikeがおかしいように見える。これは解析する必要がありそうだ。
GPR[11](42) <= 0000000000000000 44113 : 6025 : PC=[000000008000c944] (M,06,01) 00354303 lbu t1, 3(a0) MR1(0x0000000000001023)=>00000000000000ed GPR[06](30) <= 00000000000000ed 44119 : 6026 : PC=[000000008000c948] (M,07,01) 00254603 lbu a2, 2(a0) MR1(0x0000000000001022)=>00000000000000fe GPR[12](38) <= 00000000000000fe 44125 : 6027 : PC=[000000008000c94c] (M,08,01) 00754883 lbu a7, 7(a0) MR1(0x0000000000001027)=>00000000000000c8 ========================================== Wrong GPR[17](10): RTL = 00000000000000cc, ISS = 00000000000000c8 ========================================== =============================== SIMULATION FINISH : FAIL (CODE=100) ===============================