Skip to content

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]W32ビット単位で加算を行う。結果は符号拡張する
SUBW32ビット単位で減算を行う。結果は符号拡張する
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ビット右算術シフトする
LUI32ビットの即値を生成する。結果は符号拡張する
AUIPC32ビットの即値を符号拡張したものにpcを足し合わせる
LWメモリから32ビット読み込む。結果は符号拡張する
実装のテストにはriscv-testsを利用します。 RV64I向けのテストは`rv64ui-p-`から始まるテストです。 命令を実装するたびにテストを実行することで、 命令が正しく実行できていることを確認します。

XLENの変更

レジスタの幅が32ビットから64ビットに変わるということは、 XLENが32から64に変わるということです。 eeiパッケージに定義しているXLENを64に変更します(リスト1)。 RV64Iになっても命令の幅(ILEN)は32ビットのままです。

▼リスト6.1: XLENを変更する (eei.veryl) 差分をみる

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) 差分をみる

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) 差分をみる

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) 差分をみる

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) 差分をみる

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) 差分をみる

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向けのテストを実行する

terminal
$ 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)

rv32i
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向けのテストを実行する

terminal
$ 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)

rv64ui-p-add
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、ADDIW、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) 差分をみる

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) 差分をみる

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) 差分をみる

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) 差分をみる

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) 差分をみる

veryl
let add32: UInt32 = op1[31:0] + op2[31:0];
let sub32: UInt32 = op1[31:0] - op2[31:0];

次に、フラグによって演算結果を選択する関数sel_wを作成します (リスト16)。 この関数は、 is_op321ならvalue32を64ビットに符号拡張した値、 0ならvalue64を返します。

▼リスト6.16: 演算結果を選択する関数を作成する (alu.veryl) 差分をみる

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) 差分をみる

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向けのテストを実行する

terminal
$ 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)

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命令のフォーマット 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) 差分をみる

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) 差分をみる

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向けのテストを実行する

terminal
$ 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)

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命令のフォーマット LWU命令はI形式で、opcodeはLOADです。 ロードストア命令はfunct3によって区別できて、LWU命令のfunct3は3'b110です。 デコード処理に変更は必要なく、メモリにアクセスする処理を変更する必要があります。

memunitモジュールの、ロードする部分を変更します。 32ビットをrdataに割り当てるとき、 sextによって符号かゼロで拡張するかを選択します (リスト24)。

▼リスト6.24: LWU命令の実装 (memunit.veryl) 差分をみる

veryl
2'b10  : {sext & D[31] repeat W - 32, D[31:0]},

LWU命令をテストする

LWU命令のテストを実行します(リスト25)。

▼リスト6.25: LWU命令をテストする

terminal
$ 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、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) 差分をみる

veryl
const MEM_DATA_WIDTH: u32 = 64;

命令フェッチ処理を修正する

XLENMEM_DATA_WIDTHが変わっても、 命令の長さ(ILEN)は32ビットのままです。 そのため、topモジュールのi_membus.rdataの幅は32ビットなのに対し、 membus.rdataは64ビットになり、ビット幅が一致しません。

ビット幅を合わせて正しく命令をフェッチするために、 64ビットの読み出しデータの上位32ビット、 下位32ビットをアドレスの下位ビットで選択します。 アドレスが8の倍数のときは下位32ビット、 それ以外のときは上位32ビットを選択します。

まず、命令フェッチの要求アドレスをレジスタに格納します ( リスト27、 リスト28 )。

▼リスト6.27: アドレスを格納するためのレジスタの定義 (top.veryl) 差分をみる

veryl
var memarb_last_i    : logic;
var memarb_last_iaddr: Addr ;

▼リスト6.28: レジスタに命令フェッチの要求アドレスを格納する (top.veryl) 差分をみる

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) 差分をみる

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) 差分をみる

veryl
req_wdata = rs2 << {addr[2:0], 3'b0};

書き込みマスクは4ビットから8ビットに拡張されるため、 アドレスの下位2ビットではなく下位3ビットで選択します (リスト31)。

▼リスト6.31: 書き込みマスクの変更 (memunit.veryl) 差分をみる

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) 差分をみる

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バイト単位に変更する

terminal
$ cd test
$ find share/ -type f -name "*.bin" -exec sh -c "python3 bin2hex.py 8 {} > {}.hex" \;

riscv-testsを実行します(リスト34)。

▼リスト6.34: RV32I、RV64Iをテストする

terminal
$ 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を実装できました。