2023-06-17 20:25 — asano
カテゴリー:
前回のと一緒に調達したものがいくつかあるので何回かに分けて取り上げようと思います。
これはAm2901 4ビットスライスALU、AMDのAm2900ファミリの一員です。
以前取り上げたIDM2911もこのファミリ(セカンドソースですが)ですね。
その時も書いたようにこのファミリは一般的なプロセッサとペリフェラルといったものではなく、プロセッサを構成するための部品の集合体です。しかも決まった完成形があるわけではなく、すべてが回路設計者に任されています。命令デコーダは各自設計する必要があるのでAm2900命令セットといったものは存在しません。
さてこの2901ですが、簡単に言えば加減算と論理演算が可能なALU・シフトレジスタ・レジスタファイルを一体化したようなものです。ビット長は4ビットしかありませんが複数並べることで拡張できますし、必要ならルックアヘッド(2902)も用意されています。
40ピンもあるのは、74LS681などと異なりデータ入出力が独立していること、レジスタファイルの2ヶ所を同時参照するためアドレスが2組あること、複数個連結する際にキャリ/ボロー以外に双方向シフト用の線が必要なこと、などの理由でしょう。とにかく必要そうな線は全部外部に引き出した感じで、自由度は高そうです。
これを拡張したデバイスも存在します(私は持っていません)。
Am2903は機能拡張版です。
- レジスタファイルを外部で拡張可能
- パリティ・符号拡張
- 乗除算
- 正規化
データシートをざっと眺めただけですが、乗除算とかってどうするのだろう? 74AS888みたいにビット数に応じて複数回実行するのかな。
ピン数は48ピン(DIPの場合)に増えます。
Am29116は1つで16ビット扱えるようにしたものですが、単純にAm2901が4つ入っているわけではないようですね。入出力もマルチプレクスになっていますし、レジスタファイルの異なるアドレス間の演算もできなくなってしまったようです。単純な置き換えは難しいでしょう。
こちらは珍しい52ピンです。
コメント
シンCPUの創りかた
今ちょうど、作者のスダ様が、Am2901とUVEPROMを使ったμCode型i8080A互換CPUを創っています。
https://twitter.com/sudamin/status/1661930106791796736
https://twitter.com/sudamin/status/1661928713766969344
https://twitter.com/sudamin/status/1662081682143576066
https://twitter.com/sudamin/status/1669349009184665604
自作CPU設計中
自作CPUコンピュータ製作に使うため、AM2901BDCB(\1604)、AM2964BDC(\855)を若松さんで入手しました。
自己書換せず多段サブルーチンや再配置可能なプログラムが出来て、BASICインタプリタが動く最小CPUを目指しています。
以下のような感じの4bitノイマン型アキュムレータマシンを設計中ですが、使いやすいフラグ変化はどうあるべきか悩み中。
問題点として割込みが全く対応できないのですが、宜しければ他にも何かアドバイスを頂けたら嬉しいです。
Instruction 8bit (+ 8bit displacement)
ALU 4bit
Address Bus 16bit
Data Bus 8bit
AC 4bit
R0..R15 4bit
PC 16bit
BP (R3:R2:R1:R0)
CP (R7:R6:R5:R4)
DP (R11:R10:R9:R8)
EP (R15:R14:R13:R12)
Rn R0..R15
An PC,CP,DP,EP
0000:0000 NOP
0000:0001 HLT
0000:0010 CLC CF = 0
0000:0011 SEC CF = 1
0000:0100 ROR AC(n) = AC(n+1) ; AC(3) = AC(0) ; CF = AC(0)
0000:0101 RRC AC(n) = AC(n+1) ; AC(3) = CF ; CF = AC(0)
0000:0110 ROL AC(n) = AC(n-1) ; AC(0) = AC(3) ; CF = AC(3)
0000:0111 RLC AC(n) = AC(n-1) ; AC(0) = CF ; CF = AC(3)
0000:1000 INX BP = BP + 1
0000:1001 XCH CP <=> BP
0000:1010 XCH DP <=> BP
0000:1011 XCH EP <=> BP
0000:1100 DCX BP = BP - 1
0000:1101 XCH CP <=> DP
0000:1110 XCH DP <=> EP
0000:1111 XCH EP <=> CP
0001:bbbb LDI AC = uint4
0010:bbbb ADI AC = AC + uint4 + CF
0011:bbbb CPI AC - uint4
0100:bbbb ANI AC = AC & uint4
0101:bbbb XOI AC = AC ^ uint4
0110:bbbb ORI AC = AC | uint4
0111:0000 BSR [--BP] = PC + 2 ; PC = PC + int8
0111:0001 BRA PC = PC + int8
0111:0010 BNE if ZF==0 ; PC = PC + int8
0111:0011 BEQ if ZF==1 ; PC = PC + int8
0111:0100 BLO if CF==0 ; PC = PC + int8
0111:0101 BHS if CF==1 ; PC = PC + int8
0111:0110 BLT if (SF^OF)==1 ; PC = PC + int8
0111:0111 BGE if (SF^OF)==0 ; PC = PC + int8
0111:1000 JSR [--BP] = PC + 2 ; PC = CP + int8
0111:1001 JMP PC = CP + int8
0111:1010 JNE if ZF==0 ; PC = CP + int8
0111:1011 JEQ if ZF==1 ; PC = CP + int8
0111:1100 JLO if CF==0 ; PC = CP + int8
0111:1101 JHS if CF==1 ; PC = CP + int8
0111:1110 JLT if (SF^OF)==1 ; PC = CP + int8
0111:1111 JGE if (SF^OF)==0 ; PC = CP + int8
1000:dddd STA Rn = AC
1001:dddd LDA AC = Rn
1010:dddd ADC AC = AC + Rn + CF
1011:dddd SBB AC = AC - Rn + CF
1100:dddd AND AC = AC & Rn
1101:dddd XOR AC = AC ^ Rn
1110:dddd ORR AC = AC | Rn
1111:00xx STX [--BP] = An
1111:01xx STX [BP++] = An
1111:10xx LDX An = [--BP]
1111:11xx LDX An = [BP++]
Re: 自作CPU設計中
いつもコメントありがとうございます。
今回のはコメントというレベルではない力作ですね。
これフェッチ以外のメモリアクセスは16bitのみということで合っていますか?
BASIC移植を考えると8bitアクセス無いのは不便な気がします。
私も昔からいつかはCPU作ってみたいと考えていましたが……
課題を頭の片隅に置きつつ変わった古いCPUを見ていると面白い解決(回避)方法が出てきて楽しいのですが、なんか世のCPUの不便な点を寄せ集めたようなものが出来てしまいそうで、まだ具体的な計画を立てるには至っていません。
Re: 自作CPU設計中
大は小を兼ねるかなと思い切ったのですが、やっぱりI/Oとか困りますし、8bitメモリアクセスがないと不便でしたね。
内部バス制御と命令デコーダのシンプルさ、メモリコピーとスタックの作り易さ、サブルーチンのレジスタとPCの退避
・復帰コストなど、以下の命令とどちらがよいか、悩んでいました。
Pn R1:R0..R15:R14
1111:0xxx ST [BP+int8] = Pn
1111:1xxx LD Pn = [BP+int8]
どうせACは頻繁に破壊されるから、CPI命令を削除してSTX,LDX命令に変更することも考えていました。
または、LDI #n (0001:bbbb) はPDP8風に、ANI #0,ADI #n (0100:0000,0010:bbbb) に置き換え可能ですが、
LDI命令がないのは現代の命令セットとして変態なので、できれば避けたい(笑)
LDI命令がない場合、元々CF無しADD,SUB命令がないため、STA,LDA命令でCFを保持するかクリアするか悩んで
いたのですが、さらにSTA命令でACをクリアするかも悩みどころ。
他には、INX,DCX命令はBPの値でCF無変更,ZF変更にする予定ですが、STX,LDX命令でも同じにして平気かとか、
XCH命令では、どちらの値でZF変更をするべきか変更しないべきとか、フラグは奥が深い。
Re: 自作CPU設計中
いっそスタック操作も8bitのみにして、BSR/JSRでの戻りアドレスはレジスタ(スタックへの退避はプログラマの責任)にするとか。
比較命令の無いものはいくつかありますね。極端なものだと条件ブランチ系がACの値でフラグが無かったり。オペコードが足りないのならJSR/JMP以外のJcc命令をなくすのも手かなと思います。
フラグは…… 転送系の命令では変化しないものが多いですね。NS32kみたいに演算命令と比較命令で変化するフラグが全く異なるものも。各命令ごとに想定する使い方考えてどのフラグ変化させるべきか作り込むのが、素直に書けて良いと思うか、フラグ動作を覚えなくてはならなくて不便と思うか、悩ましいところ。
Re: 自作CPU設計中
長々とお付き合い頂きありがとうございます。
CPI命令を削除してSTX/LDX命令を8bit単位にしました。それに伴い、オペコード以外に、内部接続や動作も見直しました。
ブロック図レベルで色々変更したため、回路の再設計前にiverilogで動作確認予定です。本体完成度9割、テストベンチ0割。
> スタック操作も8bitのみにして、BSR/JSRでの戻りアドレスはレジスタ
命令ステップ数は倍増になりますが、合計実行クロック数では大差なさそうなので、そのほうが良いですね。
実は、戻りアドレスのレジスタ退避は、回路的には一番簡単そうなため、最初期案ではそうしていました。
しかし、JSR命令でCPに退避する場合の一時レジスタ不要の回路を思いつかず、CP以外に退避では演算用のレジスタが不足しそう、と考え断念していました。
バス周りの回路を再検討し、加算器付きMAレジスタを一時レジスタとして共用することにしました。
上記に伴いLDX/STX命令のオペランドを変更、PCの代わりにFL(SF,ZF,OF,CF)とACにして、割込みもプログラマの責任で対応できるようにしました。
ただ、STI,CLI命令相当の動作をどうするか要検討。多分I/Oにレジスタを接続して実現。
またXCH命令の回路も、再検討に伴い、MAレジスタとMDレジスタを一時レジスタに共用する(2)案に変更。
(1) X = X ^ Y ; Y = X ^ Y ; X = X ^ Y
全組み合わせのレジスタセレクタが必要でバス周りの回路が複雑
(2) MAR = X ; MDR = Y ; Y = MAR ; X = MDR
2つの一時レジスタが必要
(3) レジスタマッピングレジスタを追加
回路未検討。クロック速度は低下するがクロック数は最小
> 比較命令の無いもの
RISC-Vは比較命令と分岐命令が悪魔合体していて、正直その発想はなかった。
> JSR/JMP以外のJcc命令をなくす
すでに分岐命令のオペコードは1つだけで、覚えやすい3文字ニモニックを考えつかないというアホな理由もあり、BSR/JSR命令は無条件のみです。
1byteの条件スキップ命令も検討しましたが、次の命令が1byteか2byteかによって挙動が変わり、状態管理が一段階複雑化するため、諦めました。
> フラグは……転送系の命令では変化しないものが多い
自分は転送系でフラグ変化するCPUを、6809以外知りません。もしかして68xx系でも6809だけ?
転送系の命令でCF変化したら、ALU幅超の演算が大変になることに今更ですが気付いたため、LDX/STX/XCH命令は変化なし、LDA/STA/INX/DCX命令はZFのみ変化にしました。
以下が少しオペコードを移動した最新版です。名前は小さな4bitCPUということで、ATOM-8をリスペクトしてATOM4としました。
こんな長いコメントが書き込めることにちょっと驚き。verilogを貼ろうとしたら、タブスペースが消えて読めなかった。
Rn R0..R15
Rx #0:FL,#0:AC,R5:R4..R15:R14
BP (R3:R2:R1:R0)
CP (R7:R6:R5:R4)
DP (R11:R10:R9:R8)
EP (R15:R14:R13:R12)
0000:0000 NOP
0000:0001 HLT
0000:0010 CLC CF = 0
0000:0011 SEC CF = 1
0000:0100 ROR AC(n) = AC(n+1) ; AC(3) = AC(0) ; CF = AC(0)
0000:0101 RRC AC(n) = AC(n+1) ; AC(3) = CF ; CF = AC(0)
0000:0110 ROL AC(n) = AC(n-1) ; AC(0) = AC(3) ; CF = AC(3)
0000:0111 RLC AC(n) = AC(n-1) ; AC(0) = CF ; CF = AC(3)
0000:1000 INX MAR = BP + 1 ; BP = MAR
0000:1001 XCH MAR = CP ; MDR = BP ; BP = MAR ; CP = MDR
0000:1010 XCH MAR = DP ; MDR = BP ; BP = MAR ; DP = MDR
0000:1011 XCH MAR = EP ; MDR = BP ; BP = MAR ; EP = MDR
0000:1100 DCX MAR = BP - 1 ; BP = MAR
0000:1101 XCH MAR = CP ; MDR = DP ; DP = MAR ; CP = MDR
0000:1110 XCH MAR = DP ; MDR = EP ; EP = MAR ; DP = MDR
0000:1111 XCH MAR = EP ; MDR = CP ; CP = MAR ; EP = MDR
0001:bbbb XOI AC = AC ^ uint4
0010:bbbb ANI AC = AC & uint4
0011:bbbb ORI AC = AC | uint4
0100:bbbb ADI AC = AC + uint4 + CF
0101:bbbb LDI AC = uint4
0110:0xxx LDX MAR = BP - 1 ; MDR = [MAR] ; Rx = MDR ; BP = MAR
0110:1xxx LDX MAR = BP ; MDR = [MAR] ; Rx = MDR ; MAR = BP + 1 ; BP = MAR
0111:0xxx STX MAR = BP - 1 ; MDR = Rx ; [MAR] = MDR ; BP = MAR
0111:1xxx STX MAR = BP ; MDR = Rx ; [MAR] = MDR ; MAR = BP + 1 ; BP = MAR
1000:0000 BSR MAR = PC + int8 ; CP = PC ; PC = MAR
1000:0001 BRA MAR = PC + int8 ; PC = MAR
1000:0010 BNE if ZF==0 ; MAR = PC + int8 ; PC = MAR
1000:0011 BEQ if ZF==1 ; MAR = PC + int8 ; PC = MAR
1000:0100 BLO if CF==0 ; MAR = PC + int8 ; PC = MAR
1000:0101 BHS if CF==1 ; MAR = PC + int8 ; PC = MAR
1000:0110 BLT if (SF^OF)==1 ; MAR = PC + int8 ; PC = MAR
1000:0111 BGE if (SF^OF)==0 ; MAR = PC + int8 ; PC = MAR
1000:1000 JSR MAR = CP + int8 ; CP = PC ; PC = MAR
1000:1001 JMP MAR = CP + int8 ; PC = MAR
1000:1010 JNE if ZF==0 ; MAR = CP + int8 ; PC = MAR
1000:1011 JEQ if ZF==1 ; MAR = CP + int8 ; PC = MAR
1000:1100 JLO if CF==0 ; MAR = CP + int8 ; PC = MAR
1000:1101 JHS if CF==1 ; MAR = CP + int8 ; PC = MAR
1000:1110 JLT if (SF^OF)==1 ; MAR = CP + int8 ; PC = MAR
1000:1111 JGE if (SF^OF)==0 ; MAR = CP + int8 ; PC = MAR
1001:nnnn XOR AC = AC ^ Rn
1010:nnnn AND AC = AC & Rn
1011:nnnn ORR AC = AC | Rn
1100:nnnn ADC AC = AC + Rn + CF
1101:nnnn LDA AC = Rn
1110:nnnn SBB AC = AC - Rn + CF
1111:nnnn STA Rn = AC
Re: 自作CPU設計中
切りの良いところまでと思ったら、日を跨いでしまった。バグてんこ盛りですがiverilogで動作するところまで来たので、適当にググって見つけた以下の場所にアップロードしました。
https://d.kuku.lu/e3mjafazt
今のところ、山場の状態ステートと命令読み込みと分岐命令はクリア、交換命令とフラグ周りの演算命令がおかしく、メモリの読み書きが全滅で不定伝搬して勝手にリセットがかかってしかも復帰しない。
テスト用コード込みで合わせて370行、CPUのみだと250行なので、コメントはほとんどありませんがすぐ読めると思います。
Re: 自作CPU設計中
ストア命令以外のバグ取り完了。記述スタイルと長い行をリファクタリングしたため、CPUは280行。
https://d.kuku.lu/pyxpzs7g2
メモリライトはテスト側との問題かもしれないのでしばらく要学習。
Re: 自作CPU設計中
ちょっと返事に時間かかっていたら……
verilog実装されてる!
verilogは長らく弄っていなくて細かいこといろいろ忘れていましたが、読むだけなら何とか。
条件スキップは、無実行フラグ(実行結果をレジスタ等に反映しない)を作る手もありますね。実行速度的には不利ですが。
MC68000系もMOVE命令でコンディションコード変化します。その代わりに比較用のCビットと多倍長演算用のXビットは別になっていて、MOVE命令でCビットはクリアされてもXビットは変化しません。
Re: 自作CPU設計中
文字通り夢中でデバックした結果、メモリの読み書きも可能になりました。また、don't care の考慮漏れでおかしな動作があったのを修正しました。メモリコピーを実行したときのvvpのログも添付します。
https://d.kuku.lu/wtdfdbryg
実行ステート数は以下の通りです。分岐するしないにかかわらず同一です。また、バス制御の単純化のため、メモリアクセスのステートは多めです。
3clk NOP HLT CLC SEC
4clk ROR RRC ROL RLC INX DCX XCH XOI ANI ORI ADI LDI XOR AND ORR ADC LDA ADC LDA SBB STA
7clk LDX STX Bcc Jcc
ちょっと実デバイスでも動かしたくなったので、アマゾンで1500円のMAX2CPLD基板と700円のJTAGライタと1000円の簡易ロジアナを注文しました。
自作CPU完成(verilog版)
ソースの末尾に仕様と命令表を追記、draw.ioで描いたブロック図のPDFを追加しました。
https://d.kuku.lu/kp6pc4mkj
自作CPU(verilog版)アセンブラ作成
プログラミング言語AWK第二版がでた記念に、AWKで簡易アセンブラを書いてみました。
まだ170行しかなくラベルすら使えませんが、拡張機能のない素のAWKでも動くはず。
https://d.kuku.lu/srd3msype
今岡氏のDL166(https://monoist.itmedia.co.jp/mn/series/33743/)を参考にCPU自体も書き換え160行まで減らしました。
ステージデコードを簡略化し、回路規模と実行サイクルも削減。そのため、INX DCXとCLC SECのオペコードを入れ替えました。
新旧ブロック図を見比べると一目瞭然。
2clk NOP HLT CLC SEC XCH Bcc Jcc
3clk INX DCX ROR RRC ROL RLC XOI ANI ORI ADI LDI LDX STX XOR AND ORR ADC LDA ADC LDA SBB STA
verilogで複数レジスタをまとめて一つのレジスタとして扱う際に、別名をつける方法ってないんでしょうか。
右辺はfunctionで可能だが、左辺はどうしたらよいかわからない。
あと、AWKで書いていて、verilogでもcaseのbreak,continueが選択出来たら、と思いました。
Re: 自作CPU(verilog版)アセンブラ作成
新しいAWK本は到着待ちです。一緒に注文したものが入荷待ちになって巻き添えになってしまった……
アセンブラを作ること自体が目的でないならaslを利用してしまえばラベル回りは考えないですみます。
もう15年以上verilogからは離れてしまっているので、何をしているかなんとなくわかる以上のことはちょっと……
XC9500を死蔵しているのでちょっと弄ってみたい気はするのですがいつもGALで足りてしまって先送りになってます。
コメントを追加