RV64Iの実装
これまでに、RISC-Vの32ビットの基本整数命令セットであるRV32IのCPUを実装しました。 RISC-Vには64ビットの基本整数命令セットとしてRV64Iが定義されています。 本章では、RV32IのCPUをRV64Iにアップグレードします。
では、具体的にRV32IとRV64Iは何が違うのでしょうか? まず、RV64IではXLENが32ビットから64ビットに変更され、レジスタの幅や各種演算命令の演算の幅が64ビットになります。 それに伴い、 32ビット幅での整数演算を行う命令、 64ビット幅でロードストアを行う命令が追加されます(表1)。 また、演算の幅が64ビットに広がるだけではなく、 一部の命令の動作が少し変わります(表2)。
表6.1: RV64Iで追加される命令
| 命令 | 動作 |
|---|---|
| ADD[I]W | 32ビット単位で加算を行う。結果は符号拡張する |
| SUBW | 32ビット単位で減算を行う。結果は符号拡張する |
| SLL[I]W | レジスタの値を0 ~ 31ビット左論理シフトする。結果は符号拡張する |
| SRL[I]W | レジスタの値を0 ~ 31ビット右論理シフトする。結果は符号拡張する |
| SRA[I]W | レジスタの値を0 ~ 31ビット右算術シフトする。結果は符号拡張する |
| LWU | メモリから32ビット読み込む。結果はゼロで拡張する |
| LD | メモリから64ビット読み込む |
| SD | メモリに64ビット書き込む |
表6.2: RV64Iで変更される命令
| 命令 | 変更後の動作 |
|---|---|
| SLL[I] | 0 ~ 63ビット左論理シフトする |
| SRL[I] | 0 ~ 63ビット右論理シフトする |
| SRA[I] | 0 ~ 63ビット右算術シフトする |
| LUI | 32ビットの即値を生成する。結果は符号拡張する |
| AUIPC | 32ビットの即値を符号拡張したものにpcを足し合わせる |
| LW | メモリから32ビット読み込む。結果は符号拡張する |
XLENの変更
レジスタの幅が32ビットから64ビットに変わるということは、 XLENが32から64に変わるということです。 eeiパッケージに定義しているXLENを64に変更します(リスト1)。 RV64Iになっても命令の幅(ILEN)は32ビットのままです。
▼リスト6.1: XLENを変更する (eei.veryl) 差分をみる
const XLEN: u32 = 64;
SLL[I]、SRL[I]、SRA[I]命令を変更する
RV32Iでは、シフト命令はrs1の値を0 ~ 31ビットシフトする命令として定義されています。 これがRV64Iでは、rs1の値を0 ~ 63ビットシフトする命令に変更されます。
これに対応するために、ALUのシフト演算する量を5ビットから6ビットに変更します (リスト2)。 I形式の命令(SLLI、SRLI、SRAI)のときは即値の下位6ビット、 R形式の命令(SLL、SRL、SRA)のときはレジスタの下位6ビットを利用します。
▼リスト6.2: シフト命令でシフトする量を変更する (alu.veryl) 差分をみる
let sll: UIntX = op1 << op2[5:0];
let srl: UIntX = op1 >> op2[5:0];
let sra: SIntX = $signed(op1) >>> op2[5:0];
LUI、AUIPC命令を変更する
RV32Iでは、LUI命令は32ビットの即値をそのままレジスタに格納する命令として定義されています。 これがRV64Iでは、32ビットの即値を64ビットに符号拡張した値を格納する命令に変更されます。 AUIPC命令も同様で、即値にPCを足す前に、即値を64ビットに符号拡張します。
この対応ですが、XLENを64に変更した時点ですでに完了しています(リスト3)。 そのため、コードの変更の必要はありません。
▼リスト6.3: U形式の即値はXLENビットに拡張されている (inst_decoder.veryl) 差分をみる
let imm_u: UIntX = {bits[31] repeat XLEN - $bits(imm_u_g) - 12, imm_u_g, 12'b0};
CSRを変更する
MXLEN(=XLEN)が64ビットに変更されると、CSRの幅も64ビットに変更されます。 そのため、mtvec、mepc、mcauseレジスタの幅を64ビットに変更する必要があります。
しかし、mtvec、mepc、mcauseレジスタは XLENビットのレジスタ(UIntX)として定義しているため、 変更の必要はありません。 また、mtvec、mepc、mcauseレジスタはMXLENを基準に定義されており、 RV32IからRV64Iに変わってもフィールドに変化はないため、 対応は必要ありません。
唯一、書き込みマスクの幅を広げる必要があります (リスト4)。
▼リスト6.4: CSRの書き込みマスクの幅を広げる (csrunit.veryl) 差分をみる
const MTVEC_WMASK : UIntX = 'hffff_ffff_ffff_fffc;
const MEPC_WMASK : UIntX = 'hffff_ffff_ffff_fffc;
const MCAUSE_WMASK: UIntX = 'hffff_ffff_ffff_ffff;
LW命令を変更する
LW命令は32ビットの値をロードする命令です。 RV64Iでは、LW命令の結果が64ビットに符号拡張されるようになります。 これに対応するため、memunitモジュールのrdataの割り当てのLW部分を変更します (リスト5)。
▼リスト6.5: LW命令のメモリの読み込み結果を符号拡張する (memunit.veryl) 差分をみる
2'b10 : {D[31] repeat W - 32, D[31:0]},
また、XLENが64に変更されたことで、 幅をMEM_DATA_WIDTH(=32)として定義しているreq_wdataの代入文のビット幅が左右で合わなくなってしまっています。 ビット幅を合わせるために、rs2の下位MEM_DATA_WIDTHビットだけを切り取ります (リスト6)。
▼リスト6.6: 左辺と右辺でビット幅を合わせる (memunit.veryl) 差分をみる
case state {
State::Init: if is_new & inst_is_memop(ctrl) {
state = State::WaitReady;
req_wen = inst_is_store(ctrl);
req_addr = addr;
req_wdata = rs2[MEM_DATA_WIDTH - 1:0] << {addr[1:0], 3'b0};
riscv-testsでテストする
RV32I向けのテストの実行
まず、RV32I向けのテストが正しく動くことを確認します(リスト7)。
▼リスト6.7: RV32I向けのテストを実行する
$ make build
$ make sim VERILATOR_FLAGS="-DTEST_MODE"
$ python3 test/test.py -r obj_dir/sim test/share rv32ui-p-
...
PASS : ~/core/test/share/riscv-tests/isa/rv32ui-p-srl.bin.hex
Test Result : 40 / 40
RV32I向けのテストにすべて成功しました。 riscv-testsのRV32I向けのテストは、XLENが64のときにテストを実行せずに成功とします(リスト8)。
▼リスト6.8: rv32ui-p-addはXLENが64のときにテストせずに成功する (rv32ui-p-add.dump)
00000050 <reset_vector>:
...
13c: 00100513 li a0,1 ← a0 = 1
140: 01f51513 slli a0,a0,0x1f ← a0を31ビット左シフト
144: 00054c63 bltz a0,15c <reset_vector+0x10c> ← a0が0より小さかったらジャンプ
148: 0ff0000f fence
14c: 00100193 li gp,1 ← gp=1 (テスト成功) にする
150: 05d00893 li a7,93
154: 00000513 li a0,0
158: 00000073 ecall ← trap_vectorにジャンプして終了
riscv-testsは、a0に1を代入した後、a0を31ビット左シフトします。 XLENが32のとき、a0の最上位ビット(符号ビット)が1になり、a0は0より小さくなります。 XLENが64のとき、a0の符号は変わらないため、a0は0より大きくなります。 これを利用して、XLENが32ではないときはtrap_vectorにジャンプして、テスト成功として終了しています。
RV64I向けのテストの実行
それでは、RV64I向けのテストを実行します(リスト9)。 RV64I向けのテストは名前がrv64ui-p-から始まります、
▼リスト6.9: RV64I向けのテストを実行する
$ make build
$ make sim VERILATOR_FLAGS="-DTEST_MODE"
$ python3 test/test.py -r obj_dir/sim test/share rv64ui-p-
...
FAIL : ~/core/test/share/riscv-tests/isa/rv64ui-p-add.bin.hex
...
Test Result : 14 / 52
ADD命令のテストを含む、ほとんどのテストに失敗してしまいました。 これはriscv-testsのテストが、まだ未実装の命令を含むためです(リスト10)。
▼リスト6.10: ADD命令のテストは未実装の命令(ADDIW命令)を含む (rv64ui-p-add.dump)
0000000000000208 <test_7>:
208: 00700193 li gp,7
20c: 800005b7 lui a1,0x80000
210: ffff8637 lui a2,0xffff8
214: 00c58733 add a4,a1,a2
218: ffff03b7 lui t2,0xffff0
21c: fff3839b addiw t2,t2,-1 # fffffffffffeffff <_end+0xfffffffffffedfff>
220: 00f39393 slli t2,t2,0xf
224: 46771063 bne a4,t2,684 <fail>
ということで、失敗していることを気にせずに実装を進めます。
ADD[I]W、SUBW命令の実装
RV64Iでは、ADD命令は64ビット単位で演算する命令になり、 32ビットの加算をするADDW命令とADDIW命令が追加されます。 同様に、SUB命令は64ビッド単位の演算になり、 32ビットの減算をするSUBW命令が追加されます。 32ビットの演算結果は符号拡張します。
ADD[I]W、SUBW命令をデコードする
ADDW命令とSUBW命令はR形式で、opcodeはOP-32(7'b0111011)です。 ADDIW命令はI形式で、opcodeはOP-IMM-32(7'b0011011)です。
まず、eeiパッケージにopcodeの定数を定義します (リスト11)。
▼リスト6.11: opcodeを定義する (eei.veryl) 差分をみる
const OP_OP_32 : logic<7> = 7'b0111011;
const OP_OP_IMM_32: logic<7> = 7'b0011011;
次に、InstCtrl構造体に、 32ビット単位で演算を行う命令であることを示すis_op32フラグを追加します (リスト12)。
▼リスト6.12: is_op32を追加する (corectrl.veryl) 差分をみる
struct InstCtrl {
itype : InstType , // 命令の形式
rwb_en : logic , // レジスタに書き込むかどうか
is_lui : logic , // LUI命令である
is_aluop: logic , // ALUを利用する命令である
is_op32 : logic , // OP-32またはOP-IMM-32である
is_jump : logic , // ジャンプ命令である
is_load : logic , // ロード命令である
is_csr : logic , // CSR命令である
funct3 : logic <3>, // 命令のfunct3フィールド
funct7 : logic <7>, // 命令のfunct7フィールド
}
inst_decoderモジュールのInstCtrlと即値を生成している部分を変更します ( リスト13、 リスト14 )。 これでデコードは完了です。
▼リスト6.13: OP-32、OP-IMM-32のInstCtrlの生成 (inst_decoder.veryl) 差分をみる
OP_OP_IMM: {
InstType::I, T, F, T, F, F, F, F
},
OP_OP_32: {
InstType::R, T, F, T, T, F, F, F
},
OP_OP_IMM_32: {
InstType::I, T, F, T, T, F, F, F
},
OP_SYSTEM: {
InstType::I, T, F, F, F, F, F, T
},
▼リスト6.14: OP-IMM-32の即値の生成 (inst_decoder.veryl) 差分をみる
imm = case op {
OP_LUI, OP_AUIPC : imm_u,
OP_JAL : imm_j,
OP_JALR, OP_LOAD : imm_i,
OP_OP_IMM, OP_OP_IMM_32: imm_i,
OP_BRANCH : imm_b,
OP_STORE : imm_s,
default : 'x,
};
ALUにADDW、SUBWを実装する
制御フラグを生成できたので、 それに応じて32ビットのADDとSUBを計算します。
まず、32ビットの足し算と引き算の結果を生成します (リスト15)。
▼リスト6.15: 32ビットの足し算と引き算をする (alu.veryl) 差分をみる
let add32: UInt32 = op1[31:0] + op2[31:0];
let sub32: UInt32 = op1[31:0] - op2[31:0];
次に、フラグによって演算結果を選択する関数sel_wを作成します (リスト16)。 この関数は、 is_op32が1ならvalue32を64ビットに符号拡張した値、 0ならvalue64を返します。
▼リスト6.16: 演算結果を選択する関数を作成する (alu.veryl) 差分をみる
function sel_w (
is_op32: input logic ,
value32: input UInt32,
value64: input UInt64,
) -> UInt64 {
if is_op32 {
return {value32[msb] repeat 32, value32};
} else {
return value64;
}
}
sel_w関数を使用し、aluモジュールの演算処理を変更します。 case文の足し算と引き算の部分を次のように変更します (リスト17)。
▼リスト6.17: 32ビットの演算結果を選択する (alu.veryl) 差分をみる
always_comb {
if ctrl.is_aluop {
case ctrl.funct3 {
3'b000 : result = if ctrl.itype == InstType::I | ctrl.funct7 == 0 ? sel_w(ctrl.is_op32, add32, add) : sel_w(ctrl.is_op32, sub32, sub);
3'b001 : result = sll;
3'b010 : result = slt;
ADD[I]W、SUBW命令をテストする
RV64I向けのテストを実行して、結果ファイルを確認します ( リスト18、 リスト19 )。
▼リスト6.18: RV64I向けのテストを実行する
$ make build
$ make sim VERILATOR_FLAGS="-DTEST_MODE"
$ python3 test/test.py -r obj_dir/sim test/share rv64ui-p-
▼リスト6.19: テストの実行結果 (results/result.txt)
Test Result : 42 / 52
FAIL : ~/core/test/share/riscv-tests/isa/rv64ui-p-ld.bin.hex
FAIL : ~/core/test/share/riscv-tests/isa/rv64ui-p-lwu.bin.hex
FAIL : ~/core/test/share/riscv-tests/isa/rv64ui-p-ma_data.bin.hex
FAIL : ~/core/test/share/riscv-tests/isa/rv64ui-p-sd.bin.hex
FAIL : ~/core/test/share/riscv-tests/isa/rv64ui-p-slliw.bin.hex
FAIL : ~/core/test/share/riscv-tests/isa/rv64ui-p-sllw.bin.hex
FAIL : ~/core/test/share/riscv-tests/isa/rv64ui-p-sraiw.bin.hex
FAIL : ~/core/test/share/riscv-tests/isa/rv64ui-p-sraw.bin.hex
FAIL : ~/core/test/share/riscv-tests/isa/rv64ui-p-srliw.bin.hex
FAIL : ~/core/test/share/riscv-tests/isa/rv64ui-p-srlw.bin.hex
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-add.bin.hex
...
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-addiw.bin.hex
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-addw.bin.hex
...
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-subw.bin.hex
...
ADDIW、ADDW、SUBWだけでなく、未実装の命令以外のテストにも成功しました。
SLL[I]W、SRL[I]W、SRA[I]W命令の実装
RV64Iでは、SLL[I]、SRL[I]、SRA[I]命令はrs1を0 ~ 63ビットシフトする命令になり、 rs1の下位32ビットを0 ~ 31ビットシフトするSLL[I]W、SRL[I]W、SRA[I]W命令が追加されます。 32ビットの演算結果は符号拡張します。
SLL[I]W、SRL[I]W、SRA[I]W命令のフォーマットは、 RV32IのSLL[I]、SRL[I]、SRA[I]命令のopcodeを変えたものと同じです。 SLLW、SRLW、SRAW命令はR形式で、opcodeはOP-32です。 SLLIW、SRLIW、SRAIW命令はI形式で、opcodeはOP-IMM-32です。 どちらのopcodeの命令も、 ADD[I]W命令とSUBW命令の実装時にデコードが完了しています。
aluモジュールで、32ビットのシフト演算の結果を生成します (リスト20)。
▼リスト6.20: 32ビットのシフト演算をする (alu.veryl) 差分をみる
let sll32: UInt32 = op1[31:0] << op2[4:0];
let srl32: UInt32 = op1[31:0] >> op2[4:0];
let sra32: SInt32 = $signed(op1[31:0]) >>> op2[4:0];
生成したシフト演算の結果をsel_w関数で選択します。 case文のシフト演算の部分を次のように変更します (リスト21)。
▼リスト6.21: 32ビットの演算結果を選択する (alu.veryl) 差分をみる
case ctrl.funct3 {
3'b000: result = if ctrl.itype == InstType::I | ctrl.funct7 == 0 ? sel_w(ctrl.is_op32, add32, add) : sel_w(ctrl.is_op32, sub32, sub);
3'b001: result = sel_w(ctrl.is_op32, sll32, sll);
3'b010: result = slt;
3'b011: result = sltu;
3'b100: result = op1 ^ op2;
3'b101: result = if ctrl.funct7[5] == 0 ? sel_w(ctrl.is_op32, srl32, srl) : sel_w(ctrl.is_op32, sra32, sra);
SLL[I]W、SRL[I]W、SRA[I]W命令をテストする
RV64I向けのテストを実行し、結果ファイルを確認します (リスト22、リスト23)。
▼リスト6.22: RV64I向けのテストを実行する
$ make build
$ make sim VERILATOR_FLAGS="-DTEST_MODE"
$ python3 test/test.py -r obj_dir/sim test/share rv64ui-p-
▼リスト6.23: テストの実行結果 (results/result.txt)
Test Result : 48 / 52
FAIL : ~/core/test/share/riscv-tests/isa/rv64ui-p-ld.bin.hex
FAIL : ~/core/test/share/riscv-tests/isa/rv64ui-p-lwu.bin.hex
FAIL : ~/core/test/share/riscv-tests/isa/rv64ui-p-ma_data.bin.hex
FAIL : ~/core/test/share/riscv-tests/isa/rv64ui-p-sd.bin.hex
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-add.bin.hex
...
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-sll.bin.hex
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-slli.bin.hex
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-slliw.bin.hex
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-sllw.bin.hex
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-sra.bin.hex
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-srai.bin.hex
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-sraiw.bin.hex
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-sraw.bin.hex
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-srl.bin.hex
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-srli.bin.hex
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-srliw.bin.hex
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-srlw.bin.hex
...
SLLW、SLLIW、SRLW、SRLIW、SRAW、SRAIW命令のテストに成功していることを確認できます。
LWU命令の実装
LB、LH命令は、ロードした値を符号拡張した値をレジスタに格納します。 これに対して、LBU、LHU命令は、 ロードした値をゼロで拡張した値をレジスタに格納します。
同様に、LW命令は、ロードした値を符号拡張した値をレジスタに格納します。 これに対して、RV64Iでは、 ロードした32ビットの値をゼロで拡張した値をレジスタに格納する LWU命令が追加されます。
LWU命令はI形式で、opcodeは
LOADです。 ロードストア命令はfunct3によって区別できて、LWU命令のfunct3は3'b110です。 デコード処理に変更は必要なく、メモリにアクセスする処理を変更する必要があります。
memunitモジュールの、ロードする部分を変更します。 32ビットをrdataに割り当てるとき、 sextによって符号かゼロで拡張するかを選択します (リスト24)。
▼リスト6.24: LWU命令の実装 (memunit.veryl) 差分をみる
2'b10 : {sext & D[31] repeat W - 32, D[31:0]},
LWU命令をテストする
LWU命令のテストを実行します(リスト25)。
▼リスト6.25: LWU命令をテストする
$ make build
$ make sim VERILATOR_FLAGS="-DTEST_MODE"
$ python3 test/test.py -r obj_dir/sim test/share rv64ui-p-lwu
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-lwu.bin.hex
Test Result : 1 / 1
LD、SD命令の実装
RV64Iには、64ビット単位でロードストアを行うLD命令とSD命令が定義されています。
LD命令はI形式で、opcodeは
LOADです。 SD命令はS形式で、opcodeはSTOREです。 どちらの命令もfunct3は3'b011です。 デコード処理に変更は必要ありません。
メモリの幅を広げる
現在のメモリの1つのデータの幅(MEM_DATA_WIDTH)は32ビットですが、 このままだと64ビットでロードやストアを行うときに、 最低2回のメモリアクセスが必要です。 これを1回のメモリアクセスで済ませるために、 データの幅を32ビットから64ビットに広げます (リスト26)。
▼リスト6.26: MEM_DATA_WIDTHを64ビットに変更する (eei.veryl) 差分をみる
const MEM_DATA_WIDTH: u32 = 64;
命令フェッチ処理を修正する
XLEN、MEM_DATA_WIDTHが変わっても、 命令の長さ(ILEN)は32ビットのままです。 そのため、topモジュールのi_membus.rdataの幅は32ビットなのに対し、 membus.rdataは64ビットになり、ビット幅が一致しません。
ビット幅を合わせて正しく命令をフェッチするために、 64ビットの読み出しデータの上位32ビット、 下位32ビットをアドレスの下位ビットで選択します。 アドレスが8の倍数のときは下位32ビット、 それ以外のときは上位32ビットを選択します。
まず、命令フェッチの要求アドレスをレジスタに格納します ( リスト27、 リスト28 )。
▼リスト6.27: アドレスを格納するためのレジスタの定義 (top.veryl) 差分をみる
var memarb_last_i : logic;
var memarb_last_iaddr: Addr ;
▼リスト6.28: レジスタに命令フェッチの要求アドレスを格納する (top.veryl) 差分をみる
// メモリアクセスを調停する
always_ff {
if_reset {
memarb_last_i = 0;
memarb_last_iaddr = 0;
} else {
if membus.ready {
memarb_last_i = !d_membus.valid;
memarb_last_iaddr = i_membus.addr;
}
}
}
このレジスタの値を利用し、 i_membus.rdataに割り当てる値を選択します (リスト29)。
▼リスト6.29: アドレスによってデータを選択する (top.veryl) 差分をみる
i_membus.rdata = if memarb_last_iaddr[2] == 0 ? membus.rdata[31:0] : membus.rdata[63:32];
SD命令を実装する
SD命令の実装のためには、 書き込むデータ(wdata)と書き込みマスク(wmask)を変更する必要があります。
書き込むデータはアドレスの下位2ビットではなく下位3ビット分シフトします (リスト30)。
▼リスト6.30: 書き込むデータの変更 (memunit.veryl) 差分をみる
req_wdata = rs2 << {addr[2:0], 3'b0};
書き込みマスクは4ビットから8ビットに拡張されるため、 アドレスの下位2ビットではなく下位3ビットで選択します (リスト31)。
▼リスト6.31: 書き込みマスクの変更 (memunit.veryl) 差分をみる
req_wmask = case ctrl.funct3[1:0] {
2'b00: 8'b1 << addr[2:0],
2'b01: case addr[2:0] {
6 : 8'b11000000,
4 : 8'b00110000,
2 : 8'b00001100,
0 : 8'b00000011,
default: 'x,
},
2'b10: case addr[2:0] {
0 : 8'b00001111,
4 : 8'b11110000,
default: 'x,
},
2'b11 : 8'b11111111,
default: 'x,
};
LD命令を実装する
メモリのデータ幅が64ビットに広がるため、 rdataに割り当てる値を、 アドレスの下位2ビットではなく下位3ビットで選択します (リスト32)。
▼リスト6.32: rdataの変更 (memunit.veryl) 差分をみる
rdata = case ctrl.funct3[1:0] {
2'b00: case addr[2:0] {
0 : {sext & D[7] repeat W - 8, D[7:0]},
1 : {sext & D[15] repeat W - 8, D[15:8]},
2 : {sext & D[23] repeat W - 8, D[23:16]},
3 : {sext & D[31] repeat W - 8, D[31:24]},
4 : {sext & D[39] repeat W - 8, D[39:32]},
5 : {sext & D[47] repeat W - 8, D[47:40]},
6 : {sext & D[55] repeat W - 8, D[55:48]},
7 : {sext & D[63] repeat W - 8, D[63:56]},
default: 'x,
},
2'b01: case addr[2:0] {
0 : {sext & D[15] repeat W - 16, D[15:0]},
2 : {sext & D[31] repeat W - 16, D[31:16]},
4 : {sext & D[47] repeat W - 16, D[47:32]},
6 : {sext & D[63] repeat W - 16, D[63:48]},
default: 'x,
},
2'b10: case addr[2:0] {
0 : {sext & D[31] repeat W - 32, D[31:0]},
4 : {sext & D[63] repeat W - 32, D[63:32]},
default: 'x,
},
2'b11 : D,
default: 'x,
};
LD、SD命令をテストする
LD、SD命令のテストを実行する前に、 メモリのデータ単位が4バイトから8バイトになったため、 テストのHEXファイルを4バイト単位の改行から8バイト単位の改行に変更します (リスト33)。
▼リスト6.33: HEXファイルを8バイト単位に変更する
$ cd test
$ find share/ -type f -name "*.bin" -exec sh -c "python3 bin2hex.py 8 {} > {}.hex" \;
riscv-testsを実行します(リスト34)。
▼リスト6.34: RV32I、RV64Iをテストする
$ make build
$ make sim VERILATOR_FLAGS="-DTEST_MODE"
$ python3 test/test.py -r obj_dir/sim test/share rv32ui-p-
...
Test Result : 40 / 40
$ python3 test/test.py -r obj_dir/sim test/share rv64ui-p-
...
FAIL : ~/core/test/share/riscv-tests/isa/rv64ui-p-ma_data.bin.hex
...
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-ld.bin.hex
PASS : ~/core/test/share/riscv-tests/isa/rv64ui-p-sd.bin.hex
...
Test Result : 51 / 52
RV64IのCPUを実装できました。