FPGA開発日記

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

RocketChipの足回りを理解する(5. Load_Elf 解析中)

前回の続き。Rocket Chipがどのようにして制御を行っているのか、またプログラムのロードや、Start,Stopの制御はどのように行っているのかなどを調査している。

前回まではload_program()関数が呼ばれるところまでを見てきた。

msyksphinz.hatenablog.com

実際には内部のload_program()elfloader.ccload_elf()を呼び出しており、これが実質的なプログラムのロードルーティンになっている。

  • riscv-tools/riscv-fesvr/fesvr/elfloader.cc
std::map<std::string, uint64_t> load_elf(const char* fn, memif_t* memif, reg_t* entry)
{
...
  std::map<std::string, uint64_t> symbols;

  #define LOAD_ELF(ehdr_t, phdr_t, shdr_t, sym_t) do { \
    ehdr_t* eh = (ehdr_t*)buf; \
    phdr_t* ph = (phdr_t*)(buf + eh->e_phoff); \
    *entry = eh->e_entry; \
    assert(size >= eh->e_phoff + eh->e_phnum*sizeof(*ph)); \
    for (unsigned i = 0; i < eh->e_phnum; i++) { \
      if(ph[i].p_type == PT_LOAD && ph[i].p_memsz) { \
        if (ph[i].p_filesz) { \
          assert(size >= ph[i].p_offset + ph[i].p_filesz); \
          memif->write(ph[i].p_paddr, ph[i].p_filesz, (uint8_t*)buf + ph[i].p_offset); \
        } \
...

実際にプログラムの書き込みを発生させているのは、

        memif->write(ph[i].p_paddr + ph[i].p_filesz, ph[i].p_memsz - ph[i].p_filesz, &zeros[0]); \

の部分だとは思われるが、memif_t *mem_ifの定義は memif.ccで定義されており、ここでwrite()を呼び出すということは、

  • riscv-tools/riscv-fesvr/fesvr/memif.cc
  size_t align = htif->chunk_align();
  if (len && (addr & (align-1)))
  {
    size_t this_len = std::min(len, align - size_t(addr & (align-1)));
    uint8_t chunk[align];

    htif->read_chunk(addr & ~(align-1), align, chunk);
    memcpy(chunk + (addr & (align-1)), bytes, this_len);
    htif->write_chunk(addr & ~(align-1), align, chunk);
...

の部分だと思われる。さらにこのread_chunk(), write_chunk()というのがhtif_t *htifにて定義されており、実体はおそらくこれがdtm.ccで定義されているものになる。

  • riscv-tools/riscv-fesvr/fesvr/dtm.cc
uint32_t dtm_t::do_command(dtm_t::req r)
{
  req_buf = r;
  target->switch_to();
  assert(resp_buf.resp == 0);
  return resp_buf.data;
}

uint32_t dtm_t::read(uint32_t addr)
{
  return do_command((req){addr, 1, 0});
}

uint32_t dtm_t::write(uint32_t addr, uint32_t data)
{
  return do_command((req){addr, 2, data});
}

この中で実際にはreq_bufにコマンドの情報を挿入することでメモリアクセスなどを実現しているものと思われるが、正直ここから先は良く分からなくなった。 なんでこれでメモリアクセスが出来るんだ?まだ解析は続く。

f:id:msyksphinz:20170712013910p:plain