1桁加減算カリキュレータの作成
何かしらのシステムを作ってそれを早く動かしたい!!という野望を持った時、どういう発想があるだろうって気になっていた時がありました。私はシステムの基盤そのものを早くしたいと思いますね。もちろんデータ構造やそれに伴って勉強しなければならないアルゴリズムの観点というのは非常に重要です。それはソフトウェアとしての対策。めちゃくちゃ重要です。ではハードウェアからのアプローチってなんだろうと思って作ったものがこれです。脆弱性についてもより低いところではソフトウェアレベルでもハードウェアレベルでも対策をしないといけない(*1)のでそのアプローチの視点を増やすためにこういったことを勉強の一環で開発していました。
------------------------------------------------------------------------------------------------
実機で実際に動いたのを確認済みです。最後に最終結果として写真貼っておきます。
作成の手順 (Github)より
https://github.com/ultrasupara-jump/One-digit-add_subtract-calculator
全体としてどういうシステムを作りたいのかについて共有します。
SEG_1_OUTとSEG_SEL_1はSEG_A~SEG_Dを、SEG_2_OUTとSEG_SEL_2はSEG_E~SEG_Hをそれぞれ制御できるように配置する。↓LEDの割り当て配置と表示の例(3)
- さらに7セグの表示器と制御論についてもう少し補足
7セグメントLEDはダイナミック方式で制御される。出力データをセレクタで切り替えて表示する。出力データはSEG_1_OUT、セレクタ信号はSEG_SEL_1でOK
上のハード設計を踏まえて
SEG_SEL_1 = 1110 (E) -> SEG_1_OUTはSEG_Aで表示
SEG_SEL_1 = 1101 (D) -> SEG_1_OUTはSEG_Bで表示
SEG_SEL_1 = 1011 (B) -> SEG_1_OUTはSEG_Cで表示
SEG_SEL_1 = 0111 (7) -> SEG_1_OUTはSEG_Dで表示
SEG_SEL_2 = 1110 (E) -> SEG_2_OUTはSEG_Eで表示
SEG_SEL_2 = 1101 (D) ->SEG_2_OUTはSEG_Fで表示
SEG_SEL_2 = 1011 (B) ->SEG_2_OUTはSEG_Gで表示
SEG_SEL_2 = 0111 (7) ->SEG_2_OUTはSEG_Hで表示
の、ような表示を高速切り替えできるようにし全てのLEDに対して割り当てを行う。
- Halfadder
1ビット(A,B) の加算を行い,和(Y) と上位への桁上げ信号キャリー(CO) を出力する回路を書いてやる。極めてシンプルな加算回路のコンポーネント。
https://github.com/ultrasupara-jump/One-digit-add_subtract-calculator/blob/main/work/src/halfadder.v
なぜかわからないけどCoqとして読まれている。実機上では正しく動作が確認できたので問題なし。証拠写真⏬
テストベンチ::Github
transcript上での動作確認が取れた
雑に感じたこととしては1step == 1psに相当すると予想。そして$finishというタスクが実行されプログラムが終了。$monitorでは書式と変数を指定しておりこれはC++の出力ストリームと非常に似ていると感じた。しかしこの1行によって何度も文字が出力されていることから、$monitorは変数の変化を検出するたびに実行でうまく動かしているのではないかということだ。
- 2. Fulladder
1bitの被加数A, 加数B, 下位桁からのキャリーCINの加算を行い,和(SUM) と上位への桁上げ信号キャリー(COUT) を出力。フルアダーは2個のハーフアダーと,1個のORゲートで構成。⏬構成図と対応表
みたままの割り当てをしてやれば良い Github::
⏬テスト
テストベンチ::Github
transcript 桁上げ対応がちゃんとできていることがわかる。
ただしい挙動を得ることができた
ソースコードについて A,B,SUM は4ビット指定している。それを出力するコードをモジュール内に書いた。テストベンチについてテスト入出力の信号を4ビット幅でとって タイムステップを50でとることによってタイムステップごとに a, b の値を変化させてシュミレーションすることによってテストを行うことができた。
- 3. four (4) bit ripple carry adder
3個のフルアダー(FA1~FA3) と1個のハーフアダー(HA0) を図のように接続すると4ビット2進加算器(ripple4adder)ができる。これはいわゆる構造記述の一環で今までに書いてきたものを部品としてみることによって部品として扱うことができるのでそれらを定義通りに組み合わせるにはどうすれば良いのかを考える。
Github::
テストについてもここで提示する。Github::
テスト結果
加算結果が16を超えない範囲では出力SUMが正しい数値を表示していることが分かる。これらの組み合わせではcarryは0を出力していた。6+14, 13+9など合計が16を超えるとcarry信号が1となりsumに何かしらの信号が確認できるのでO K
これには前提としてBCD符号についての理解が必要。10-16にはA-Fが割り当てられることになるがSに0110 = 610 を加えて補正するとこれが1の桁になる)つまり下記の表のように補正する必要性がある。
では全体としてどのような概略図になるのだろうか。以下である。
dummyはハーフアダーの桁上げ信号と接続されていることから桁あふれを表す信号線。3ビットの出力を設けてやりさらにdummyを設けることによって補正結果の調整に使うことを想定している。ここでは前に書いたhalf adderとfull adderを接続することによって計算している。
大きくテーマに分けると「桁上げ検出」「6補正」を先ほど作ったモジュールに噛み合わせれば作成できそうな目算がたった。では順番に処理の過程を見ていくことにする。
「桁上げ検出」
and演算とor演算をしているところが桁上げ検出である。それがcarry detect。加算されて出てきた結果を入力側に渡してk1とk2のand演算k3とk4のand演算を割り当てる。そして一番重要なのがdetect= (an1 || an2) || k5の部分だがそこでは論理条件を考えることによって桁上げの検出をしている。
Github::
「6補正」
16進数で表現されてしまうため補正を行う必要があってこの補正方法として表現数の差である6を加算結果にもう一度加算することで強制的に桁をあふれさせ0~9の表現のみを使用できる仕組みをうまく利用することによって桁の処理をする。2進数の加算をして得た結果と桁上げ判定後の結果を入力信号として受け渡すことでつなげている。
Github::
https://github.com/ultrasupara-jump/One-digit-add_subtract-calculator/blob/main/work/src/hosei6.v
ここまでできたら次はそれら3つのコンポーネントを繋いで本格的にシステムを組み上げていく作業。新しいモジュールを立ててそこに書き込むことで接続した。
それがBCDTOP
Github::
https://github.com/ultrasupara-jump/One-digit-add_subtract-calculator/blob/main/work/src/BCDTOP.v
無事、f0-f4ワイヤ線を結合することができました。
それではテストプログラムを書いて挙動の確認をしてみる。
Github::
結果的にこうなりました。
補正検出回路では4bitの加算で桁あふれが発生した場合と結果のbitから10以上であることを検出した場合に信号が1になっている。つまり桁上げとして正しくないところに1を出すことができているのでその他正常に動作しているものと合わせ、うまく動作できた。
- 5. six (6) bit add-subtract circuit
したの図の部分を主に作成する。4までで全体制御は書くことができたのでここからは実際に入力された信号に対する過減算装置を記述する。以下の手順で作成して実機に反映させるまでのステップを踏んだ。
(1) 減算モジュールの設計
4bitの入力A,Bの減算結果を6bitのSUB_DATAへ出力する回路を設計する。減算は入力Bの2の補数を生成しその数とAを足し合わせることによって実現する。
イメージ図はこんな感じ
Github::
https://github.com/ultrasupara-jump/One-digit-add_subtract-calculator/blob/main/work/src/sub.v
(2) 加算モジュールの設計
4bit加算器を実装するFPGAの仕様に合わせて改良したものを使用する。
イメージ図はこんな感じ
Github::
https://github.com/ultrasupara-jump/One-digit-add_subtract-calculator/blob/main/work/src/add.v
(3) 1桁加減算トップモジュールの設計
設計した計算モジュールとあらかじめ用意されたコントロールモジュールを組み合わせてトップモジュールを設計する。
イメージ図はこんな感じ
Github::
LEDへの出力の受け渡しと配線->controllerで制御部分の実装。インスタンス部分では.CLK(CLK)などの部分が続くがこれはテンキーへの制御受け渡しをしている部分で非常に核となる重要な実装箇所
(4) トップモジュールのテストベンチの設計
モジュールの動作確認するためのテストベンチを設計する。検証する計算は1+2, 5-9, 9+8, 6-6でやった。
3で作ったものに対するテストベンチを作成する。以下の様な実装をした。
Github::
クロック信号、リセット信号、Key dataの呼び出しをすることにSEG_1_OUTはSEG_Cで表示、SEG_SEL_1 = 0111 (7) のときSEG_1_OUTはSEG_Dで表示する必要があるのでこれに従った実装をすることである。出力はLED に渡してやる。次にトップのモジュールを呼び出す。これは動作指定したものをテストで呼び出す作用を測っている。次にalways文でシグナル生成をしてタイムステップを踏む。ここでのポイントは7セグメントLEDはダイナミック方式での制御であるため出力データをセレクタで切り替えて出力しないといけない点である。つまり例を示すとSEG_SEL_1 = 1110 (E) のときSEG_1_OUTはSEG_Aで表示、SEG_SEL_1 = 1101 (D) のときSEG_1_OUTはSEG_Bで表示、SEG_SEL_1 = 1011 (B) のときといった感じである。その際のテンキーの受け渡しのルールはFPGAの仕様に基ついて以下のルールを守って行う。
テンキーの割り当てについて
- キーデータ,ボタンデータは押していない状態では"1"が出力されている。押すと"0"が出力される。キー,ボタンについては”0” が有効データ
- リセットボタンを押すとRST_X信号が"0" になる。離すと"1" になる。
- テンキーデータは,14ビットの信号PSW[13:0] にセットされる。
- あるキーを押したとき,対応するPSW [X]の値が"0" になる
(5) シミュレーションで波形が正しいことを確認したらQuartus を起動しVerilogのコンパイル、ピンのアサインを行ってFPGAにプログラムを書き込む。実機での動作確認
その結果⏬
完成しました。
コラム (追加の実装 -もっと高速でより速いFPGA実装への探求- )
先ほど示したsubの改良論理回路設計に伴う変更コードを作成した。改良点としては最下位bitの加算にFulladderを用いているところである。これによってセレクタ信号が1の時に「A_DATA + B_DATA + 1」のような演算ができることによって少し機能性の上がった回路設計になったのではないか?とおもう。ソースコード、およびテストのソースコードについて載せておく
https://github.com/ultrasupara-jump/One-digit-add_subtract-calculator/blob/main/work/src/andandsub.v
テストのソースコード: