SAP32アーキテクチャ

Simple Architecture Processor for educational

CPUの実装がまだよくわからない人向けの、シンプルな32bitプロセッサアーキテクチャです。とあるセミナーで使った実装を元に、某研究室のプロセッサ実装入門のために作りました。

コンパイラやアセンブラはありません。

SAP32基本仕様

オペランド3オペランド
エンディアンリトルエンディアン
レジスタ数16(汎用レジスタ*13, スタックポインタ*1, プログラムカウンタ*1, flagレジスタ), (IOレジスタ)
メモリアクセスOnly Load/Store, Byte, Half Word, Word
分岐Flag & Condition Code方式
IOMMIO, IOレジスタ(1ワードのIOアドレス空間と考える/これはデバッグ用)

レジスタ

レジスタマップ

addrregister nameDescription
0r0汎用レジスタ0
1r1汎用レジスタ1
2r2汎用レジスタ2
3r3汎用レジスタ3
4r4汎用レジスタ4
5r5汎用レジスタ5
6r6汎用レジスタ6
7r7汎用レジスタ7
8r8汎用レジスタ8
9r9汎用レジスタ9
10r10汎用レジスタ10
11r11汎用レジスタ11
12r12汎用レジスタ12
13flagrフラグレジスタ
14sprスタックレジスタ
15pcrプログラムカウンタ

汎用レジスタ

31:0
r?

FLAGR

ビットフィールド43210
名称SPOFCFZF
機能演算結果が負数の場合1演算結果がオーバーフローした際1演算結果に桁上げが生じた場合1演算結果が0ならば1

分岐

無条件分岐

b <label>, #al

条件分岐

以下のコンディションコードが適用される。

b <label>, #eq

CMP命令を使用した際のフラグは、r0-r1のフラグがCCと比較されます。

//   R0   >=  1
cmp  r0,      1
b    <hoge>,  #ge
LevelCodeOperate条件CC1CC2CC3(GCC)フラグ条件
0x0AlwaysAlwaysAL  Always
0x1Equal==EQZ EQZF
0x2Not Equal!=NEQNZNE!ZF
0x3Minus- MI SF
0x4Plus+ PL !SF
0x5
0x6
0x7Overflow  OVF OF
0x8Unsigned >=(Cary Set)>=UEOCGEUCF
0x9Unsigned <(Not Cary Set)<UUNCLTU!CF
0xAUnsigned >>UO GTUCF and !ZF
0xBUnsigned ⇐UEU LEU!CF or ZF
0xCSigned >=>=SEO GE(SF and OF) or (!SF and !OF)
0xDSigned <<SU LT(SF and !OF) or (!SF and OF)
0xESigned >>SO GT!((SF xor OF) or ZF)
0xFSigned ⇐SEU LE(SF xor OF) or ZF

ISA

インストラクションフォーマット

C Format
31:2423:0
OpCodeReserve
O3 Format

3 Operand Format

31:2423:1211:87:43:0
OpCodeReserveRs1Rs0Rd
O2 Format

2 Operand Format

31:2423:1211:87:43:0
OpCodeReserveReservedRs0Rd
O1 Format

1 Operand Format

31:2423:1211:87:43:0
OpCodeReserveReservedReservedRd
I16 Format

Immediate 16 Format

31:2423:87:43:0
OpCodeImm16Rs0Rd
JO2 Format

2 Operand for Jump Format

31:2423:2019:87:43:0
OpCodeCCReservedRs0Rd
JI16 Format

Immediate 16 for Jump for Jump

31:2423:2019:1615:0
OpCodeCCReservedImm16

ニーモニック

