Hisa Ando氏の著書「GPUを支える技術」を買っていたのだが、ずいぶんと積ん読にしているのだった。
なので、一応最後まで読んでいきたい。こういうのは、きちんと宣言しないと途中で辞めちゃうので宣言する。頑張って最後まで読んでいこう。
今回は第6章。PCI ExpressやNVLink, さらにはCAPIのようなコヒーレンシプロトコルの話。
Hisa Ando氏の著書「GPUを支える技術」を買っていたのだが、ずいぶんと積ん読にしているのだった。
なので、一応最後まで読んでいきたい。こういうのは、きちんと宣言しないと途中で辞めちゃうので宣言する。頑張って最後まで読んでいこう。
今回は第6章。PCI ExpressやNVLink, さらにはCAPIのようなコヒーレンシプロトコルの話。
Rocket Chipは構成によってどのようなコンポーネントが含まれているのかが変わっているのだが、それ以外にもリビジョンによっても結構構成が変わって、FPGAへのインプリメントに使用する面積が変わったりもする。
比較対象として、以下のリビジョンを使用した。
35d377d Merge pull request #1100 from freechipsproject/disable-local-amos
(日記を執筆時点の最新のリビジョン)
FreeChipProjectのRocket Chipは、DefaultConfigとDefaultFPGAConfigというものが存在しており、それぞれの構成の違いは以下のようになっている。
src/main/scala/system/Config.scala
class DefaultConfig extends Config(new WithNBigCores(1) ++ new BaseConfig) class DefaultFPGAConfig extends Config(new WithNSmallCores(1) ++ new BaseFPGAConfig) class DefaultFPGASmallConfig extends Config(new DefaultFPGAConfig)
WithNBigCores()
とWithNSmallCores()
の違いは、パラメータの違いとして表現されているのだが、
src/main/scala/coreplex/Config.scala
class WithNBigCores(n: Int) extends Config((site, here, up) => { case RocketTilesKey => { val big = RocketTileParams( core = RocketCoreParams(mulDiv = Some(MulDivParams( mulUnroll = 8, mulEarlyOut = true, divEarlyOut = true))), dcache = Some(DCacheParams( rowBits = site(SystemBusKey).beatBits, nMSHRs = 0, blockBytes = site(CacheBlockBytes))), icache = Some(ICacheParams( rowBits = site(SystemBusKey).beatBits, blockBytes = site(CacheBlockBytes)))) List.tabulate(n)(i => big.copy(hartid = i)) } }) class WithNSmallCores(n: Int) extends Config((site, here, up) => { case RocketTilesKey => { val small = RocketTileParams( core = RocketCoreParams(useVM = false, fpu = None), btb = None, dcache = Some(DCacheParams( rowBits = site(SystemBusKey).beatBits, nSets = 64, nWays = 1, nTLBEntries = 4, nMSHRs = 0, blockBytes = site(CacheBlockBytes))), icache = Some(ICacheParams( rowBits = site(SystemBusKey).beatBits, nSets = 64, nWays = 1, nTLBEntries = 4, blockBytes = site(CacheBlockBytes)))) List.tabulate(n)(i => small.copy(hartid = i)) } })
これを見ると、だいたい分かってくるのは以下のような構成になっているということだ。主に命令キャッシュ、データキャッシュの構成を変更している。
WithNBigCores | WithNSmallCores | |||
---|---|---|---|---|
コア機能 | VM | 指定なし(あり) | なし | |
演算器 | mulUnroll | 8 | 指定なし(なし) | |
mulEarlyOut | あり | 指定なし(なし) | ||
divEarlyOut | あり | 指定なし(なし) | ||
FPU | 指定なし(あり) | なし | ||
Dキャッシュ | rowBits | システムバスと同じサイズ | システムバスと同じサイズ | |
セット数 | 指定なし(64) | 64 | ||
ウェイ数 | 4 | 1 | ||
TLBエントリ数 | 32 | 4 | ||
MSHR数 | 0 | 0 | MSHR: Miss Status Handling Register | |
ブロックバイト数 | CacheBlockBytes | CacheBlockBytes | * | |
命令キャッシュ | rowBits | システムバスと同じサイズ | システムバスと同じサイズ | |
セット数 | 64 | 64 | ||
ウェイ数 | 4 | 1 | ||
TLBエントリ数 | 32 | 4 | ||
ブロックバイト数 | CacheBlockBytes | CacheBlockBytes | * |
それぞれの構成において、Vivadoで論理合成して比較してみた。
なお、合成のためには以下のモジュールをRocket Chip以外に追加する必要がある。
というわけで、Rocket-ChipのVerilogのモジュールのうち、RocketTile_rocket から下の1階層のモジュールの面積比較を行ってみた。
DCacheが大きすぎる。ちゃんとブロックRAMに変換できていないんじゃないか?BigCoresとSmallCoresでキャッシュの構成が違いすぎている気がする。
基本的にDCacheの大きさがダイレクトに聞いているような気がしていて、data_arrays_0_ext
を正しく作らなければこのようになってしまう。
これは外部のVerilogで定義されているのだが、この実装がそのまんまなのでBlockRAMに手起業させることが出来ない。
っていうかRAMBを全く使ってくれない。
+---------------------------------------+----------------------------+------------+------------+---------+------+--------+--------+--------+--------------+ | Instance | Module | Total LUTs | Logic LUTs | LUTRAMs | SRLs | FFs | RAMB36 | RAMB18 | DSP48 Blocks | +---------------------------------------+----------------------------+------------+------------+---------+------+--------+--------+--------+--------------+ | dcache | DCache_dcache | 56796 | 56795 | 0 | 1 | 133722 | 0 | 4 | 0 | | (dcache) | DCache_dcache | 788 | 787 | 0 | 1 | 536 | 0 | 0 | 0 | | data | DCacheDataArray | 54494 | 54494 | 0 | 0 | 131145 | 0 | 0 | 0 | | data_arrays_0 | data_arrays_0 | 54494 | 54494 | 0 | 0 | 131145 | 0 | 0 | 0 | | data_arrays_0_ext | data_arrays_0_ext | 54494 | 54494 | 0 | 0 | 131145 | 0 | 0 | 0 | | tag_array | tag_array | 147 | 147 | 0 | 0 | 0 | 0 | 4 | 0 | | tag_array_ext | tag_array_ext | 147 | 147 | 0 | 0 | 0 | 0 | 4 | 0 | | tlb | TLB | 1367 | 1367 | 0 | 0 | 2041 | 0 | 0 | 0 |
+--------------------------------+----------------------------+------------+------------+---------+------+------+--------+--------+--------------+ | Instance | Module | Total LUTs | Logic LUTs | LUTRAMs | SRLs | FFs | RAMB36 | RAMB18 | DSP48 Blocks | +--------------------------------+----------------------------+------------+------------+---------+------+------+--------+--------+--------------+ | dcache | DCache_dcache | 1119 | 1119 | 0 | 0 | 482 | 1 | 1 | 0 | | (dcache) | DCache_dcache | 880 | 880 | 0 | 0 | 482 | 0 | 0 | 0 | | data | DCacheDataArray | 139 | 139 | 0 | 0 | 0 | 1 | 0 | 0 | | data_arrays_0 | data_arrays_0 | 139 | 139 | 0 | 0 | 0 | 1 | 0 | 0 | | data_arrays_0_ext | data_arrays_0_ext | 139 | 139 | 0 | 0 | 0 | 1 | 0 | 0 | | tag_array | tag_array | 91 | 91 | 0 | 0 | 0 | 0 | 1 | 0 | | tag_array_ext | tag_array_ext | 91 | 91 | 0 | 0 | 0 | 0 | 1 | 0 | | tlb | TLB | 9 | 9 | 0 | 0 | 0 | 0 | 0 | 0 | | pmp | PMPChecker | 9 | 9 | 0 | 0 | 0 | 0 | 0 | 0 |
以下がそのVerilogの実装だ。ちょっとひどい。ちゃんとBlockRAMに置き換えないと。
freechips.rocketchip.system.DefaultConfig.behav_srams.v
の構成reg [255:0] ram [511:0]; ... always @(posedge RW0_clk) if (RW0_en && RW0_wmode) begin if (RW0_wmask[0]) ram[RW0_addr][7:0] <= RW0_wdata[7:0]; if (RW0_wmask[1]) ram[RW0_addr][15:8] <= RW0_wdata[15:8]; if (RW0_wmask[2]) ram[RW0_addr][23:16] <= RW0_wdata[23:16]; if (RW0_wmask[3]) ram[RW0_addr][31:24] <= RW0_wdata[31:24]; if (RW0_wmask[4]) ram[RW0_addr][39:32] <= RW0_wdata[39:32]; if (RW0_wmask[5]) ram[RW0_addr][47:40] <= RW0_wdata[47:40]; if (RW0_wmask[6]) ram[RW0_addr][55:48] <= RW0_wdata[55:48]; if (RW0_wmask[7]) ram[RW0_addr][63:56] <= RW0_wdata[63:56]; if (RW0_wmask[8]) ram[RW0_addr][71:64] <= RW0_wdata[71:64]; if (RW0_wmask[9]) ram[RW0_addr][79:72] <= RW0_wdata[79:72]; if (RW0_wmask[10]) ram[RW0_addr][87:80] <= RW0_wdata[87:80]; if (RW0_wmask[11]) ram[RW0_addr][95:88] <= RW0_wdata[95:88]; if (RW0_wmask[12]) ram[RW0_addr][103:96] <= RW0_wdata[103:96]; if (RW0_wmask[13]) ram[RW0_addr][111:104] <= RW0_wdata[111:104]; if (RW0_wmask[14]) ram[RW0_addr][119:112] <= RW0_wdata[119:112]; if (RW0_wmask[15]) ram[RW0_addr][127:120] <= RW0_wdata[127:120]; if (RW0_wmask[16]) ram[RW0_addr][135:128] <= RW0_wdata[135:128]; if (RW0_wmask[17]) ram[RW0_addr][143:136] <= RW0_wdata[143:136];
freechips.rocketchip.system.DefaultFPGAConfig.behav_srams.v
の構成reg [63:0] ram [511:0]; always @(posedge RW0_clk) if (RW0_en && RW0_wmode) begin if (RW0_wmask[0]) ram[RW0_addr][7:0] <= RW0_wdata[7:0]; if (RW0_wmask[1]) ram[RW0_addr][15:8] <= RW0_wdata[15:8]; if (RW0_wmask[2]) ram[RW0_addr][23:16] <= RW0_wdata[23:16]; if (RW0_wmask[3]) ram[RW0_addr][31:24] <= RW0_wdata[31:24]; if (RW0_wmask[4]) ram[RW0_addr][39:32] <= RW0_wdata[39:32]; if (RW0_wmask[5]) ram[RW0_addr][47:40] <= RW0_wdata[47:40]; if (RW0_wmask[6]) ram[RW0_addr][55:48] <= RW0_wdata[55:48]; if (RW0_wmask[7]) ram[RW0_addr][63:56] <= RW0_wdata[63:56]; end
追記: fpga-zynqの構成で実行すると、BlockRAMが正しく推論されているようだ。
+-----------------------------------------+------------------------------------------+------------+------------+---------+------+-------+--------+--------+--------------+ | Instance | Module | Total LUTs | Logic LUTs | LUTRAMs | SRLs | FFs | RAMB36 | RAMB18 | DSP48 Blocks | +-----------------------------------------+------------------------------------------+------------+------------+---------+------+-------+--------+--------+--------------+ | DCache | DCache | 1659 | 1658 | 0 | 1 | 986 | 0 | 36 | 0 | | (DCache) | DCache | 504 | 503 | 0 | 1 | 502 | 0 | 0 | 0 | | MetadataArray | MetadataArray | 283 | 283 | 0 | 0 | 7 | 0 | 4 | 0 | | data | DCacheDataArray | 535 | 535 | 0 | 0 | 0 | 0 | 32 | 0 | | fq | FinishQueue | 8 | 8 | 0 | 0 | 5 | 0 | 0 | 0 | | tlb | TLB_2 | 329 | 329 | 0 | 0 | 472 | 0 | 0 | 0 |
Hisa Ando氏の著書「GPUを支える技術」を買っていたのだが、ずいぶんと積ん読にしているのだった。
なので、一応最後まで読んでいきたい。こういうのは、きちんと宣言しないと途中で辞めちゃうので宣言する。頑張って最後まで読んでいこう。
今回は第4章前半。NVIDIA P100のザックリとした構造の概要。
GPUを支える技術 ――超並列ハードウェアの快進撃[技術基礎] (WEB+DB PRESS plus)
Hisa Ando氏の著書「GPUを支える技術」を買っていたのだが、ずいぶんと積ん読にしているのだった。
なので、一応最後まで読んでいきたい。こういうのは、きちんと宣言しないと途中で辞めちゃうので宣言する。頑張って最後まで読んでいこう。
今回は第3章。
(https://wccftech.com/review/intel-core-i7-6700k-skylake-k-cpu-review-asus-z170-pro-gaming/3/ より)
Rustのライブラリを使えばElfの解析だってできる。
バイナリファイルを読み込んで、HashMapにアドレスとデータの対を格納するプログラムを作成した。 各セクション毎にデータを読み込んで一つのHashMapにバイト単位でデータを書き込む。このモデルを作成すれば、Rustで簡単な命令セットシミュレータが作れるようになるはずだ。
training/program/rust/projects/sim_mem_elf/src/main.rs
fn memory_elf (elf_obj: goblin::elf::Elf, memory: &mut std::collections::HashMap<u64, u8>, buffer: &std::vec::Vec<u8>) -> error::Result<()> { let shdr_strtab = &elf_obj.shdr_strtab; for section in &elf_obj.section_headers { println!("elf_obj.section_headers = {:#?}, file_offset = {:#x}, size = {:#x}", &shdr_strtab[section.sh_name], section.sh_offset, section.sh_size ); if section.sh_size != 0 { for idx in 0..(section.sh_size-1) { let mut offset = idx + section.sh_offset; memory.insert(section.sh_addr + idx, buffer[offset as usize]); } } } Ok(()) }
$ cargo run ~/work/rocket-chip-msyksphinz/riscv-tools/riscv-tests/benchmarks/qsort.riscv
ただしまだRustとかScalaの初心者なので、MutableとかImmutableとか、まだ分かっていないことが多い。コンパイラの結果を見ながら進めている感じ。こんなので大丈夫かな?