today 2019-03-28
access_time 3 mins
BFに対する理解とアーキテクチャについて
前回まででChiselを使った開発フローについて理解したが、実際になにか作ってみることにする。
今回はBrainf**kという言語(以後BFと略する)の処理系をChiselを使って実装してみようと思う。
お題の理由としては以下となる。
kamiyaowl/arty-chisel-brainfxxk - Github
今回の開発にあたって、以下ページを何度も読み返している。特に英語ができない私にとっては日本語情報は本当にありがたかった。改めて感謝を。
まずはBF言語について簡単に説明しておく。
BFプログラムと、現在実行している命令のポインタ、30000個の要素を持つuint8_tの配列、配列のどのデータを操作するかを示すポインタの4要素である。
以下の8種類のみ使用できる
+
現在参照しているデータをインクリメント-
現在参照しているデータをデクリメント>
データ参照ポインタをインクリメント<
データ参照ポインタをデクリメント,
外部入力を現在参照しているデータに上書き.
現在参照しているデータを外部出力[
現在参照しているデータが0なら、対応する]
に命令ポインタを移動]
対応する[
に命令ポインタを移動これを利用するとHello world!
は以下のようなプログラムになる
">+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>++++++++[<++++>-]<+.[-]++++++++++."
一見複雑そうに見えるがからくりはこうである。例えば最初のH
を出力する箇所を確認する。
>+++++++++[<++++++++>-]<.
>+++++++++
→ mem[1] += 9
[<++++++++>-]
→ mem[0] += 8
, mem[1] -= 1
(mem[1] > 0
の間繰り返し)<.
→ put(mem[0])
となっている。c言語に書き下すならこうだ
int mem[30000];
int ptr = 0;
ptr++;
mem[ptr] = 9; // ptr == 1
while(mem[ptr]) { // ptr == 1
ptr--;
mem[ptr] += 8; // ptr == 0
ptr++
}
ptr--;
put(mem[ptr]); // ptr == 0, mem[0] == 72('H')
上記の例にもあるようにかなり簡単に実装できるので、まずは理解を深めるためにも自分の得意な言語で処理系を実装することを推奨する。 最初からHDL実装をしようとすると、意外と理論から実装に落とすフェーズに勘違いがあったりして躓くことがしばしあるからだ。
参考までにc++で実装したので、以下に貼っておく。 brainfxxk.cpp - Gist
#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <stdint.h>
using namespace std;
int main() {
vector<uint8_t> mem(30000,0);
stack<int> branchStack;
string src;
cin >> src;
int ptr = 0;
int pc = 0;
for(pc = 0; pc < src.length() ; ++pc) {
switch(src[pc]) {
case '+':
mem[ptr]++;
break;
case '-':
mem[ptr]--;
break;
case '>':
ptr++;
break;
case '<':
ptr--;
break;
case ',':
cin >> mem[ptr];
break;
case '.':
cout << (char)mem[ptr];
break;
case '[':
if (mem[ptr]) {
branchStack.push(pc - 1);
} else {
for(int nest = 1 ; nest > 0 && pc < src.length() - 1 ; ++pc) {
switch(src[pc + 1]) {
case '[':
nest++;
break;
case ']':
nest--;
break;
}
}
}
break;
case ']':
pc = branchStack.top();
branchStack.pop();
break;
}
}
return 0;
}
ここまででBFの実装については理解できたと思うので、いよいよHDLに起こすことを検討する。 今回は手元にArty A7があったので、これにポーティングできることをとりあえず目標とする。
お風呂に入りながら考えたが、stdin/outがあるのが面倒なので途中にFIFOをもたせるようなインターフェースがいいだろうと考えた。
簡単に説明しておく
記事のきりが良いのでここで区切るが、次回からは実装が簡単だった順番に紹介しようと思う。実際はProcessorから作っていったが…。