FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

RISC-VのLinuxブート環境をbuildrootで構築する (3. RTLでブートローダを実行する)

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)
===============================