RISC-Vのアウトオブオーダ実装であるBOOM (Berkeley Out-of-Order Machine)のRTLシミュレーションは、基本的にverilatorを使って実行されている。
それもそのはず、BOOMはオープンソースハードウェアであるため、オープンソースのシミュレータを使わないと万人が利用できない。
しかし、波形を観測したり、デバッグをしたりするのにverilatorだけでは不便だ。今回は僕がいつも(趣味なFPGA開発のときに)利用しているVeritakに移植してみよう。
BOOM のハードウェアを改造してRTLシミュレーション用に変更する
BOOMのRTL実装は1ファイルにまとめられており、boomのリポジトリをビルドすると、rocketchip.BOOMConfig.v というファイルにまとめられている。 昨日のブログでも紹介したとおり、これは80万行超の膨大なファイルであり、全体を見渡すのはなかなかやっかいだ。
トップに存在するのはTestHarness()というモジュールだ。ここにはどうやらCPUの本体と、外部周辺RAM、モニタ、インターコネクトなどが接続されているらしい。
この部分はまだテストベンチとしての役割ではなさそうだ。Verilatorの場合、さらに上にC++のラッパーを被せるのだろう。今回はVeritakで動作させるため、Verilogで記述されたテストベンチを記述する必要がある。
module TestHarness( input clock, input reset, output io_success ); wire dut_clock; wire dut_reset; wire dut_io_success; wire dut_io_debug_req_ready; wire dut_io_debug_req_valid; ...
まず、TestHarnessを動作させるためのテストベンチを作成した。
`timescale 100ps / 1ps module tb; parameter STEP = 100000; parameter H_STEP = 50000; parameter H2_STEP = 25000; reg CPU_CLK; reg CPU_RESET; integer step_count; TestHarness u_TestHarness ( .clock (CPU_CLK), .reset (CPU_RESET), .io_success () ); always # (H_STEP) begin CPU_CLK <= ~CPU_CLK; end initial begin CPU_CLK = 1'b1; CPU_RESET = 1'b1; #(STEP * 10); CPU_RESET = 1'b0; for (step_count = 0; step_count < 100; step_count = step_count) begin #(STEP * 20000); $display ("%t", $time); end $display ("<Info: Simulation TimeOut %t>", $time); $finish; // $stop; end endmodule // tb_rocketchip
Veritakでコンパイルしてみると、rocketchip.BOOMConfig.v には$Fatal
というシステムタスクが呼ばれており、これがVeritakには無いと怒られてしまった。ここの部分は改造するしか無さそうだ。
if (T_41 & T_45) begin $Fatal; end
改造する。
if (T_41 & T_45) begin $display ("Fatal error. Exit"); $finish; end
一応コンパイルは通るようになったのだが、シミュレーションが開始されない。いくつかトップモジュールを変更して試行してみたのだが、正しく動作させるまでには至らなかった。まだ解析が必要そうだ。
備考: BOOMはどこから命令をフェッチするのか?
rocketchip.BOOMConfig.v を探索していると、bootrom というモジュールを見つけた。 探ってみると、内部の1000ワード近くを、RAMモデルを使わずすべてワイヤで記述してある...!
BOOMのモデルの中には、こういう部分が多量にあるはずだ。これを律儀にVeritakで動作させようとしたから、重かったのかなあ。。。?
module bootrom( input clock, input reset, output io_in_0_a_ready, input io_in_0_a_valid, ... input io_in_0_e_bits_sink ); wire [31:0] rom_0; wire [31:0] rom_1; wire [31:0] rom_2; ... reg [31:0] GEN_1030; reg [31:0] GEN_1037; assign io_in_0_a_ready = io_in_0_d_ready; assign io_in_0_b_valid = 1'h0; assign io_in_0_b_bits_opcode = GEN_1024; assign io_in_0_b_bits_param = GEN_1025; assign io_in_0_b_bits_size = GEN_1026; assign io_in_0_b_bits_source = GEN_1027; assign io_in_0_b_bits_addr_hi = GEN_1028; assign io_in_0_b_bits_mask = GEN_1029; assign io_in_0_b_bits_data = GEN_1030; assign io_in_0_c_ready = 1'h1; assign io_in_0_d_valid = io_in_0_a_valid; assign io_in_0_d_bits_opcode = T_2371_opcode; assign io_in_0_d_bits_param = T_2371_param; assign io_in_0_d_bits_size = T_2371_size; assign io_in_0_d_bits_source = T_2371_source; assign io_in_0_d_bits_sink = T_2371_sink; assign io_in_0_d_bits_addr_lo = T_2371_addr_lo; assign io_in_0_d_bits_data = T_2371_data; assign io_in_0_d_bits_error = T_2371_error; assign io_in_0_e_ready = 1'h1; assign rom_0 = 32'h6f; assign rom_1 = 32'h0; assign rom_2 = 32'h0; assign rom_3 = 32'h1020; assign rom_4 = 32'h0; assign rom_5 = 32'h0; assign rom_6 = 32'h0; assign rom_7 = 32'h0; assign rom_8 = 32'h63696c70; assign rom_9 = 32'h200a7b20; assign rom_10 = 32'h69727020; assign rom_11 = 32'h7469726f; assign rom_12 = 32'h78302079; ...