Levelnumberopcodeformatoperationimmediate
0nopcno operation
1wl16i16rd = {rd[31:16], imm16}unsigned
2wh16i16rd = {imm16, rd[15:0]}unsigned
3lili16rd = SignExtend(imm16)signed
4lihi16rd = imm16 « 16signed
5ulili16rd = imm16unsigned
6clro1rd = 0
—-7—-—-—-—-
8moveo2rd = rs0
9noto2rd = not(rs0)
—-10—-—-—-—-
—-11—-—-—-—-
12ando3rd = rs0 and rs1
13oro3rd = rs0 or rs1
14xoro3rd = rs0 xor rs1
—-15—-—-—-
16testo3rd = flag(rs0 & rs1)
—-17—-—-—-—-
—-18—-—-—-—-
—-19—-—-—-—-
20addo3rd = rs0 + rs1
21addii16rd = rs0 + imm16signed
22uaddii16rd = rs0 + imm16unsigned
23subo3rd = rs0 - rs1
24subii16rd = rs0 - imm16signed
25usubii16rd = rs0 - imm16unsigned
26mullo3rd = 0xffffffff & (rs0 * rs1)
27mulho3rd = (rs0 * rs1) » 32
—-28—-—-—-—-
—-29—-—-—-—-
—-30—-—-—-—-
31shli16rd = rs0 « (imm16 & 0x1f)—-
32shri16rd = rs0 » (imm16 & 0x1f)—-
33sari16rd = rs0 »> (imm16 & 0x1f)—-
—-34—-—-—-—-
35cmpo3flag = only_flag(rs0 - rs1)
—-36—-—-—-—-
—-37—-—-—-—-
—-38—-—-—-—-
—-39—-—-—-—-
40bjo2pcr = rs0
41biji16pcr = imm16signed
42brjo2pcr = pcr + rs0
43briji16pcr = pcr + imm16signed
—-44—-—-—-—-
—-45—-—-—-—-
—-46—-—-—-—-
—-47—-—-—-—-
48dino2rd = ior
49douto2ior = rs0
—-50—-—-—-—-
—-51—-—-—-—-
—-52—-—-—-—-
—-53—-—-—-—-
—-54—-—-—-—-
—-55—-—-—-—-
56ld8o2rd = 0xff & mem[rs0]
57ld16o2rd = 0xffff & mem[rs0]
58ld32o2rd = 0xffffffff & mem[rs0]
59st8o2mem[rd] = {mem[rd], mem[rs0]}
60st16o2mem[rd] = {mem[rd], mem[rs0]}
61st32o2mem[rd] = mem[rs0]
62popo1rd = mem[spr] / spr = spr + 4
63pusho1mem[spr] = rs0 / spr = spr - 4
64~127

アセンブリフォーマット

分岐

opcode label, cc

b <label>, #al
b 1024, #al

1-Operand

opcode r?

push r0

2-Operand

opcode r?, r?

//r0 <= r1
move r0, r1

3-Operand

opcode r?, r?, r?

//r0 <= r1 + r2
add r0, r1, r2

I16

opcode r?, imm16

or

opcode r?, r?, imm16

//r0 <= {r0[31:16], 1024}
wl16 r0, 1024
//r0 <= r1 << 2
shr r0, r1, 2

実装

実装例のブロック図

FIXME

赤 : 命令

青 : データ

演習手順

まずはパイプライン化なしで◎の命令及びコンディションコードを実装します。その後演習2で○、演習3で△を実装します。最終演習でパイプライン化をします。

各章ごとに正しく動作することを確認する為に、シミュレーションを行う必要があります。

演習1

最低限の実装です。まずはこれをパイプライン無しで実装してください。今後の演習で命令も追加されます。そして、パイプライン化も行います。その見通しを持ってHDLのモジュール分を行って実装してください。

実装したハードウェアが正しく実行できるかどうかをシミュレータで検証してください。

次に、実際に論理合成してFPGAで動作確認をします。検証方法としては、プロセッサのIO(IOレジスタ)にLEDやスイッチを接続して、プログラムでそれを用いて簡単なアプリケーションを作ってみてください。

実装のアドバイス

