FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

Chiselを使ってCPUを作ろう(7. Load / Store命令の実装)

Chiselを使って、非常にシンプルなCPUを作ってみるプロジェクト、ある程度演算命令は動き始めたので、次はLoad/Store命令を実装する。

RV64の命令系列では、64-bit, 32-bit, 16-bit, 8-bitのメモリアクセスが行える必要がある。 メモリの部分はVerilogで記述してもよいのだが、そうするとScalaでシミュレーションできなくなるのでこれもChiselで実装したい。

f:id:msyksphinz:20181122002550p:plain

8‐bit単位でのアクセスを行うために、メモリモジュールを改造して、8ビットのメモリが8本(バンク)存在する構成に変更した。

ChiselだとVecを使って以下のようにできる。

  for (bank <- 0 until 8) {
    val memory = Mem(math.pow(2, bus_width).toInt , UInt(8.W))
...

データの書き込み

データ幅に合わせて書き込みを行うバンクを制御するために、switch文を使用して書き込みバンクを制御した。

    val data_msb = bank * 8 + 7
    val data_lsb = bank * 8 + 0
    when(io.data_bus.req & (io.data_bus.cmd === CMD_WR)) {
      switch (io.data_bus.size) {
        is (MT_D) {
          memory(io.data_bus.addr(bus_width-1, 3)) := io.data_bus.wrdata(data_msb, data_lsb)
        }
        is (MT_W) {
          if (io.data_bus.addr(2) == bank_idx(2)) {
            memory(io.data_bus.addr(bus_width-1, 3)) := io.data_bus.wrdata(data_msb, data_lsb)
          }
        }
        is (MT_H) {
          if (io.data_bus.addr(2,1) == bank_idx(2,1)) {
            memory(io.data_bus.addr(bus_width-1, 3)) := io.data_bus.wrdata(data_msb, data_lsb)
          }
        }
        is (MT_B) {
          if (io.data_bus.addr(2,0) == bank_idx(2,0)) {
            memory(io.data_bus.addr(bus_width-1, 3)) := io.data_bus.wrdata(data_msb, data_lsb)
          }
        }
      }

データの読み込み

こちらもデータ幅とアドレスに合わせて、switch文を使用してバンクを制御した。

  switch (io.data_bus.size) {
    is(MT_D) {
      io.data_bus.rddata := Cat(data_rd_data(7), data_rd_data(6), data_rd_data(5), data_rd_data(4),
        data_rd_data(3), data_rd_data(2), data_rd_data(1), data_rd_data(0)).asSInt
    }
    is(MT_W) {
      io.data_bus.rddata := Cat(Fill(32, 0.U(1.W)), data_rd_data(io.data_bus.addr(2) * 4.U + 3.U),
        data_rd_data(io.data_bus.addr(2) * 4.U + 2.U),
        data_rd_data(io.data_bus.addr(2) * 4.U + 1.U),
        data_rd_data(io.data_bus.addr(2) * 4.U + 0.U)).asSInt
    }
    is(MT_HU) {
      val target_data = Cat(data_rd_data(io.data_bus.addr(2,1) * 2.U + 1.U), data_rd_data(io.data_bus.addr(2,1) * 2.U + 0.U))
      io.data_bus.rddata := Cat(0.U(48.W), target_data).asSInt
    }
    is(MT_H ) {
      val target_data = Cat(data_rd_data(io.data_bus.addr(2,1) * 2.U + 1.U), data_rd_data(io.data_bus.addr(2,1) * 2.U + 0.U))
      io.data_bus.rddata := Cat(Fill(48, target_data(15)), target_data).asSInt
    }
    is(MT_BU) { io.data_bus.rddata := Cat(Fill(56, 0.U), data_rd_data(io.data_bus.addr(2, 0))).asSInt }
    is(MT_B ) {
      val target_data = data_rd_data(io.data_bus.addr(2,0))
      io.data_bus.rddata := Cat(Fill(56, target_data(7)), target_data).asSInt
    }
  }

テスト

とりあえずロード命令で所望のバンクからデータを読めるかテストしてみる。

    sd  x21,  0(x31)
    sd  x22,  8(x31)
    sd  x23, 16(x31)
    sd  x24, 24(x31)

    ld  x12,  0(x31)
    ld  x13,  8(x31)
    ld  x14, 16(x31)
    ld  x15, 24(x31)

    lw  x1,  0(x31)
    lw  x2,  4(x31)
    lw  x3,  8(x31)
    lw  x4, 12(x31)

    lh  x5,  0(x31)
    lh  x6,  2(x31)
    lh  x7,  4(x31)
    lh  x8,  6(x31)

    lhu x1,  0(x31)
    lhu x2,  2(x31)
    lhu x3,  4(x31)
    lhu x4,  6(x31)

    lb  x5,  0(x31)
    lb  x6,  1(x31)
    lb  x7,  2(x31)
    lb  x8,  3(x31)

    lbu x9,  0(x31)
    lbu x10, 1(x31)
    lbu x11, 2(x31)
    lbu x12, 3(x31)

正しく動作しているようだ。

        95 : x12<=0xb3fefd77eb062f44 [00002000]=>0xb3fefd77eb062f44 : 0x00000174 : INST(0x000fb603) : ld      a2, 0(t6)
        96 : x13<=0x9b0c605e63aadd2b [00002008]=>0x9b0c605e63aadd2b : 0x00000178 : INST(0x008fb683) : ld      a3, 8(t6)
        97 : x14<=0x8cd5b53f23cedcc1 [00002010]=>0x8cd5b53f23cedcc1 : 0x0000017c : INST(0x010fb703) : ld      a4, 16(t6)
        98 : x15<=0x48a2b40b10a2099a [00002018]=>0x48a2b40b10a2099a : 0x00000180 : INST(0x018fb783) : ld      a5, 24(t6)
        99 : x01<=0x00000000eb062f44 [00002000]=>0x00000000eb062f44 : 0x00000184 : INST(0x000fa083) : lw      ra, 0(t6)
       100 : x02<=0x00000000b3fefd77 [00002004]=>0x00000000b3fefd77 : 0x00000188 : INST(0x004fa103) : lw      sp, 4(t6)
       101 : x03<=0x0000000063aadd2b [00002008]=>0x0000000063aadd2b : 0x0000018c : INST(0x008fa183) : lw      gp, 8(t6)
       102 : x04<=0x000000009b0c605e [0000200c]=>0x000000009b0c605e : 0x00000190 : INST(0x00cfa203) : lw      tp, 12(t6)
       103 : x05<=0x0000000000002f44 [00002000]=>0x0000000000002f44 : 0x00000194 : INST(0x000f9283) : lh      t0, 0(t6)
       104 : x06<=0xffffffffffffeb06 [00002002]=>0xffffffffffffeb06 : 0x00000198 : INST(0x002f9303) : lh      t1, 2(t6)
       105 : x07<=0xfffffffffffffd77 [00002004]=>0xfffffffffffffd77 : 0x0000019c : INST(0x004f9383) : lh      t2, 4(t6)
       106 : x08<=0xffffffffffffb3fe [00002006]=>0xffffffffffffb3fe : 0x000001a0 : INST(0x006f9403) : lh      s0, 6(t6)
       107 : x01<=0x0000000000002f44 [00002000]=>0x0000000000002f44 : 0x000001a4 : INST(0x000fd083) : lhu     ra, 0(t6)
       108 : x02<=0x000000000000eb06 [00002002]=>0x000000000000eb06 : 0x000001a8 : INST(0x002fd103) : lhu     sp, 2(t6)
       109 : x03<=0x000000000000fd77 [00002004]=>0x000000000000fd77 : 0x000001ac : INST(0x004fd183) : lhu     gp, 4(t6)
       110 : x04<=0x000000000000b3fe [00002006]=>0x000000000000b3fe : 0x000001b0 : INST(0x006fd203) : lhu     tp, 6(t6)
       111 : x05<=0x0000000000000044 [00002000]=>0x0000000000000044 : 0x000001b4 : INST(0x000f8283) : lb      t0, 0(t6)
       112 : x06<=0x000000000000002f [00002001]=>0x000000000000002f : 0x000001b8 : INST(0x001f8303) : lb      t1, 1(t6)
       113 : x07<=0x0000000000000006 [00002002]=>0x0000000000000006 : 0x000001bc : INST(0x002f8383) : lb      t2, 2(t6)
       114 : x08<=0xffffffffffffffeb [00002003]=>0xffffffffffffffeb : 0x000001c0 : INST(0x003f8403) : lb      s0, 3(t6)
       115 : x09<=0x0000000000000044 [00002000]=>0x0000000000000044 : 0x000001c4 : INST(0x000fc483) : lbu     s1, 0(t6)
       116 : x10<=0x000000000000002f [00002001]=>0x000000000000002f : 0x000001c8 : INST(0x001fc503) : lbu     a0, 1(t6)
       117 : x11<=0x0000000000000006 [00002002]=>0x0000000000000006 : 0x000001cc : INST(0x002fc583) : lbu     a1, 2(t6)
       118 : x12<=0x00000000000000eb [00002003]=>0x00000000000000eb : 0x000001d0 : INST(0x003fc603) : lbu     a2, 3(t6)