前回の続き。Rocket Chipがどのようにして制御を行っているのか、またプログラムのロードや、Start,Stopの制御はどのように行っているのかなどを調査している。
前回まではload_program()
関数が呼ばれるところまでを見てきた。
実際には内部のload_program()
はelfloader.cc
のload_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
にコマンドの情報を挿入することでメモリアクセスなどを実現しているものと思われるが、正直ここから先は良く分からなくなった。
なんでこれでメモリアクセスが出来るんだ?まだ解析は続く。