Skip to main content
Open Design Computer Project
FPGAでコンピュータを作ってみた
伊藤 剛浩 川田 裕貴
http://open-arch.org/
伊藤 剛浩 @cpu_labs
筑波大学 情報学群 情報科学類
システム情報工学研究科 産学間連携推進室
Open Design Computer Project : ハードウェア担当
2011年度IPA認定スーパークリエータ
Verilog HDL SVA 回路設計 基板アートワーク等
川田 裕貴 @hktechno
筑波大学 情報学群 情報科学類
システム情報工学研究科 産学間連携推進室
Open Design Computer Project : ソフトウェア担当
2011年度IPA認定スーパークリエータ
gcc, Binutils の MIST32 向け移植
Open Design Computer Project
http://open-arch.org/
Open Design Computer
• Open Processor Architecture
– MIST32
• Open Source Processor
– MIST1032ISA
– MIST1032SA
• Open Development Environment
– Binutils
– gcc
Agenda
FPGAと回路設計
Out of Orderコアの実装
開発環境について
FPGAとは
• Field Programmable Gate Array
• つまり書き換えることができるハードウェア
• 最近安くなってきた
• もはや一家に一台レベル
– 買いましょう
– http://www.terasic.com.tw/
全ては並列動作のために
• 回路は並列動作させなきゃ意味が無い!
– 逐次だけやりたいんだったらプロセッサを使え
– スループットを常に念頭に
• プログラミングではない!!!
– 全て並列動作
– 結構大変
– プログラミングより地味
– 配線、配線、敗戦…
HDL : Verilog HDL
module addr(
input [31:0] a,
input [31:0] b,
iniput ci,
output [31:0] x,
output co);
assign {co, x} = a + b + ci;
endmodule
Synthesize / Place&Rout
• Synthesize : 論理合成
– HDLから回路図レベルまで落とす
– 要らないロジックを消す
– 早くするためにロジックを追加
• Place&Rout : 配置配線
– FPGA上のどこに配置するか
– 位置によってfmaxが変わる
気をつけること
• 非同期回路はできるだけ作らない
– 非同期回路は難易度up
– Synopsys Design Constraints(.sdc)でクロックドメイ
ンの定義
• スループットを稼ごう
– FPGAのIOは遅い
– レイテンシではなくスループットを重視する
• マクロブロックを積極的に利用
– Memory, FIFO系は使ったほうが良い
FPGAに関するまとめ
• HDLレベルでの記述でok
– 回路図レベルとか大規模開発出来ません
• 回路で作るなら並列に動かすことを心がける
– プログラミング的感覚は論外!
• IOは遅い
– スループットをどれだけ稼ぐかがキーポイント!
• あまり難しいことを考えない
– FPGAなんだから何度も書き換えできるし
Agenda
FPGAと回路設計
Out of Orderコアの実装とデバッグ
開発環境について
Out of Order Execution
• 実行ハードウェアを複数持つことが前提
• メモリからはプログラム順に入る(In Order)
• 実行の順序は動的に変化(Out Of Order)
– WAW/WARを排除
– 必要なデータさえ揃えば実行
• 実行の完了Commit処理にて(In Order)
– 割り込み対応のため
Atom : Bonnellのパイプライン
• In Order Core
• Decode
• Queue
• Dispatch
• Execute
• Writeback
Atom : Silvermontのパイプライン
• Out of Order Core
• Decode
• Queue
• Scheduler(RN/ROB/RS)
• Execute
• Writeback(CDB)
Out Of Order実行
• 今回はTomasuloのアルゴリズム
– Rename / Reservation Station / Re Order Buffer
– Frontend / Backendで回路を分離 -> 結構重要!
Register Renaming
• レジスタファイルの拡張
• ARF(LRF) : Architecture Register File
– Architectureで定義されているレジスタセット
• PRF : Physical Register File
– 物理的にプロセッサが持つレジスタファイル
• Renaming Table
– ARFとPRFの対応付け
Register Renaming実装
ARF Id PRF Id
R0 p-R12
R1 p-R3
Rn p-Rn
Instruction
Source
Next Stage
SourceDestination Destination
• Source Operand
– Renaming Tableを元にARF-PRF変換
• Destination Operand
– Renaming TableのARFに対する新たなPRFの割り当て
Frontend -> Reservation Station
• Out of Order実行されたデータを新しく入って
きた命令に適用
– 命令はデータを必要としていない
• そのままRSに格納
– 命令はデータを必要としている
• CDBから必要とするデータが流れてきた
• PRFに必要なデータが格納されている
Reservation Station -> Ex
Rs ID 演算タイプ Source1
Valid
Source2
Valid
Source1 Source2
0 2項演算 1 0 10 Invalid-r0
1 単項演算 1 1 5 5
n 2項演算 0 0 Invalid-r0 Invalid-r2
[実行不可] : Register0を待ち受けている
[実行可能] : 全てのデータは揃っている
[実行不可] : Register0, 1を待ち受けている
• 命令の実行に必要なデータが揃うまで待つ
– 常にExecuteのCDBを監視
• 必要データが揃ったら実行ステージへ送る
Reservation Station : 実装
Rs ID Source1
Valid
Source2
Valid
Source1 Source2
0 1 0 10 Invalid-r0
1 1 1 5 10
n 0 0 Invalid-r0 Invalid-r2
Priority
Encoder
Register
File
Data
p-R0 20
p-R1 0
p-Rn 10
CDB
From
Backend
Re Order Buffer
• つじつま合わせ
– Robがないと割り込み対応が面倒
• 命令が読まれた順を保持(R-Pointer)
• 実行し終わった命令にフラグ付け(実行完了
フラグ)
• 以下すべての命令が実行し終わったところを
指すポインタ(C-Pointer)
Re Order Bufferの実装
OoOの問題点
• とにかくデカイ
– 連想メモリの塊
– ブロードキャストとかしてる
– FPGAになんか入れられなry
• 超えられない壁
– ILP / Power / Memory Wall
MIST32でのアプローチ : CDB/Rs/Rob/Rn
• CDBの幅を32bitに固定 基本Flagも流さない
– 多倍長演算は以下により実現
命令レベル並列化のまとめ
• 命令を実行するにはデータが揃えば良い
• ハザードを回避するために実行順序を変える
• つじつま合わせに苦労する
• 連想メモリがー
• FPGAへの実装には向かない!!!
Agenda
FPGAと回路設計
Out of Orderコアの実装とデバッグ
開発環境について
MIST32の開発環境
とりあえずCで何か開発できる環境
• gcc(コンパイラ)
– C++はまだコンパイルできません
– newlibの移植が完全に終わってない
– 標準ライブラリなし
• binutils(as, objdump)
– 超重要
– Llvmは暇な時にやりたい
gccとbinutilsの移植
• 少しだけ移植方法を書き溜めてます
– まだまだ足りないので、暇な時に絶賛追記中
– http://open-arch.org/software/assembler
– http://open-arch.org/software/compiler
• binutils
– cgenを使うと楽
• gcc
– RTLからアセンブラへのパターンを書く(.md)
– Prologue, epilogueは既存アーキをとにかく参考にする
(define_expand "movqi"
[(set (match_operand:QI 0 "nonimmediate_operand" "")
(match_operand:QI 1 "general_operand" ""))]
""
"
{
if (MEM_P (operands[0]))
operands[1] = force_reg (QImode, operands[1]);
}")
(define_insn "addsi3"
[(set (match_operand:SI 0 "register_operand" "=r,r")
(plus:SI (match_operand:SI 1 "register_operand" "%0,0")
(match_operand:SI 2 "nonmemory_operand" "r,I")))]
""
"@
add¥t%0, %2
add¥t%0, %2"
)
バグ戦争…
ソフトウェアの問題なのか…
ハードウェアの問題なのか…
どっちかわからないwww
ウェーイ(☝ ՞ਊ ՞)☝
という問題が起きるわけです
デバッグ環境
• printfデバッグ
– I/Oの初期化が完了し、そこまで動けば
• ソフトウェアシミュレーター
– オレオレシミュレータ
– ハードウェア要因のデバッグは不可能(比較は可能)
• RTLシミュレーション
– とっても時間が書かる
• 勘と目grep
– 非常に重要。あきらめない。
– 最後の砦
00000470 <genrand_int31>:
470: 11 00 03 c0 push rbase
474: 11 00 03 e0 push rret
478: 18 00 03 c0 srspr rbase
47c: 0e e0 03 a0 lih rtmp,0x0
480: 0d 40 47 a0 wl16 rtmp,0x220
484: 20 70 03 e2 movepc rret,8
488: 14 40 03 a0 b rtmp,#al
48c: 08 30 00 01 shr r0,0x1
490: 12 00 03 e0 pop rret
494: 12 00 03 c0 pop rbase
498: 14 40 03 e0 b rret,#al
00000470 <genrand_int31>:
470: 11 00 03 c0 push rbase
474: 11 00 03 e0 push rret
478: 18 00 03 c0 srspr rbase
47c: 0e e0 03 a0 lih rtmp,0x0
480: 0d 40 47 a0 wl16 rtmp,0x220
484: 20 70 03 e2 movepc rret,8
488: 14 40 03 a0 b rtmp,#al
48c: 08 30 00 01 shr r0,0x1
490: 12 00 03 e0 pop rret
494: 12 00 03 c0 pop rbase
498: 14 40 03 e0 b rret,#al
00000470 <genrand_int31>:
470: 11 00 03 c0 push rbase
474: 11 00 03 e0 push rret
478: 18 00 03 c0 srspr rbase
47c: 0e e0 03 a0 lih rtmp,0x0
480: 0d 40 47 a0 wl16 rtmp,0x220
484: 20 70 03 e2 movepc rret,8
488: 14 40 03 a0 b rtmp,#al
48c: 08 30 00 01 shr r0,0x1
490: 12 00 03 e0 pop rret
494: 12 00 03 c0 pop rbase
498: 14 40 03 e0 b rret,#al
ソフトウェアシミュレータ
ELFを読み込んで、配置して、実行
エミュレータといったほうが正しいかも
https://github.com/techno/mist32-simulator
• 対応してるI/Oデバイス
– タイマ
– シリアル
– VGAディスプレイ
– PS/2キーボード
– SDカード(MMC)
シミュレータの中
• 命令はソフトウェアシミュレーション
– X86の命令に変換しているわけではない
– 完全に変換するのは余計に大変
– 速度を求めていない…
• 内部的な仮想メモリ
– ページ単位に区切ってアクセスがあったら割り当てる
• Memory Mapped I/O
– I/O領域にLoad/Storeが掛かったら専用命令にフック
する
– MIST32はあるアドレス以上はすべてI/Oなので、振り
分けがかなり楽
命令のエミュレーション
• めんどくさいこと
• フラグ
– Carryフラグはどうやって生成するの
– Carry = msb((dest & scr) | (~result & (dest | src)))
– Overflow = msb(~(dest ^ src) & (dest ^ result))
• エンディアン
– MIST32はビッグエンディアン
– バイトアクセスはエンディアン変換しただけではうまく
いかない
エンディアン変換の落とし穴
• Big Endian: 00 11 22 33
• Little Endian: 33 22 11 00
•
00 11 22 33
• ^^ == 0
•
33 22 11 00
• ^^ == ??
I/Oのエミュレーション
• ディスプレイ
– ノートPCでエミュレーションさせたくない
– 描画と処理を同時にやると思い
– ネットワーク経由で表示クライアントを接続可能
– msgpackとかおしゃれなものを使ってる
– クライアントはGTK+(cairo)で描画
• シリアル(UART)
– MkfifoしたFIFOに吐かせる
– catすれば通信が見える
– とりあえずok
セキュリティ
• あんまり考えてなかったり…
• PC, SPがちょっと辺
– 専用命令でしか操作できない
• Return pointerがレジスタ
• MMUでNX bitなどはサポート
• カーネル専用のページテーブルを持つ
– 有効活用できるかどうか怪しい
何かいいアイデアがあれば
Pull requestお待ちしています