4558D が 2024年12月25日23時59分15秒 に編集
初版
タイトルの変更
アナログ電圧で計算できるCPUを作ってみた (AnaProV1)
タグの変更
自作CPU
オペアンプ
アナログ回路
サンプルアンドホールド回路
AnaProcV1
メイン画像の変更
記事種類の変更
製作品
ライセンスの変更
(CC BY-NC-SA 4+) Creative Commons Attribution-NonCommercial-ShareAlike CC BY-NC-SA version 4.0 or later
本文の変更
# はじめに - 自作CPUについて 電子工作趣味の中で、多少マイナー気味ながらも根強い人気がある分野に「自作CPU」がある。これは、パソコンをはじめとする情報機器などに多く搭載されている中央処理装置=CPUを、電子部品屋で揃う電子部品で自作するジャンルである。「CPUの機能を実現すること」のみが共通目標となっているので、人によりアプローチは様々である。以下はその例だ。 - TD4: 元祖自作CPU。比較的入手性の高いロジックICで実現 [参考文献1] - NLP-16A: NANDゲートのみで実現した16bitコンピュータ [参考文献2] - ComProc: FPGA上に実装。C言語ベースで開発されていてLCD制御やDOSなども備える [参考文献3] - リレーコンピュータ: 機械式リレーを用いて実現 上記方式はアプローチの仕方に違いがあるものの、すべてデジタル回路に分類される方式、つまり、情報量の最小単位として0,1を用いている方式である。しかし、CPU全体で果たす機能やCPUの構成要素が何かを掘り下げていくと、必ずしもデジタル回路である必要性はないことに気が付いた。このような背景から、アナログ回路を用いたアナログ電圧を直接処理するCPU「AnaProcV1」を製作した。本記事では、その設計思想や実際に作成した回路を紹介させていただきたい。 # CPUにおけるデータ表現 ## 従来のCPUにおけるデータ表現 設計思想を説明する前に、まずは「CPUでアナログ値を扱う」ことが具体的にはどういうことかを説明する。先述の通り、CPUでのデータは基本的に、情報量の最小単位として0,1を用いて表現する。例えば普段我々になじみのある10進法の整数は、CPUでは以下のように表現される。 - 9 (10進) → 1001 (2進) - 5 (10進) → 0101 (2進) このような表現の形式をとるのは、従来のCPUでは電圧のH (高)/L(低)しか読み取れないからである。つまり、信号線1本あたり2種類の情報しか持たせられないので、このように何本か(4 bit ~ 64 bit)の信号線を束ねてデータ化することがしばしば行われる。 (筆者注:ではなぜ、そのような「不便な」デジタルがあえて使われているのかというと、電圧のH/Lのみでのデータ表現がデータの保存性やら演算の正確性やらを担保しやすい、というメリットが大きい。これ以上話すと、AnaProcV1の存在意義が怪しくなるのでこの辺りで…) ## データ表現をデジタル→アナログに変更 アナログを扱えるようになると、信号線1本の電圧がそのまま情報として扱えるようになるため、信号線の入出力電圧の範囲においては、連続した実数値をそのままやり取りすることができる。 ![デジタル→アナログでのデータ表現](https://camo.elchika.com/4e856db1f8a5ae5d0a1cb2fcbcf0fc2c8a852a39/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38623938623232322d616463342d343636332d616530342d6361303264303433383638322f61613164653732642d386437612d343666302d383931332d396534383238336636373964/) つまり、上図左側のように4ビットのデジタルで表現されるようなデータも、1本の信号線のみで表現することができる。しかも、4ビットでは符号なしで0~15までの16通りの数値しか表現できないところが、アナログだと信号線1本で出力電圧範囲内の実数すべてを(理論上)表現することができる。 なお、本記事ではデジタルにおける情報量の最小単位"bit"に代わり、アナログ信号線による情報量の最小単位を"**DoF (Degree of Freedom: 自由度)**"と呼称することとする。 ## データの表現をアナログにするメリット このように、データの表現をアナログにするとよいことが主に2点ある。 ### ①:無理数を直接的に処理できる 例えば、Pythonで$\sqrt 2$と$(\sqrt 2 )^2$を計算すると下図のようになる。$\sqrt 2$は無理数だが有限値で近似されているため、$(\sqrt 2 )^2$を計算しても2から少しずれる。これは、浮動小数点数の表現上、無理数を完全には表現できないためである。1DoFのアナログ信号であれば連続的な表現が可能であるため、このような問題は理論上起きえない。 ![従来CPUによる無理数の計算 ](https://camo.elchika.com/1dda5a13d6250be528527f4952cb69a5e23948b9/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38623938623232322d616463342d343636332d616530342d6361303264303433383638322f31376363616138382d663231392d343236652d613535612d376134636462636531636238/) ### ②:組み込みシステムの小規模化・最適化 いわゆる組み込み系のシステムの典型的な形態として、センサからの入力をCPUに取り込み、入力データをCPUを処理して出力のアクチュエータを制御する、というものがある。このようなシステムを実現する場合、下図上側のような、A/D変換とD/A変換を挟んだ実装がよくなされる。 ![組み込みシステムの最適化](https://camo.elchika.com/d72c4b6c086752bde566bf00b75899c75d0509ec/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38623938623232322d616463342d343636332d616530342d6361303264303433383638322f30313730346433662d376435312d346434342d383462382d343732366661656136333230/) このようなシステムは、厳密性が求められるような制御には適している一方、厳密性よりも高速性が求められるような用途、もしくは、低コストを最優先とする用途では、A/D変換やD/A変換を挟む分だけ速度面やコスト面が不利になりうる。このような場合、CPUでアナログ信号を直接処理することができれば、A/D変換・D/A変換を省略できる分、高速化や低コスト化を実現しやすくなる。 次章から、1DoFのアナログ信号を扱えるCPUをどのように実現していくかを説明する。 # 1DoFを扱うCPUの設計思想 AnaProcV1のアーキテクチャは、既存CPUの構成要素を1DoFのアナログ信号を処理できるように変更する方法で決定した。 ## 既存のCPUの機能と構成 視点やCPU構成により定義は様々と思うが、自作CPUにおいてはおおよそ以下のように定義できると考えた。 ++CPUとは、外部入力もしくは記憶装置から取り込まれた値を、プログラムによって規定された順序で保持また演算し、結果を外部に出力する装置++ 上記定義のCPUは、以下の5つの構成要素で実現される。 1. 入出力装置:外部からデータを入力し、外部へデータを出力する。 2. 記憶装置:データを受け取り保持し、必要時に他装置にデータを渡す。 3. 演算装置:複数のデータ同士を演算し、演算結果を記憶装置に渡す。 4. 命令処理装置:プログラムに書かれた命令を1個ずつ解読し、各装置の動作を決める。 5. プログラムカウンタ:プログラムを格納する(特別な)記憶装置。 この構成要素は、既存の自作CPUにおいても成立する。例えば、冒頭でも紹介した自作CPUの金字塔、TD4のアーキテクチャにおいてもこれら構成要素がそろうことで成立している。 ![TD4における各構成要素の対応](https://camo.elchika.com/a18267a25f1bc44b9ba0ba4862c7e5be58ea4f81/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38623938623232322d616463342d343636332d616530342d6361303264303433383638322f63313737343034352d363638352d343137372d626462662d336232363930613333346135/) # 構成要素のアナログ回路化 先述の構成要素の確認を踏まえ、1DoFのアナログ信号を処理できるCPUを実現できるように各構成要素を変更した。以下では、各構成要素の設計思想、および、回路構成を説明する。 ### 入出力装置 アナログ信号を入力・出力するので、下図のような装置で実現できる。 ![入出力装置](https://camo.elchika.com/46fccab15834ee786bad5dd9425a6780f9c51f4b/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38623938623232322d616463342d343636332d616530342d6361303264303433383638322f63373538376363642d376634392d343631622d393930382d643436306132386363643230/) まず、上側の出力装置については、後述の1DoFのデータ保持ができる記憶装置(後述)の出力を正確に出力できれば良いので、1DoFのレジスタ出力にバッファとスイッチがあればよい。ただし、これらの構成は、全体アーキテクチャを最適化する過程で記憶装置内に組み込むことに決定したため、出力のためのみの回路は実装していない。 次に入力装置については、1DoFのアナログ電圧を入力できればよいので、AnaProcV1では手動で簡単に入力できるスライド式の可変抵抗を用いた。可変抵抗のほかにも入力範囲に対応しているセンサの電圧出力なども入力することができる。 ### 記憶装置 記憶装置はサンプル・アンド・ホールド回路を使用した。この回路は、オペアンプ、アナログスイッチ、コンデンサを用いた回路で、下図のような回路で実現できる。この回路では、図左下の「データの受け取り」と右下の「データの保持・受け渡し」を実現できるので、先述で確認した記憶装置の機能を果たすことができる。 ![記憶装置 (サンプル・アンド・ホールド回路)](https://camo.elchika.com/1261a21991fdbc6ea16f5e2e8117fd604606c1e4/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38623938623232322d616463342d343636332d616530342d6361303264303433383638322f63613932643965662d396233382d346432622d613533352d376630373861323363376539/) 「データの受け取り」では、コンデンサ前段のスイッチがオンすることでコンデンサに電荷がチャージされ、入力電圧とコンデンサの端子間電圧が等しくなる。「データの保持・受け渡し」では、コンデンサ前段のスイッチがオフすることでコンデンサへの電荷チャージが止まり、受け取ったデータを保持することができる。また、保持の状態において出力端子前段のスイッチがオンすることで、保持したデータを出力することができる。 AnaProcV1の記憶装置は、保持・出力電圧の精度を最大限向上させるために、下図のような回路構成を採用した。 ![AnaProcV1の記憶装置](https://camo.elchika.com/a40d418a1f4bde33a6938f088be718224a3d6569/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38623938623232322d616463342d343636332d616530342d6361303264303433383638322f33383363363863632d363164622d343063342d613066352d633064636363313237633134/) ここでは詳細は省略するが、基板設計にも注意を払い最大限性能を出せるように工夫している。詳しくは[PCBGOGOのUV印刷で配線パターンを描いてみた](https://elchika.com/article/767282ea-d6f6-4126-9afc-b1bda46b8730/)を参照してほしい。また、これらの回路設計、および、基板設計については参考文献[4]を参考にして設計した。本記事末尾に記載するので興味のある方はご参照いただきたい。 ### 演算装置 演算装置にはオペアンプの基本回路である、加算回路・減算回路・比較回路をアナログスイッチで切り替える方式を採用した。また、2個ある入力のうち1個の前段にはR-2R DACを搭載し、加算・減算・比較と同時に定数除算をできるようにしている。つまり、以下の表のような計算が可能である。ただし、Nの範囲は $0 \le N \le 32$とする。 | 計算 | 計算式 | |:---|:---| | 加算 | $A+\frac{N}{32} \times B$ | | 減算 | $A-\frac{N}{32} \times B$ | | 除算 | $\frac{N}{32} \times B$ | | 比較 | $V_{DD} (A>B), 0 (A<B)$ | また、全体の回路と設計ポイントを下図に示す。 ![アナログALUの回路構成](https://camo.elchika.com/81afd7e31c11dbefc98f1dbd9b4814041b9ae520/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38623938623232322d616463342d343636332d616530342d6361303264303433383638322f65656661613635392d316438322d343831302d393430352d663765383961326633356330/) なおオペアンプは、Operational Amplifier の名称が示す通り様々なアナログ演算に対応できるオペアンプのため、回路構成を変更することで絶対値や対数などほかの計算を行うことも可能である。 ### その他構成要素 従来のCPUの構成要素のうち、以下の要素についてはプログラムの実行自体に関わる要素、つまり、CPU自体の動作の決定にかかわる部分である。 - 命令処理装置:プログラムに書かれた命令を1個ずつ解読し、各装置の動作を決める。 - プログラムカウンタ:プログラムを格納する(特別な)記憶装置。 これら要素をアナログにした場合、ちょっとしたノイズ等の影響で、プログラムを実行した結果が大きく変わる可能性がある(特にジャンプ命令)。AnaProcV1は最初の試作機であることも踏まえ、入出力装置、記憶装置、演算装置のアナログ化による一部データバスのみの1DoFアナログ化達成を目的に据え、命令処理系とプログラムメモリのアナログ化は見送った。 # AnaProcV1の仕様と実装 1 DoFのアナログ信号を扱えるようにするために必要な、CPUの構成要素それぞれを前章までで説明した。ここからは、制作したAnaProcV1について、アーキテクチャや命令体系を含め、より詳細に紹介していきたい。 ## ハードウェア構成 ### 仕様とレジスタ構成 AnaProcV1のハードウェア構成は以下表のとおりである。データバスは、1 DoFのアナログバスが1本と、8 bitのデジタルバスが1本あり、前者はメインのアナログ信号の処理に、後者はプログラムメモリの読み込みやジャンプ命令やループ処理の制御に用いる。また、表中の※はロジックIC等は用いず、PIC16F18877でエミュレートしている。 表 AnaProcV1の仕様とレジスタ構成 | 要素 | アナログ | デジタル | デジアナ混載 | |:---|:---:|:---:|:---:| | 電源 | +12V単電源, +5V単電源 | +5V単電源 | +5V単電源 | | クロック | なし※1 | なし※1 | なし※1 | | | データバス | 1 DoF (メイン) $\times 1$ | 8 bit (サブ) $\times 1$ | - | | レジスタ | 1 DoF アナログレジスタ $\times 3$ | 8 bit レジスタ $\times 2$ ※2| ADC※2, DAC | | 入力 | 1 DoF 入力レジスタ $\times 1$ | - | - | | 出力 | 1 DoF 出力レジスタ $\times 1$ | - | - | | ALU | 1 DoF, 2入力 $\times 1$ | 8 bit, 2入力 $\times 1$ ※2 | - | | | プログラムメモリ | - | 2 KB RAM※2, 256B ROM※2 | - | | メモリ構成 | - | ノイマン型 | - | ※1: デジタルブロックとアナログブロックで動作速度の差が大きすぎるため、一定のクロックに従って動作させるような仕組みにはしていない。先述のCPUの構成要素の説明でクロックに言及しなかったのはこのため。 ※2: PIC16F18877でソフト的にエミュレートしており、ハードウェア的な実装はしていない。 また、アナログとデジタルという、全く異なる性質の信号を処理する必要がある性質上、A/D変換とD/A変換も実装している。前者は比較演算を行った際、大小判定の結果を踏まえて分岐命令を処理するような場合に必要であり、後者はプログラムメモリからアナログメモリへリテラル値を代入する際に必要であるため実装した。 ## 全体アーキテクチャ 上記ハードウェア使用を実現するため、CPUアーキテクチャは下図のようにした。なお簡単のため、PIC16F18877でエミュレートしている構成要素の一部は省略している。 ![AnaProcV1の全体アーキテクチャ](https://camo.elchika.com/c9cadd4181524be799d2ca675bf2a20878cf4197/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38623938623232322d616463342d343636332d616530342d6361303264303433383638322f31343164346562362d336639392d346432302d623330382d363165646330313564363164/) AnaProcV1は大きくデジタル処理部とアナログ処理部に分かれており、前者を緑のブロックで、後者をオレンジのブロックで示している。デジタル処理部内のレジスタやALU(図では省略)は8 bitのデータバスで、アナログ処理部内のレジスタやALUは1 DoFのアナログデータバスで接続し、さらに、デジタル処理部とアナログ処理部の橋渡しとしてADCとDACを備える。図の赤枠で示す通り、デジタル処理部とADCはPIC16F18877を用いて、ソフト上でエミュレートしている。これにより、アナログ処理部の設計に注力することができ、ちゃんと動作するものに仕上げることができた。 ## 命令 次に、実装した命令を説明する。AnaProcV1で動作する命令は、アーキテクチャ図において命令デコーダが赤枠に入っていたことからも分かる通り、PIC16F18877でソフトウェア的に実現したものである。構想時からデモンストレーションを意図していたため、必要な機能を実装しつつもできるだけ少ない命令数となるように命令設計を行った。次節と次次節において、命令の分類の説明と、各命令の詳細を説明する。 ### 命令の分類 命令は計78個実装しており、以下7個の分類に分けられる。 1. CPU制御系:NOP, RESET, PAUSEなど 2. ジャンプ命令:JP, JPZ, JPNZ, JPC, JPNC 3. デジタルレジスタ操作・演算:LDD, ADDDなど 4. レジスタ-メモリ間転送:LDDRM, LDDMR 5. デジタルレジスタ入出力:IND, OUTD 6. アナログレジスタ操作・演算:LDA, ADDAなど 7. アナログレジスタ入出力:INA, OUTA 8. A/D変換、D/A変換: LDAD, LDDA できるだけ命令数を少なくすることを意図したため、例えばサブルーチンコールや割り込みなどは実装しておらず、ジャンプ命令で対処するようにしたり、デジタルレジスタのインクリメント・デクリメント命令を省略していたりする。このため可読性のよいコードを書きづらいが、概念実証とデモンストレーションを目的に対しては必要十分である。 ### 命令一覧 以下表に、AnaProcV1の命令一覧を示す。命令長はいずれも、1バイトもしくは2バイト命令である。AnaProcV1にはアセンブラを搭載しており、ニーモニックとオペランドを書き連ねたコードを読み込ませることで自動的にコードに変換しプログラムメモリに格納することができる。 | 分類 | ニーモニック | オペランド1 | オペランド2 || 命令長 | コード(HEX) | 動作 | |:---|:---:|:---:|:---:|:---:|:---|:---|:---| | CPU制御 | NOP | - | - || 1| 00 | 何もしない | | 〃 | RESET | - | - ||1| 01 | レジスタ & PCリセット | | 〃 | HALT | - | - || 1| 02| 実行停止 | | 〃 | RESETA | - | - || 1| 02| 全アナログレジスタのリセット | | 〃 | PAUSE | - | - || 1 | 04| 端末からの入力があるまで待機 | | | ジャンプ | JP | nn | - || 2 | 08 nn | nn番地にジャンプする | | 〃 | JPNZ | nn| - || 2| 09 nn| 直前の命令実行でZフラグ=クリアならnn番地にジャンプする | | 〃 | JPZ | nn| - || 2| 0A nn| 直前の命令実行でZフラグ=セットならnn番地にジャンプする | | 〃 | JPNC | nn| - || 2| 0B nn| 直前の命令実行でCフラグ=クリアならnn番地にジャンプする | | 〃 | JPC | nn| - || 2| 0C nn| 直前の命令実行でCフラグ=クリアならnn番地にジャンプする | | | デジタルレジスタ操作 | LDD | D| E || 1| 10 | DレジスタにEレジスタの値を格納する | | 〃 | LDD | D| nn|| 1| 11 nn | Dレジスタに値nnを格納する | | 〃 | LDD | E| D || 1| 12 | EレジスタにDレジスタの値を格納する | | 〃 | LDD | E| nn|| 1| 13 nn | Eレジスタに値nnを格納する | | | デジタルレジスタ演算 | ADDD | D | E || 1 | 20 | DレジスタにD+Eを格納する | | 〃 | ADDD | D | nn || 2 | 21 nn | DレジスタにD+nnを格納する | | 〃 | ADDD | E | D || 1 | 22 | EレジスタにD+Eを格納する | | 〃 | ADDD | E | nn || 2 | 23 nn | EレジスタにE+nnを格納する | | 〃 | SUBD | D | E || 1 | 24 | DレジスタにD-Eを格納する | | 〃 | SUBD | D | nn || 2 | 25 nn | DレジスタにD-nnを格納する | | 〃 | SUBD | E | D || 1 | 26 | EレジスタにE-Dを格納する | | 〃 | SUBD | E | nn || 2 | 27 nn | EレジスタにE-nnを格納する | | | レジスタ-メモリ間転送 | LDDRM | D | D || 1 | 28 | Dレジスタで指定したアドレスの中身をDレジスタに書き込む | | 〃 | LDDRM | E | E || 1 | 29 | Eレジスタで指定したアドレスの中身をEレジスタに書き込む | | 〃 | LDDRM | D | E || 1 | 2A | Eレジスタで指定したアドレスの中身をDレジスタに書き込む | | 〃 | LDDRM | E | D || 1 | 2B | Dレジスタで指定したアドレスの中身をEレジスタに書き込む | | 〃 | LDDRM | D | nn || 2 | 2C nn | nn番地の中身をDレジスタに書き込む | | 〃 | LDDRM | E | nn || 2 | 2D nn | nn番地の中身をEレジスタに書き込む | | 〃 | LDDMR | D | D || 1 | 2E | Dレジスタの値をDレジスタで指定したアドレスに書き込む | | 〃 | LDDMR | E | E || 1 | 2F | Eレジスタの値をEレジスタで指定したアドレスに書き込む | | 〃 | LDDMR | D | E || 1 | 30 | Eレジスタの値をDレジスタで指定したアドレスに書き込む | | 〃 | LDDMR | E | D || 1 | 31 | Dレジスタの値をEレジスタで指定したアドレスに書き込む | | 〃 | LDDMR | nn | D || 2 | 32 nn | Dレジスタの値をnn番地に書き込む | | 〃 | LDDMR | nn | E || 2 | 33 nn | Eレジスタの値をnn番地に書き込む | | |デジタルレジスタ入出力 | IND | D | - || 1 | 36 | Dレジスタに端末から取得した値を格納する | | 〃 | IND | E | - || 1 | 37 | Eレジスタに端末から取得した値を格納する | | 〃 | OUTD | D | nn || 2 | 38 nn | Dレジスタの値を端末にnn進数で表示する | | 〃 | OUTD | E | nn || 2 | 39 nn | Eレジスタの値を端末にnn進数で表示する | | | アナログレジスタ操作 | LDA | A | B || 1 | 40| AレジスタにBレジスタの電圧を格納する | | 〃 | LDA | A | W || 1 | 41| AレジスタにWレジスタの電圧を格納する | | 〃 | LDA | B | A || 1 | 42| BレジスタにAレジスタの電圧を格納する | | 〃 | LDA | B | W || 1 | 43| BレジスタにWレジスタの電圧を格納する | | 〃 | LDA | W | A || 1 | 44| WレジスタにAレジスタの電圧を格納する | | 〃 | LDA | W | B || 1 | 45| WレジスタにBレジスタの電圧を格納する | | | D/A変換 | LDDA | A | nn || 2 | 50 nn | nnをDACで電圧変換しAに格納する | | 〃 | LDDA | A | D || 1 | 51 | Dの値をDACで電圧変換しAに格納する | | 〃 | LDDA | A | E || 1 | 52 | Eの値をDACで電圧変換しAに格納する | | 〃 | LDDA | B | nn || 2 | 53 nn | nnをDACで電圧変換しBに格納する | | 〃 | LDDA | B | D || 1 | 54 | Dの値をDACで電圧変換しBに格納する | | 〃 | LDDA | B | E || 1 | 55 | Eの値をDACで電圧変換しBに格納する | | | A/D変換 | LDAD | D | A || 1 | 58 | Aの電圧をADCで数値に変換しDに格納する | | 〃 | LDAD | D | B || 1 | 59 | Bの電圧をADCで数値に変換しDに格納する | | 〃 | LDAD | D | W || 1 | 5A | Wの電圧をADCで数値に変換しDに格納する | | 〃 | LDAD | E | A || 1 | 5B | Aの電圧をADCで数値に変換しEに格納する | | 〃 | LDAD | E | B || 1 | 5C | Bの電圧をADCで数値に変換しEに格納する | | 〃 | LDAD | E | W || 1 | 5D | Wの電圧をADCで数値に変換しEに格納する | | | アナログレジスタ入出力 | INA | A | - || 1 | 60 | 入力端子から電圧を読み込みAレジスタに格納する | | 〃 | INA | B | - || 1 | 61 | 入力端子から電圧を読み込みAレジスタに格納する | | 〃 | OUTA | W | - || 1 | 62 | Wレジスタの電圧を出力端子に出力する | | 〃 | OUTA | I | - || 1 | 63 | Iレジスタ(入力レジスタ)の電圧を出力端子に出力する | | | アナログ演算命令 | ADDA | - | - || 1 | 64 | A+Bを計算しWレジスタに格納する | | 〃 | SUBA | - | - || 1 | 65 | A-Bを計算しWレジスタに格納する | | 〃 | CMPA | - | - || 1 | 67 | A>BならVDDを、A<Bなら0VをWレジスタに格納する | | 〃 | ADDA | nn | - || 2 | 68 nn | A+B×nn/32を計算しWレジスタに格納する | | 〃 | SUBA | nn | - || 2 | 69 nn | A-B×nn/32を計算しWレジスタに格納する | | 〃 | DIVA | nn | - || 12| 6A nn | B×nn/32を計算しWレジスタに格納する | | 〃 | CMPA | nn | - || 2 | 6B nn | A>B×nn/32ならVDDを、A<B×nn/32なら0VをWレジスタに格納する | | 〃 | ADDA | D | - || 1 | 6C | A+B×D/32を計算しWレジスタに格納する | | 〃 | SUBA | D | - || 1 | 6D | A-B×D/32を計算しWレジスタに格納する | | 〃 | DIVA | D | - || 1 | 6E | B×D/32を計算しWレジスタに格納する | | 〃 | CMPA | D | - || 1 | 6F | A>B×D/32ならVDDを、A<B×D/32なら0VをWレジスタに格納する | | 〃 | ADDA | E | - || 1 | 70 | A+B×E/32を計算しWレジスタに格納する | | 〃 | SUBA | E | - || 1 | 71 | A-B×E/32を計算しWレジスタに格納する | | 〃 | DIVA | E | - | |1 | 72 | B×E/32を計算しWレジスタに格納する | | 〃 | CMPA | E | - || 1 | 73 | A>B×E/32ならVDDを、A<B×E/32なら0VをWレジスタに格納する | | | 疑似命令 | END | - | - || - | - | プログラムを終了する | | 〃 | ORG | nn | - || - | - | 以降の命令をnn番地から開始する | | 〃 | DB | nn | - || - | - | 記載行に該当するアドレスに値nnを格納する | ## 外観 AnaProcV1は、先述の通りデモンストレーション用途を意図していたため、運搬と展示のしやすさからアタッシュケースに収めている。アタッシュケースの下側(ケース側)にCPU本体を、上側(ふた側)に各レジスタの電圧表示用インジケータ、および、入力装置として用いるスライド式可変抵抗を格納している。 ![全体像](https://camo.elchika.com/790a5a4e190609e3a13441cf0f18d231a110e932/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38623938623232322d616463342d343636332d616530342d6361303264303433383638322f31626331363831332d396232642d343061622d616666372d646133666130353234343366/) ![下側の様子 (CPU本体)](https://camo.elchika.com/0a35f9177a51efbd273b4fac6fa98b2ad41f2681/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38623938623232322d616463342d343636332d616530342d6361303264303433383638322f35386638316631312d303335372d343737392d623862352d366333343536353365386365/) ![上側の様子 (インジケータ・入力装置)](https://camo.elchika.com/7a10db221f1ec759f04336009ca3a592a6aecc1b/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38623938623232322d616463342d343636332d616530342d6361303264303433383638322f62353934383462372d393534302d343332342d616361662d376264663435333363366136/) ## 運用方式 運用方式は、以下の2通りが選択可能である。 1. スタンドアローン方式:電源だけを供給することで内蔵ROMに記録されたプログラムを繰り返し実行するモード 2. 端末接続方式:USB-シリアルを利用してPCと接続し、端末エミュレータ(TeraTerm)上で実行するモード 基本的には、デモンストレーション時には1を使用し、プログラムの作成・デバッグを行う時には2を使用する。また、INDやPAUSEのように、2のみで使用できるプログラムもあるため、原則2を使用する前提でUIを設計している。 ![AnaProcV1の運用方式](https://camo.elchika.com/745de462c7ab111497f1cea3b44e8f8b727f112b/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38623938623232322d616463342d343636332d616530342d6361303264303433383638322f33383934636230302d343233392d343234662d626530382d386662626563613336336431/) ## 端末エミュレータ上のUI AnaProcV1には、端末エミュレータでのシリアル接続で動作させる簡易的なコマンドラインインターフェースを搭載している。AnaProcV1を起動させると、まずスタンドアローン動作に入り、ROMに保存してあるプログラムを動作させる。この動作はエラー、もしくは、HALT命令でプログラムが停止するか、キーボード入力が入るまで続く。スタンドアローン動作から抜け出すと、以下のようなメニュー画面が現れ、メニュー選択が可能になる。このUIは、拙稿[Z80 CPUをPICマイコンで動かしてLチカしてみた](https://elchika.com/article/c4dee540-a1ad-4876-bff0-df620e168b67/)を応用したものである。 ![メニュー画面](https://camo.elchika.com/bd7543977566df1a84bd0336e04c0e73bc8504c0/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38623938623232322d616463342d343636332d616530342d6361303264303433383638322f32653262353763622d366531342d343730652d623334352d643135653366663662666631/) メニューは全部で9個あり、それぞれの機能は以下の通りである。これらメニューのうち、1,3,4,5,9は開発・デバッグ用に、2,6,7,8はデモンストレーション用に、筆者は使い分けている。 1. Interactive Shell Mode:対話シェルを起動し、ニーモニックを命令1個づつ入力し即時実行するモード。 2. Memory Operation Mode (Sequence):メモリに書き込まれたプログラムを連続的に自動実行するモード。 3. Memory Operation Mode (Step by Step):メモリに書き込まれたプログラムを命令1個づつ自動実行するモード。 4. Display Program in Virtual Memory:メモリの内容を表示する。(ダンプリストの表示) 5. Write Program by Binary Editor:バイナリエディタを使用して機械語でプログラムする。 6. Write Program by Mnemonic:読み込んだアセンブラを元に機械語のコードを生成してプログラムする。 7. Save Program to EEPROM:PIC16F18877内蔵のEEPROMにプログラムメモリの内容を保存する。(冒頭256Byteのみ) 8. Load Program from EEPROM:EEPROMの内容をプログラムメモリに読み込む。 9. Display Program in Virtual Memory (EEPROM用):EEPROMの内容を表示する。(ダンプリストの表示) ## 作成したアプリケーション AnaProcV1で、アナログ電圧を用いた演算ができることを示すために、以下のようなアプリケーションを作成した。 - フィボナッチ数の計算:出力端子に、入力電圧×フィボナッチ数の電圧を順次出力する。 - 整数値の加減乗除:入力電圧=1として整数値を電圧に変換してアナログレジスタに格納し、加減乗除した値に対応する電圧を出力端子に出力した。 - 波形生成:ノコギリ波、もしくは、サイン波を出力する。 - 円周率$\pi$、ネイピア数$e$の近似値:無限級数の一部を計算し、近似値を計算する。 これらは正常に動作し、AnaProcV1で"演算"ができることを示した。 以下では、それぞれのアプリケーションについて簡潔に説明する。 ### フィボナッチ数の計算 入力端子から電圧をAレジスタに入力後、A+Bの計算結果を、AレジスタとBレジスタに交互に格納しながら出力することで、フィボナッチ数を順番に算出している。以下にコードを示す。 ```C:Analog_Fibonacci.txt INA A ; Input A Register ADDA ; W = A+B OUTA W ; Output W Register LDA B W ; B = W PAUSE ; Pausing until Key Pushed ADDA ; W = A+B OUTA W ; Output W Register LDA A W ; A = W PAUSE ; Pausing until Key Pushed JP 0x01 ; Return to 2nd line END ``` 実行した様子は以下のポストの通りである。 @[x](https://x.com/electrotelecast/status/1854512503687983173) ### 整数値の加減乗除 D, Eレジスタを端末から入力して取り込み、入力端子の電圧INPUTを取り込み、以下を計算する。 - 加算:OUTPUT = (D × INPUT) + (E × INPUT) - 減算:OUTPUT = (D × INPUT) - (E × INPUT) - 乗算:OUTPUT = (D × INPUT) × (E × INPUT) - 除算:OUTPUT = (D × INPUT) ÷ (E × INPUT) コードを以下に示す。 - 加算 ```C:Add.txt RESETA IND D ; Input D: Taget Number INA B ; Set Areg as Unit Voltage ORG 0x10 ; Setting Areg Voltage to D ADDA ; LDA A W ; A=A+B SUBD D 001 JPNZ 0x10 ; When finished, A==D IND E ; Input E: Base Number INA B ; Set Areg as Unit Voltage ORG 0x20 LDD D E ; D=E ORG 0x22 ; Calculating W<-D-E ADDA ; LDA A W ; A=A-B SUBD E 001 ; E=E-1 JPNZ 0x22 LDA A W OUTA W ; Display Result PAUSE JP 0x00 END ``` - 減算 ```c:Subtract.txt RESETA IND D ; Input D: Taget Number INA B ; Set Areg as Unit Voltage ORG 0x10 ; Setting Areg Voltage to D ADDA ; LDA A W ; A=A+B SUBD D 001 JPNZ 0x10 ; When finished, A==D IND E ; Input E: Base Number INA B ; Set Areg as Unit Voltage ORG 0x20 LDD D E ; D=E ORG 0x22 ; Calculating W<-D-E SUBA ; LDA A W ; A=A-B SUBD E 001 ; E=E-1 JPNZ 0x22 LDA A W OUTA W ; Display Result PAUSE JP 0x00 END ``` - 乗算 ```c:Multiply.txt RESETA IND D ; Input D: Taget Number INA B ; Set Breg as Unit Voltage ORG 0x10 ; Setting Areg Voltage to D ADDA ; LDA A W ; A=A+B SUBD D 001 JPNZ 0x10 ; When finished, A==D IND E ; Input E: Base Number LDA B A ; A=B ORG 0x20 SUBD E 001 ; E=E-1 JPZ 0x30 ; E=0 -> Finish Calculation ADDA ; LDA A W ; A=A JP 0x20 ORG 0x30 ; Calculating W<-D-E LDA A W OUTA W ; Display Result PAUSE JP 0x00 END ``` - 除算 ```c:Divide.txt RESETA IND D ; Input D: Taget Number INA B ; Set Breg as Unit Voltage ORG 0x10 ; Setting Areg Voltage to D ADDA ; LDA A W ; A=A+B SUBD D 001 JPNZ 0x10 ; When finished, A==D ORG 0x20 IND E ; Input E: Base Number ORG 0x22 ; Calculate D=D-E SUBA LDA A W ; A=A-B SUBD E 001 ; D=D-1 JPNZ 0x22 ; CMPA ; A vs B LDAD E W ADDD E 0x7f JPNC 0x40 ; A<B: Finish ADDD D 001 JP 0x20 ORG 0x40 ; Calculating W<-D-E ADDD D 001 OUTD D 010 ORG 0x48 ; Setting Areg Voltage to D ADDA ; LDA A W ; A=A+B SUBD D 001 JPNZ 0x48 ; When finished, A==D OUTA W ; Output PAUSE JP 0x00 END ``` ### 波形生成 ノコギリ波生成とサイン波生成を実装した。これらは詳細な部分は異なるものの、おおよそ以下のようなフローで波形生成をしている。 1. Aレジスタをリセットする 2. Aレジスタに足す(or 引く)電圧を決定しBレジスタを格納する。 3. W=A+B (or A-B)を計算する。 4. WをAに格納する。 5. Aが一定の閾値を超える、もしくは、一定回数以上足し上げるまで2-4を繰り返す。これら条件を満たしたとき、1に戻る 以下、コードを示す。 - ノコギリ波 ```c:LED_bar_blowup2.txt RESETA LDDA B 025 ; Set Voltage for one scale. ADDA ; W=A+B OUTA W ; W -> Output LDA A W ; A=W INA B ; B=Input (Reference) CMPA ; A>B->W=VDD, A<B->W=0V LDAD D W ; D <- W ADDD D 0x7F ; D = D+VDD/2 JPNC 0x01 ; C flag Not Set? -> JP to 0x01 JP 0x00 ; return to RESET END ``` このコードを実行することで、以下の波形が得られる。 ![生成したノコギリ波](https://camo.elchika.com/b00a816c12c6dd4a2d833050da77b010c08482bc/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38623938623232322d616463342d343636332d616530342d6361303264303433383638322f37623266356561632d653837302d346437652d623931312d346337306661396665366339/) - サイン波 ```c:Wave2.txt RESETA ORG 0x01 LDD D 016 ; Initialize D=16 ORG 0x10 ; Address: 0x10 INA B ADDA D ; W=A+B*D/32 LDA A W ; A=W OUTA W ; Output W value. SUBD D 002 ; D=D-2 JPNZ 0x10 ; D>0->0x10, D=0:Next Address ORG 0x20 LDD D 000 ; Initialize D=0 ORG 0x30 ; Address: 0x30 LDD E 018 ; E=16+2 INA B SUBA D ; W=A-B*D/32 LDA A W ; A=W OUTA W ; Output W value ADDD D 002 ; D=D+2 SUBD E D ; E=E-D JPNZ 0x30 ; E>0->0x30, E=0:Next Address JP 0x00 ; return to Add 0x00 END ``` このコードを実行することで、以下の波形が得られる。 ![生成したサイン波](https://camo.elchika.com/706fa6f3e26a952d16725a3847b48a7106dd0769/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f38623938623232322d616463342d343636332d616530342d6361303264303433383638322f66393164626138312d316137342d343633382d626234342d636263336366393739366339/) ### 円周率$\pi$、ネイピア数$e$の近似値 AnaProcV1では、無理数の近似値計算も実装した。計算に用いたのは、以下の無限級数である。 - $e=\displaystyle\sum_0^\infty \frac{1}{n!}=1+\frac{1}{1!}+\frac{1}{2!}+\frac{1}{3!}+\cdots$ - $\frac{\pi}{4}=\displaystyle\sum_0^\infty\frac{(-1)^n}{2n+1}=1-\frac{1}{3}+\frac{1}{5}-\frac{1}{7}+\cdots$ 円周率について、実行した様子は以下のポストの通りである。 @[x](https://x.com/electrotelecast/status/1862150931271516403) # まとめと今後の展望 最後に、本記事で紹介した内容のまとめと今後の展望を記載する。 ==まとめ ・既存のCPUの機能から必要な構成要素を見直し、各構成要素をアナログ電圧を処理できるように再設計することで、1 DoF (自由度1)のアナログ信号を処理できるCPUを製作することができた。 ・命令体系を整備することによって、アナログ値の加減乗除、波形生成、無理数の近似値計算を行うことができるようになった。== ++今後の展望 ・電源を単電源から正負電源に変更し、マイナス電圧を扱えるように記憶装置と演算装置を再設計する。 ・搭載するアナログレジスタの数を増やし、より複雑な計算をできるようにする。また、2 DoF以上のアナログ信号を扱えるようにアーキテクチャを拡張する。 ・記憶装置や演算装置を改良し、より高精度な計算をできるようにする。++ # 謝辞 筆者X (ID: [electrotelecast](https://x.com/electrotelecast))の方で都度ポストしていますが、その際とても多くの反響をいただきました。いつも応援いただいていること、この場をお借りして感謝申し上げます。 また、AnaProcV1の設計思想、および、アーキテクチャの概要については、2024年12月1日、サイボウズ東京オフィスにて開催された「第4回自作CPUを語る会」で発表させていただきました。会を主催いただいた運営スタッフの方々、および、参加いただいたすべての方にこの場を借りて感謝申し上げます。 当日の発表資料、および、自作CPUを語る会については以下を参照してください。 - 第4回自作CPUを語る会:https://makecpu.connpass.com/event/332857/ - 発表資料:https://making-cpu.github.io/archive/ # 参考文献 [1] 渡波郁, CPUの作り方, 毎日コミュニケーションズ, 2003 [2] ちぇりーたくあん, [NAND(74HC00)だけで16bitCPUを作る[NLP-16]](https://cherry-takuan.org/article/?id=3), 2022 [3] uchan, [ComProc プロジェクト](https://uchan.net/ublog.cgi/comproc-project), 2024 [4] 稲葉保, 復刻版「初心者のためのアナログ技術指南」教科書からの脱出, CQ出版社, 2023