大まかなモジュール分割としては以下のようになります。

  • Fetch
    • 命令の読み出し。命令メモリから命令を読み出します。
  • Decode
    • 命令をから、内部で使用する信号に変換します。
  • Allocate
    • 使用するレジスタを割り当てます。
  • Execute
    • 命令を実行します(Load/Storeの場合はアドレス計算)。
  • Memory Access
    • Load/Storeの実行をします。
  • Writeback
    • レジスタに値を書き戻します。

このモジュール分割は、パイプライン化を行うときにも意味を持つので重要です。

今はパイプライン化を行わないので、これらを1サイクルで実行するように構成してください。

検証のアドバイス(シミュレーション)

一般的に、プロセッサの検証は、命令レベル・ファンクションレベル・システムレベル検証の3つのレベルで行います。

  • 命令レベル検証
    • 各命令が個別に正しく実行されることを検証します。これは命令ごとにテストベクタ(検証のためのプログラム)を作り、期待値と実際にRTLで演算された値を比較します(通常Assertionも用いて組み合わせて検証を行います)。
    • 検証するテストプログラムが最低限動く必要があります。よって一般的にload, store, cmp, add, branch, imm_set命令が正しく動かない限りこの懸賞は成り立ちません。
      • これらの命令は何かしらの方法で、予め正しく動くことを保証しておく必要がある。
    • 通常この検証は自動化します。
    • より深く検証するには、全命令でカバレッジを取り、最終的にマージして、カバレッジ値が100%に近づくように努力します。
      • 不要なハードウェアを見つけることができる。不要なハードウェアはバグの元
      • 但しISIMとかVIVADO Simulatorとかではこれはできません。
  • ファンクションレベル検証
    • 命令の組み合わせや、割り込み、命令の実行権限などが正しいかどうか検証します。これも期待値と実際の値とで比較することで実現します。
      • SAP32では割り込み・実行権限は未定義なのでテスト不要
      • 命令の組み合わせはforwardingなどを実装した時に検証が必要。
  • システムレベル検証
    • 具体的なワークロードやOSが動作するかどうか検証。
    • SAP32では、何かしらの意味のある簡単なプログラムを書いて検証してみるべき。

これらの検証に関してはMIST32アーキテクチャ向けの検証ツール(テストベクタ生成)及び、検証環境を参考にしてください。

検証ツール : mist32-processor-verification-tool

検証環境 : mist1032isa/sim/

検証のアドバイス(実機)

命令メモリはとりあえずHDL内でROM記述で書きます。そうすることで簡単なアセンブラもどきもできます。

データメモリはFPGAのRAMを用います。この時、まだパイプライン化及びインターロックを実現していないので、レイテンシ0でデータが読めるようにします。

演習2

○の命令を実装して、演習1と同様の検証を行ってください。

演習3

△の命令を実装して、演習1と同様の検証を行ってください。

演習4

パイプライン化をします。演習1の実装のアドバイスで述べたモジュールごとにパイプラインレジスタの追加を行います。

パイプライン化するにあたり、インターロックを追加する必要があります。例えば、メモリアクセス等、通常の演算より実行に時間がかかったりする命令があった場合、それに続く命令が追い越して実行されたりしないようにするためです。

これらを実装し、命令が正しく実行されることを検証してください。

実装のアドバイス

インターロックの実装としては、ある待ちが発生したステージより下のステージをロックします。もしインターロックの要因が解除されたら下のモジュールのロックも解除します。

検証のアドバイス

命令レベル及びファンクションレベルの検証が重要です。特に、ファンクションレベル検証でLoad/Storeに関する検証を重点的に行なってください。

発展課題(演習5)

演習4までの実装では多くのストールが発生してまともに使えません。最も、このプロセッサアーキテクチャはあくまで教育用なわけですが。最近の比較的高性能なプロセッサは以下の仕組みを実装しているので、勉強のために実現してみるのも良いと思います。

5-1フォワーディングを実現してください。

5-2キャッシュを追加してください。

5-3アウトオブオーダ実行(load/store以外)を実現してください。

5-4アウトオブオーダ実行(load/storeを含む)を実現してください。