マルツオンラインが、RISC-VのArduinoボード、HiFive1を取り扱い始めた。
これまで海外で購入可能だったが、海外サイトでわざわざ購入するのが億劫で買っていなかったのだが、マルツだと安心。 購入して即日で届く速さだった。
まだ開封しただけで、ソフトウェアの環境などは用意していないが、ボチボチ構築していこう。
さて、これで何して遊ぼうかな(買ったは良いが何をするか決めていない)。
S1coreとOpenSPARC T1純正環境との差分は引き続き調査中だ。 とりあえず、S1coreを使いこなせるようになりたい。 まずはベンチマークプログラムを動作させられるようにしたい。Coremarkが動けば面白いのだが、まずはDhrystoneかな。
Dhrystoneは、CPUの基本性能を知るための最も基礎的なベンチマークプログラムだ。
前回少し解説したが、S1coreでベンチマークプログラムを動作させるためには、以下の2つのファイルを用意する必要がある。
rom_harness.hex
: リセット後のブート動作を行うためのプログラム。main()
に入る直前まで。ram_harness.hex
: main()
以降のプログラムを格納している。まずは、S1coreのリリース群に入っているスクリプトを確認して、何をしなければならないのかを確認しよう。
$ less ./tools/bin/compile_test ... # Compile the boot code sparc64-linux-gnu-as -ah -am -o boot/boot.bin boot/boot.s sparc64-linux-gnu-objdump -d -EB -w -z boot/boot.bin > boot/boot.dump grep " " boot/boot.dump | egrep -v "elf64-sparc" | dump2hex.bin > boot/rom_harness.hex # Compile the C test sparc64-linux-gnu-gcc -c -O0 $1.c sparc64-linux-gnu-ld -Ur --script=$S1_ROOT/tools/src/linker.map -EB -o $1.bin $1.o sparc64-linux-gnu-objdump -d -EB -w -z $1.bin > $1.dump grep " " $1.dump | egrep -v "elf64-sparc" | dump2hex.bin > ram_harness.hex
基本的にsparcのgccを使ってコンパイルし、リンカはlinker.map
を使っている。これはDhrystoneも同じものを使えばよかろう。
このコンパイル手順をDhrystoneにも適用すれば良いと思われる。
Dhrystoneのコードは以下からダウンロードした。
Makefile
の編集を行う。
CC= sparc64-linux-gnu-gcc # C compiler name goes here (UNIX) GCC= sparc64-linux-gnu-gcc ... CFLAGS= $(OPTIMIZE) $(TIME_FUNC) -DHZ=$(HZ) $(ENUMS) $(STRUCTASSIGN) $(CFL) -Ur -Xlinker --script=/home/masayuki/work/s1_core_opensparc_t1/s1_core/tools/src/linker.map -Xlinker -EB -lc GCCFLAGS= $(GCCOPTIM) $(TIME_FUNC) -DHZ=$(HZ) $(ENUMS) $(STRUCTASSIGN) $(CFL) -Ur -Xlinker --script=/home/masayuki/work/s1_core_opensparc_t1/s1_core/tools/src/linker.map -Xlinker -EB -lc
さらに、cc_dhry
を作成した後に、hexファイルを生成する手順も追加した。
cc_dry2: $(SRC) $(HDR) $(CC) $(CFLAGS) $(SRC) $(LFLAGS) -o $@ sparc64-linux-gnu-objdump -d -EB -w -z $@ > $@.dump grep " " $@.dump | egrep -v "elf64-sparc" | dump2hex.bin > ram_harness.hex
これで、Dhrystoneのディレクトリにram_harness.hex
が生成される。これを既存のs1_core/test/ram_harness.hex
(hello.c
から生成したもの)と置き換えてシミュレーションを走らせて見ればよい。
S1coreの動作方法が分かってきたのだが、S1coreとOpenSPARC T1のコアは分離されているが、SPARCコアの部分は共通のものを利用しているので、使いようがあるはずだ。
S1coreの起動方法をトレースすると、OpenSPARC T1も動作させることが出来るに違いないと思い、S1Coreの動作方法について調査を行った。
S1coreは以下のような階層構成を取っている。
ここでsparc_0
と表記されているのがOpenSPARCのコア1つ分で、それを包んでいるのがs1_top_0
という階層になる。
ここでは、以下の外部ポートが制約されており、駆動しないようになっている。
/* * Continuous assignments */ assign const_cpuid = 4'h0; assign const_maskid = 8'h20; // sscan assign ctu_tck = 1'b0; assign ctu_sscan_se = 1'b0; assign ctu_sscan_snap = 1'b0; assign ctu_sscan_tid = 4'h1; // bist assign ctu_tst_mbist_enable = 1'b0; // efuse assign efc_spc_fuse_clk1 = 1'b0; // Activity assign efc_spc_fuse_clk2 = 1'b0; // Activity assign efc_spc_ifuse_ashift = 1'b0; assign efc_spc_ifuse_dshift = 1'b0; assign efc_spc_ifuse_data = 1'b0; // Activity assign efc_spc_dfuse_ashift = 1'b0; assign efc_spc_dfuse_dshift = 1'b0; assign efc_spc_dfuse_data = 1'b0; // Activity // scan and macro test assign ctu_tst_macrotest = 1'b0; assign ctu_tst_scan_disable = 1'b0; assign ctu_tst_short_chain = 1'b0; assign global_shift_enable = 1'b0; assign ctu_tst_scanmode = 1'b0; assign spc_scanin0 = 1'b0; assign spc_scanin1 = 1'b0;
これをOpenSPARC側にも移植すれば良いのではないか?ということで移植して際シミュレーションしてみたのだが、どうやらそれだけではSPCパケットはまだ発行されない。
まだリセット解除に問題があるようだ。
OpenSPARC T1のインタフェースは、どうやらSPARC固有のパケットを使って通信しているらしい。 このパケットを分解して、S1Coreに搭載されているWishBoneに載せ変えているのがSPC2WBMユニットだ。
インタフェースは以下のようになっており、SPARCのパケットを受信してWishBoneへの変換ステートマシンが搭載されている。
インタフェース名 | 接続信号 | |
---|---|---|
Top-level system inputs | sys_clock_i | sys_clock_i |
sys_reset_i | sys_reset_final | |
sys_interrupt_source_i | sys_interrupt_source | |
Bridge inputs connected to SPARC Core outputs | spc_req_i | spc_pcx_req_pq |
spc_atom_i | spc_pcx_atom_pq | |
spc_packetout_i | spc_pcx_data_pa | |
Bridge outputs connected to SPARC Core inputs | spc_grant_o | pcx_spc_grant_px |
spc_ready_o | cpx_spc_data_rdy_cx2 | |
spc_packetin_o | cpx_spc_data_cx2 | |
spc_stall_o | wbm_spc_stall | |
spc_resume_o | wbm_spc_resume | |
Top-level Wishbone Interconnect inputs | wbm_ack_i | wbm_ack_i |
wbm_data_i | wbm_data_i | |
Top-level Wishbone Interconnect outputs | wbm_cycle_o | wbm_cycle_o |
wbm_strobe_o | wbm_strobe_o | |
wbm_we_o | wbm_we_o | |
wbm_addr_o | wbm_addr_o | |
wbm_data_o | wbm_data_o | |
wbm_sel_o | wbm_sel_o |
SPARCの命令フェッチパケットの構成
ちなみに、T1のパケットは以下のような124ビットの構成であり、図に起こすとさらに以下のような図になる。
#define PCX_VLD 123 //PCX packet valid #define PCX_RQ_HI 122 //PCX request type field #define PCX_RQ_LO 118 #define PCX_NC 117 //PCX non-cacheable bit #define PCX_R 117 //PCX read/!write bit #define PCX_CP_HI 116 //PCX cpu_id field #define PCX_CP_LO 114 #define PCX_TH_HI 113 //PCX Thread field #define PCX_TH_LO 112 #define PCX_BF_HI 111 //PCX buffer id field #define PCX_INVALL 111 #define PCX_BF_LO 109 #define PCX_WY_HI 108 //PCX replaced L1 way field #define PCX_WY_LO 107 #define PCX_P_HI 108 //PCX packet ID, 1st STQ - 10, 2nd - 01 #define PCX_P_LO 107 #define PCX_SZ_HI 106 //PCX load/store size field #define PCX_SZ_LO 104 #define PCX_ERR_HI 106 //PCX error field #define PCX_ERR_LO 104 #define PCX_AD_HI 103 //PCX address field #define PCX_AD_LO 64 #define PCX_DA_HI 63 //PCX Store data #define PCX_DA_LO 0
詳細は省略するが、内部にステートマシンが組まれており、SPARCのパケットをWishBoneに変換している。
OpenSPARC T1のラッパーであるS1Coreは、デフォルトではVcsもしくはIcarus Verilogで動作するようになっているが、これらで動作するのだったらModelsim Starter Editionでも動作するはずだ。 デバッグにあたり、やはりModelSimの方が簡単なので、Modelsimへの環境を以降しておこう。
まずは、S1Coreのためのリポジトリを作成した。
S1Coreの環境変数では、S1CoreそのものとOpenSPARC T1の場所を設定する必要がある。 これに加えて、ModelSim用のファイルリストを作成する記述を追加した。
diff --git a/s1_core/sourceme b/s1_core/sourceme index 8f8d03b..01112cb 100755 --- a/s1_core/sourceme +++ b/s1_core/sourceme @@ -6,6 +6,7 @@ export PATH=.:$S1_ROOT/tools/bin:$PATH # Filelist names export FILELIST_ICARUS=$S1_ROOT/hdl/filelist.icarus +export FILELIST_QUESTA=$S1_ROOT/hdl/filelist.questa export FILELIST_VCS=$S1_ROOT/hdl/filelist.vcs export FILELIST_FPGA=$S1_ROOT/hdl/filelist.fpga export FILELIST_DC=$S1_ROOT/hdl/filelist.dc
さらに、build_questa
および run_questa
を作成した。
#!/bin/bash if [ -z "$S1_ROOT" ]; then echo "***ERROR***: S1_ROOT variable is undefined, please set it and run 'source sourceme'."; exit 1; fi if ! [ -d "$S1_ROOT" ]; then echo "***ERROR***: directory '$S1_ROOT' does not exist, please check it and run 'source sourceme' again."; exit 1; fi echo "Building testbench using QuestaSim simulator..." mkdir -p $S1_ROOT/run/sim/questa cd $S1_ROOT/run/sim/questa # rm -rf * vlib worklib vlog -work worklib -f $FILELIST_QUESTA echo -e "Done!"
#!/bin/bash if [ -z "$S1_ROOT" ]; then echo "***ERROR***: S1_ROOT variable is undefined, please set it and run 'source sourceme'."; exit 1; fi if ! [ -d "$S1_ROOT" ]; then echo "***ERROR***: directory '$S1_ROOT' does not exist, please check it and run 'source sourceme' again."; exit 1; fi cd $S1_ROOT/run/sim/questa ln -f -s $S1_ROOT/tests/boot/rom_harness.hex . ln -f -s $S1_ROOT/tests/ram_harness.hex . vsim -c worklib.testbench -voptargs="+acc" -l sim.log -do "run -all; quit" echo "Simulation completed!" echo "To see the output:" echo " less $S1_ROOT/run/sim/questa/sim.log" echo "Too watch the waveforms:" echo " gtkwave $S1_ROOT/run/sim/questa/trace.vcd"
S1Coreのリポジトリにはhello.c
というサンプルプログラムが用意されているが、これを動作させるためにはもちろんSparcのコンパイラでコンパイルしなければならない。
// Sample program that writes two words at a predefined address int main() { unsigned long* address; address = (unsigned long*)0x0000CAC0; (*address) = 0xC1A0C1A0; // First store address = (unsigned long*)0x0000CAC0; (*address) = 0xFABA1210; // Second store return 0; }
このためにUbuntuのパッケージとしてgcc-sparc64-linux-gnu
をインストールした。
sudo aptitude install -y gcc-sparc64-linux-gnu
そしてcompile_test
コマンドをいくつか改造する必要がある。
と言っても、sparc64-linux-
をsparc64-linux-gnu-
に変更しただけだ。
diff --git a/s1_core/tools/bin/compile_test b/s1_core/tools/bin/compile_test index 5684c25..060ab69 100755 --- a/s1_core/tools/bin/compile_test +++ b/s1_core/tools/bin/compile_test @@ -6,7 +6,7 @@ # Parameter is test name without extension (e.g. to compile # $S1_ROOT/tests/hello.c) just run "compile_test hello". # -# Note: requires sparc64-linux-gcc (see Download section on +# Note: requires sparc64-linux-gnu-gcc (see Download section on # http://www.srisc.com). if [ -z "$S1_ROOT" ]; then echo "***ERROR***: S1_ROOT variable is undefined, please set it and run 'source sourceme'."; exit 1; fi @@ -32,15 +32,14 @@ fi gcc -o $S1_ROOT/tools/bin/dump2hex.bin $S1_ROOT/tools/src/dump2hex.c # Compile the boot code -sparc64-linux-as -ah -am -o boot/boot.bin boot/boot.s -sparc64-linux-objdump -d -EB -w -z boot/boot.bin > boot/boot.dump -grep " " boot/boot.dump | egrep -v "file format" | dump2hex.bin > boot/rom_harness.hex +sparc64-linux-gnu-as -ah -am -o boot/boot.bin boot/boot.s +sparc64-linux-gnu-objdump -d -EB -w -z boot/boot.bin > boot/boot.dump +grep " " boot/boot.dump | egrep -v "elf64-sparc" | dump2hex.bin > boot/rom_harness.hex # Compile the C test -sparc64-linux-gcc -c -O0 $1.c -sparc64-linux-ld -Ur --script=$S1_ROOT/tools/src/linker.map -EB -o $1.bin $1.o -sparc64-linux-objdump -d -EB -w -z $1.bin > $1.dump -grep " " $1.dump | egrep -v "file format" | dump2hex.bin > ram_harness.hex +sparc64-linux-gnu-gcc -c -O0 $1.c +sparc64-linux-gnu-ld -Ur --script=$S1_ROOT/tools/src/linker.map -EB -o $1.bin $1.o +sparc64-linux-gnu-objdump -d -EB -w -z $1.bin > $1.dump +grep " " $1.dump | egrep -v "elf64-sparc" | dump2hex.bin > ram_harness.hex #rm -f *.o *~ *.bin *.dump # Make clean -
最初は何のことだか分からなかったのだが、以下のfile format
を除去するのは、dumpファイルの先頭行を除去したいようなのだが、環境変数が日本語だと違う文字列になってしまうので、共通のelf64-sparc
に変更した。
-grep " " $1.dump | egrep -v "file format" | dump2hex.bin > ram_harness.hex +grep " " $1.dump | egrep -v "elf64-sparc" | dump2hex.bin > ram_harness.hex
これで、build_questa && run_questa
で run/sim/questa/
にシミュレーションログが出力された。
正しく動作しているようだ。
cd run/sim/questa less sim.log ... # INFO: MEMH testbench.ram_harness: R @ 35410000 ns, AD=0800000000040018 SEL=ff DAT=8410a1a0c4704000 # INFO: WBM2SPC: *** RETURN PACKET TO SPARC CORE *** # INFO: WBM2SPC: Valid bit is 1 # INFO: WBM2SPC: Return Packet of Type IFILL_RET # INFO: WBM2SPC: L2 Miss is 0 # INFO: WBM2SPC: Error is 0 # INFO: WBM2SPC: Non-Cacheable bit is 1 # INFO: WBM2SPC: Thread is 0 # INFO: WBM2SPC: Way Valid is 0 # INFO: WBM2SPC: Replaced L2 Way is 0 # INFO: WBM2SPC: Fetch for Boot is 0 # INFO: WBM2SPC: Atomic LD/ST or 2nd IFill Packet is 1 # INFO: WBM2SPC: PFL is 0 # INFO: WBM2SPC: Data is c25fa7f7053068308410a1a0c4704000 # INFO: TBENCH: Completed Simply RISC S1 Core simulation! # ** Note: $finish : /home/masayuki/work/s1_core_opensparc_t1/s1_core/hdl/behav/testbench/testbench.v(94) # Time: 50 us Iteration: 0 Instance: /testbench # End time: 01:15:23 on Mar 04,2017, Elapsed time: 0:09:38 # Errors: 0, Warnings: 43
OpenSPARC T1のコア部分のみを切り出したものとして、S-RISC (Simply-RISC)というものをコメントで紹介してもらった。
どうやら、コアの部分をOpenSPARC-T1として利用し、それ以外の外側を独自に囲むことによってOpenSPARCを動作させるものらしい。 概要図を当該HPから引用する。
上記のとおり、OpenSPARC-T1のコアはそのまま利用し、外部へのWishboneバスおよびペリフェラルを追加することによって動作させる仕組みだ。
With this new system, now Linux boots correctly as single-thread! And even using PROM file 1c4t_obp_prom.bin, i.e. the four-threads version of the OpenBoot code, Linux starts booting.
どうやらシングルコアでもLinuxのブートに成功しているらしい。すごいな!
という訳で早速チャレンジしてみることにする。
以下からS1 Coreのダウンロードを行う。解答するとsourceme
というファイルが見えるので、これを編集する。
# General paths settings export S1_ROOT=~/work/s1_core export T1_ROOT=~/work/opensparc/t1 export PATH=.:$S1_ROOT/tools/bin:$PATH
まず、OpenSPARC T1のソースコードはダウンロードしていること(http://www.oracle.com/technetwork/systems/opensparc/opensparc-t1-page-1444609.html)が必要である。
コア部分はOpenSPARCそのものを利用しているということになる (これはT1_ROOT
変数にて指定されている)。
そして、ダウンロードしたS1 Coreの場所を指定している (これはS1_ROOT
変数にて指定されている)。
sourceme
を読み込んで、update_filelist
によりファイルリストが更新される。
source sourceme
update_filelist
シェル一覧を見ていると、どうやらVcs, Icarus Verilogでの動作をサポートしているらしい。Vcsなんて持っていないので、Icarus Verilogでシミュレーションしてみよう。
build_icarus run_icarus
しばらくするとシミュレーションが完了する。vcdファイルが生成されたようだ。
$ run_icarus
Simulation completed!
To see the output:
less /home/xxx/work/s1_core/run/sim/icarus/sim.log
Too watch the waveforms:
gtkwave /home/xxx/work/s1_core/run/sim/icarus/trace.vcd
vcdを眺めるのは面倒だし、ちょうどModelSimをインストールしているのでwlf形式に変換した。
vcd2wlf trace.vcd trace.wlf
ModelSimで波形を開いてみた。構造としてはS1コアの横に命令ROM、データROMが接続されているような状態だ。 命令ROMの波形を出してみると、ちゃんとフェッチが効いているようなので、これは動作しているのかな?もうちょっと掘り下げてみよう。