ソフトウェア設計者が論理回路設計者になるためのワンポイント

ソフトウェア設計から集積回路(IC)の論理回路設計に移るときのちょっとしたポイントをまとめてみます.自分がC#で書いてばかりいるので,オブジェクト指向っぽいけど何か違うな書き方になってしまうかとは思いますが.
ポイントは3つ:

さて,細かく書いてみます.

コードに書いたすべての関数は,インスタンスです


まず普通に書いてきたプログラムが,どんな回路で動いていたかを図にしてみます.回路構成要素は,プロセッサとメモリです.メモリは実行する(機械語の)コードと処理結果であるデータを収めています.プロセッサはメモリから読み込んだコードとデータ,そして処理途中結果を収めるレジスタの値を演算して,演算結果を書き戻します.
プロセッサが実行できる演算は加算などの四則演算と条件分岐くらいの単純なものですが,コードでそれらの結果を組み合わせることでどんな複雑な処理もできるようになっています.
では,プログラムの考え方から回路設計の考え方に頭を切り替えてみましょう.例えば0から100までの全ての整数の加算値を求めよ,というのを回路化してみます.等差数列の和の公式を使えば一発なのですが,ここは上記のコードのように,1つ1つを加算して求めてみます.

プロセッサの構成をそのまま頭において回路にすると,こうなります.2入力加算器とレジスタを組み合わせて,i = i +1; を実行します.クロック毎に加算を繰り返して100クロック後に処理結果を取り出せばよいわけです.
プロセッサの構成を真似て作ったわけですが,よくよく見ると, i = i + 1; および a = a + i;という2つのwikipedia:関数 (数学)が,加算器とレジスタという1つの回路要素に1対1対応しています.ここがプログラムと大きく違うポイントです.これらの回路要素は,電源が入ってから切れるまで,ずーっと先ほどの関数を実行し続けます.プロセッサのように,1つの回路がプログラム・コードにより様々な処理をこなす,なんてことがありません.つまり,全ての関数,任意関数のインスタンス,は回路要素に1対1対応してしまうことになります.

ここが分かると...

回路設計のポイントは,回路規模が小さく(安く作れる),動作速度が速く(クロックを上げられる),1クロックあたりの処理能力が高く(安いプロセスで性能確保できる),消費電力が小さい(電池が持つ),設計をすることです.
この関数が回路要素に1対1対応する,が理解できていれば論理回路設計は簡単です.まず,全ての処理を関数言語で考えます.次に,関数のインスタンスの数が最小になる構成を工夫(具体的には,関数を時間軸で使いまわす,インスタンスが最小になるような公式を見出すなど)すればOKです.
なんて,いきなり関数型で考えろといわれても理屈では分かっても実際にコードを書くとなると難しいものです.全てのインスタンスがFinalizeされることなく,しかも1度インスタンスしたものが,そのまま回路素子に1対1対応するのだと,覚えておけばよいです.

処理完了までのクロック数により,回路構成(インスタンスの数)がまったく違ってきます


関数と回路素子1対1対応するものですから,別にプロセッサみたいな構成にしなくても,関数を直列接続してもよいです.ですが,このままだと動作速度がとても遅くなります.それはこの関数の入力->出力の遅延時間が(加算回路)の100回路分だからです.回路では,常に時間に気を払うことになります.
回路設計では,単位時間あたりの処理能力 (スループット)と,1つの処理が完了する時間(レイテンシ)に注目します.
関数だけで処理を書くと,処理完了までにいくつの関数を通るかなんて気にはしないですが,回路設計では,関数を通る=入力->出力の遅延時間がある,なので,気にしないといけないです.
上記の回路の場合だと,処理完了までに100回路通るのは,計算式がそうなのですから,どうやっても避けられません.しかし,上図のように,処理途中結果をレジスタで保持してやれば,クロック毎に次の入力値を入力できます.これがパイプライン化という処理になります.

まとめ

プログラムから論理回路設計に頭を切り替えるポイントをまとめました.
回路設計のポイントは,回路規模が小さく(安く作れる),動作速度が速く(クロックを上げられる),1クロックあたりの処理能力が高く(安いプロセスで性能確保できる),消費電力が小さい(電池が持つ),設計をすることです.全ての処理を関数で表したときに,関数1つ1つが回路素子に対応する,また関数それぞれに入力→出力遅延時間がかかるの2点を押さえて,前述のポイントを満たす設計をするのがコツです.
処理を関数で表したときに,入力->出力までの関数の数がレイテンシを表します.関数をなるべく細かくパイプライン化したときに,全関数の最大遅延時間がスループットを決定します.
例として,0から100まで和を求める2つの極端な回路構成を出しました.使えるインスタンスの数(回路素子の数)により,回路構成には様々なバリエーションが生じます.次回は,そのバリエーションを,デザインパターンとしてタイプ別に分けて紹介できればと思います.

余談

このエントリー,実は最初の"関数"と最後の"関数"の意味が違うものだったりします.最初の"関数"はふつーのプログラムでのサブルーチンの意味で書いていたりしますが,最後の"関数"は本当に関数,入力値と出力値の1対1写像,を示しています.それなのに,計算結果をレジスタで受けていますとか,後者の関数に当てはまらない考え方を使っているところは,大混乱の元かもしれません.クロックという概念と,レジスタというものを関数で書き表す,をしていないので,これははダメダメかもしれないです.

さらに余談

最近はC言語で回路設計とかが流行っていますが(すでに廃れた?),こうして考えてみると,もともとプロセッサありきの言語で無理やりHW設計は無理があるなと思います.HW/SW混在設計ならば,少なくともHW設計には(初心者でも分かりやすい,C言語ライクな表現もできる)関数型言語を使うのが正解じゃないかなと思えてしまいます.