SSブログ
English Version

SuperSimpleController(その8)メモリ関連のマシン語実装 [OriginalCPU]

 前回の記事でALUとジャンプ関連のマシン語の実装ができたので、今回はメモリ関連のマシン語について書いてみます。
 また、本記事の後半にハード的な問題と対処方法についてメモしておきます。

 今回追加したマシン語は下表の黄色部分のメモリ関連の命令です。

マシン語コード表(作成中) 第2版

★変更 2021/03/13 CFセット機能を追加
★追記 2021/04/28 I/O命令追加の記事に更新したコード表を記載しました


 今回こだわった点の一つとしてコールを1命令で実装したいということがあります。
 ハード的にスタックポインタを持っていませんが、なるべくスタック処理を軽くしようと考えた結果、FFFFHのメモリ内容をスタックの下位バイトとしてスタック上位バイトはFFHに固定することにしました。
 それでもスタック操作では処理数が増えてしまうので上記表に示すようにCALLとRETでは多くのクロック数を必要としています。

 また、メモリアクセス方法としてはアドレス直値指定の方法とベースアドレス(上位アドレス)値を命令内に埋め込んでR0をインデックスにする方式の二通りを準備しました。

 追加したマシン語の動作確認は下記のソースを用いて行っています。

追加したマシン語試験用プログラム(アセンブリ言語)
;++++++++++++++++++++++++++++++++++++++++ ; Simple8Z test program ; by skyriver ; V0.01 2021/02/16 ;++++++++++++++++++++++++++++++++++++++++ .list FFFF STACK EQU 0FFFFH FF3A VAL EQU 0FF3AH 00FF BASE EQU 0FFH 0100 SUB EQU 0100H 0000' ASEG ORG 0 LD_R0 05H ; x7 R0:05 R1:xx 0000 10 + DB 10H 0001 05 + DB 05H WR_R0 VAL ; x18 R0:05 R1:3A 0002 50 + DB 50H 0003 FF3A + DW VAL LD_R0 0FH ; x22 R0:0F R1:3A 0005 10 + DB 10H 0006 0F + DB 0FH RD_R0 VAL ; x33 R0:05 R1:3A NG ->OK 0007 60 + DB 60H 0008 FF3A + DW VAL LD_R1 3CH ; x37 R0:05 R1:3C 000A 18 + DB 18H 000B 3C + DB 3CH LD_R0 0FFH ; x41 R0:FF R1:3C 000C 10 + DB 10H 000D FF + DB 0FFH WR_R1R0 BASE ; x51 R0:FF R1:3C setr SP:FFFF 3C 000E 70 + DB 70H 000F FF + DB BASE LD_R1 0 ; x55 R0:FF R1:00 0010 18 + DB 18H 0011 00 + DB 0 RD_R1R0 BASE ; x65 R0:FF R1:3C NG R1:00 ->OK 0012 80 + DB 80H 0013 FF + DB BASE CALL SUB ; x89 R0:14 R1:FF ADR:0100 0014 B0 + DB 0B0H 0015 00FF + DW (SUB) - 1 HALT ;x147 R0:14 R1:00 0017 FF + DB 0FFH ORG SUB RD_R0 0FFFFH ; x100 R0:3A R1:FF 0100 60 + DB 60H 0101 FFFF + DW 0FFFFH RD_R0 0FF3BH ; x111 R0:00 R1:3A 0103 60 + DB 60H 0104 FF3B + DW 0FF3BH RD_R0 0FF3AH ; x122 R0:14 R1:3A 0106 60 + DB 60H 0107 FF3A + DW 0FF3AH LD_R0 55H ; x126 R0:55 R1:3A 0109 10 + DB 10H 010A 55 + DB 55H RET ; x145 R0:14 R1:00 ADR:0017 010B E0 + DB 0E0H END


 この試験プログラムを最後まで通すのに苦労しましたが、下図が最後のHALT命令まで実行できた際のロジアナ画面になります。
 12:IRW/が命令フェッチ信号なので0の部分がマシン語の区切りになります。
 A1カーソルがPC下位バイトからのキャリによるPC上位バイトのライト信号生成部分(詳細は後述)でA2カーソルが最後のHALT命令を実行したタイミングを示しています(HALT命令はアドレスを進めずに以降も実行し続けます)

テストプログラム完走時のロジアナ画面


 冒頭で書いたようにメモリ関連のマシン語を実装する上で動作が不安定な部分があったのでハード面で対応した内容について以下にメモしておきます。

  1. PCHのライト信号
     プログラムカウンタ(PC)の上位バイト(PCH)へのライト信号はデータロード時などに使うuCODE制御のライト信号と下位バイト(PCL)からのキャリ信号をダイオードORするように回路を組んでいました。
     しかし、PCHへのライト信号がダイオードORにより鈍ってしまい動作が不安定でした。下図で水色と黄色がそれぞれダイオードor通過前と通過後の信号です。

    ダイオードorによるPCHライト信号の鈍り

     そこでPCHライトへのPCLキャリの合成をGALで対応し、それまでGALで対応していたROMのセレクト信号(MEM/=0 & A15=0)をダイオードでANDすることにしました。
     下図のロジアナ画面で07:PCL_RCO/がPCLからのキャリで05:QRCO/がGALでラッチ後のRCO/です。
     14:PCHWのA1カーソル部で判るようにキャリによりライト信号が自動的に生成されています。

    GALでのライト信号とキャリの合成


  2. 電源電圧とノイズ対策
     今回のオリジナルCPUは現時点では13個のICで構成されています(なるべくTTLを使うようにしていてGALは1個だけ使用)が、消費電流が0.5A程度でブレッドボード上の電源電圧が4Vくらいまで低下していました。
     対策として5V電源からの給電をやめて安定化電源から電源供給するようにしてブレッドボード上で電源電圧が5Vになるように調整しました。またノイズ対策としてパスコンを増やしました。

  3. uCODEのグリッチノイズ対策
     CALL命令実行時にスタックに書き込んだPCのローバイトがFFHに化ける現象が発生し、悩みました。各制御信号は想定通りに出ていてSRAMのピン上でレベル確認しても問題ない状態でした。
     しかも、R0レジスタをメモリに格納する命令"LD (adr),R0"では問題ないのにCALL命令での書込みだけNGとなるという奇妙な現象です。
     SRAM(UM61512A-15)のライト信号にグリッチが発生し、メモリ内容が書き換えられているのが原因でした。
     このSRAMのライト信号幅の最小値は9ns(さすが高速なSRAM)でロジアナでは見れないグリッチが発生していたようです。グリッチ発生個所はuCODEのアドレスがB9HからBAHに移行する際にBBH(ライトがアクティブ)のデータが一瞬現れたものと推定しています。
     対策としてはライト信号を470Ωの抵抗経由で接続することで解決しました(最初は20pF程度のコンデンサも入れる予定でしたがSRAMの入力ピン容量が最大8pFであり抵抗だけで大丈夫でした)
     もっと根本的に対処するのであれば、uCODEのROMのOE/を適切に制御(今回は0に固定)するか、uCODEのROMに対するアドレスをグレイコードにする等の対策が必要になります。

  4. データバスのプルアップ
     ハード問題ではないですが、スタック操作する際にPCをFFFFHに設定したいのでデータバスにプルアップ抵抗を追加しました。




[TOP] [ 前へ ] 連載記事 [ 次へ ]

nice!(0)  コメント(0) 

SuperSimpleController(その7)マシン語の実装検討 [OriginalCPU]

 前回の記事でジャンプ命令が動くようになったのでマシン語の実装検討を進めています。
 今回のオリジナルCPUはROM内に制御信号を格納する方式でALU自体もROMで実現しているので自由度は高いのですが、例えばALUの入力をビットフィールドで指定するなどのような機構がないので、一般的なCPUのマシン語にあるディスティネーションレジスタを指定するビットフィールド等のようなものがありません。
 そのため、対称性とか直行性とは無縁(チップ数を倹約しないのであればできなくもないですが)です。

 RAMはまだ未結線状態でRAM関連のマシン語は検討中なのですが、ALU関連のマシン語等について暫定版ができたので下図に示します。
 ALUの検討の記事で書いたように4bitで機能指定しますが、この4bitはマシン語コードの下位4bitで指定するようにしました。

マシン語コード表(作成中)
★変更 2021/02/16
 マシン語構成検討からの反映でジャンプ命令のコードを変更

 下のリストは今回使用した試験用のプログラムです。uCODE内のそれぞれのコードを実行するようしています。
 コメント中の「x数字」はそこまでの命令実行に必要なクロック数で、このクロック数分だけ実行して確認するようにしています。
 始めはuCODEを記述するのが大変でしたが、次第に慣れてきたので思惑通りに動くようになってきました。

uCODE試験用プログラム(アセンブリ言語)
;++++++++++++++++++++++++++++++++++++++++ ; Simle8Z test program ; by skyriver ; V0.01 2021/02/08 ;++++++++++++++++++++++++++++++++++++++++ .list 0000' ASEG ORG 0 CLR_R0 ; x6 R0:00 0000 2E + DB 2EH LD_R0 0FFH ; x10 R0:FF 0001 10 + DB 10H 0002 FF + DB 0FFH INC_R0 ; x13 R0:00 ZF:0 CF:1 0003 3D + DB 3DH LD_R1 00FH ; x17 R0:00 R1:0F ZF:0 CF:1 0004 18 + DB 18H 0005 0F + DB 00FH ADC_R0_R1 ; x20 R0:10 R1:0F ZF:0 CF:0 0006 35 + DB 35H SHL_R0 ; x23 R0:20 R1:0F ZF:0 CF:0 0007 38 + DB 38H XOR_R1_R0 ; x26 R0:20 R1:2F ZF:0 CF:0 0008 43 + DB 43H LD_R1_R0 ; x29 R0:20 R1:20 ZF:0 CF:0 0009 20 + DB 20H CP_R0_R1 ; x32 R0:20 R1:20 ZF:1 CF:0 000A 4E + DB 4EH HALT ; x34 000B FF + DB 0FFH END Macros: ADC_R0_R1 ADC_R1_R0 ADD_R0_R1 ADD_R1_R0 AND_R0_R1 AND_R1_R0 CKDECR0 CKINCR0 CKLSBR0 CKMSBR0 CKZR0 CKZR1 CLR_R0 CP_R0_R1 DEC_R0 HALT INC_R0 JC JNC JNZ JP JZ LDF_R0_R1 LDF_R1_R0 LD_R0 LD_R0_R1 LD_R1 LD_R1_R0 NOP OR_R0_R1 OR_R1_R0 RST SBC_R0_R1 SHLC_R0 SHL_R0 SHRC_R0 SHR_R0 SUB_R0_R1 XOR_R0_R1 XOR_R1_R0 Symbols: No Fatal error(s)


 途中まで実行してはロジアナで確認し、問題あれば修正する作業を繰り返して、最後まで動くようになりました。
 図中のA1カーソルが先頭のCLR_R0でA2カーソルが最後のHALT実行後のタイミングになります(HALTは以降繰り返します)

試験プログラム実行時のロジアナ画面


 まだ途中ではありますが、レジスタ操作と条件ジャンプのマシン語ができたのでナイトライダーのようなデモを作ってみました。
 クロックは試験用に50Hzにしていますが、高速動作時にボトルネックになるのはメモリアクセス時間(100ns程度)なので10MHzくらいまでは動作するのではないかと思います。

 ROM式ALUから派生したコマンドであるMSBとLSBのチェックという少し変わったコードを下記のリストで使用しています。

Simple8Z 初めてのデモプログラム(アセンブリ言語)
;++++++++++++++++++++++++++++++++++++++++ ; Simle8Z demo program (Night Rider) ; by skyriver ; V0.01 2021/02/14 ;++++++++++++++++++++++++++++++++++++++++ .list 0000' ASEG ORG 0 LD_R1 00H 0000 18 + DB 18H 0001 00 + DB 00H LD_R0 01H 0002 10 + DB 10H 0003 01 + DB 01H 0004 LEFT: SHL_R0 0004 38 + DB 38H CKMSBR0 0005 48 + DB 48H JNC LEFT 0006 C3 + DB 0C3H 0007 0003 + DW LEFT - 1 0009 RIGHT: SHR_R0 0009 3A + DB 3AH CKLSBR0 000A 4A + DB 4AH JNC RIGHT 000B C3 + DB 0C3H 000C 0008 + DW RIGHT - 1 JP LEFT 000E C4 + DB 0C4H 000F 0003 + DW LEFT - 1 END Macros: ADC_R0_R1 ADC_R1_R0 ADD_R0_R1 ADD_R1_R0 AND_R0_R1 AND_R1_R0 CKDECR0 CKINCR0 CKLSBR0 CKMSBR0 CKZR0 CKZR1 CLR_R0 CP_R0_R1 DEC_R0 HALT INC_R0 JC JNC JNZ JP JZ LDF_R0_R1 LDF_R1_R0 LD_R0 LD_R0_R1 LD_R1 LD_R1_R0 NOP OR_R0_R1 OR_R1_R0 RST SBC_R0_R1 SHLC_R0 SHL_R0 SHRC_R0 SHR_R0 SUB_R0_R1 XOR_R0_R1 XOR_R1_R0 Symbols: 0004 LEFT 0009 RIGHT No Fatal error(s)


 twitterにポストした動画付きメッセージを貼っておきます。




[TOP] [ 前へ ] 連載記事 [ 次へ ]

nice!(0)  コメント(0) 

SuperSimpleController(その6)JP命令が動いた! [OriginalCPU]

 前回の記事の冒頭でジャンプ命令の処理の中でアドレスが想定通りに設定されないことを書きロジアナ画面も貼りましたが、原因が判明しジャンプ命令が無事動作するようになりました^^

 原因はなんとTL866II用のUV-EPROMアダプタ(中華製)でした orz
 ALUとして使っているM27C322への書込みはM27C4096のピン配置に変換するアダプタを用いて8回書き込んでいますが、書込みブロック設定用のDIPスイッチが上下逆に取り付けられているため、ブロックアドレスが反転していましたw
 その他、ブレッドボードで使っていたリード線が2本断線していました orz

TL866用アダプタ


 今回使ったテストプログラムは下記の4バイトのマシン語です(LOOPラベルへのJP命令でループするので最後のHALTは実行されません)。

テスト用のサンプルプログラム
  0000'                	ASEG
                       
                       	ORG	0
                       
                       	NOP
  0000    08     +     	DB	08H
  0001                 LOOP:	JP	LOOP
  0001    C4     +     	DB	0C4H
  0002    0000   +     	DW	LOOP - 1
                       
                       	HALT
  0004    FF     +     	DB	0FFH
                       
                       
                       	END
 


 下図がジャンプでループしている時のロジアナ画面になります。

JP命令でループ動作中のロジアナ画面


 このジャンプ命令には今回設計したCPUの多くのリソースの制御処理が含まれていて、これが動作したということは多くのリソースが正常動作していると言えると思います。

 ジャンプ命令の具体的な処理は下記になります(まだ最適化は行っていません)。
 因みに、本CPUのバス幅はデータが8bitでアドレスが16bitになります。「SuperSimpleController(その3)」の記事に載せたブロック図を参照してもらうと判り易いと思います。

 条件ジャンプにも対応するために、条件に合致しなかった場合も下記の6項以降は実行されるのでジャンプ命令で指定するアドレスは

   ジャンプ先アドレス - 1

の値を設定するようにしました。

  1. PCをインクリメント
  2. ジャンプ先のLowアドレスをR1にセーブ
  3. PCをインクリメント
  4. ジャンプ先のhighアドレスをPCのhigh側にロード
  5. R1レジスタの内容をPCのlow側にロード
  6. PCをインクリメント
  7. PCが示すメモリの内容をIRにフェッチ

 上記のロジアナ画面でも判るように、ジャンプ命令の実行時間は現状10クロックです。
 また、ジャンプ命令でR1レジスタの内容が壊れてしまいます(どこかで聞いたような・・F8?)

 今回検討中のオリジナルCPUはROM内のuCODE(制御信号の時系列データ)でマシン語の処理内容を記述する方式なので、ロジックICの変更無しにアーキテクチャの変更がある程度できるようになっています。

 参考にuCODEのソースも貼っておきます。
 JP命令の処理部分は下記リストの「CSTART 0C0H  ; **** JP」と書かれた部分を参照してください。

作成中のuCODEリスト
;++++++++++++++++++++++++++++++++++ ; uCODE data for Simple8Z ; made by skyriver ; Ver 0.01 2021/02/04 ;++++++++++++++++++++++++++++++++++ ; Simple8Z control signals bit assign (*:active low) 0001 PLW EQU 01H ; write PCL 0002 PHW EQU 02H ; write PCH 0004 FLW EQU 04H ; write FLG 0008 IRW EQU 08H ;*write IR 0010 PCS0 EQU 10H ; PCL, PHL control(S0) 0020 PCS1 EQU 20H ; PCL, PCH control(S1) 0040 MEM EQU 40H ;*select memoy not I/O 0080 ALUR EQU 80H ;*read ALU 0100 RD EQU 0100H ;*read 0200 WR EQU 0200H ;*write 0400 R0W EQU 0400H ; write R0 0800 R1W EQU 0800H ; write R1 1000 ALD EQU 1000H ; load(latch) addr into ADH, ADL 2000 ALR EQU 2000H ;*read ADL(addr low) 4000 AHR EQU 4000H ;*read ADL(addr high) 8000 PWEN EQU 8000H ; enable PLW,PHW independent Z,C flags ; low active control signals 63C8 NEGSIG EQU (IRW or MEM or ALUR or RD or WR or ALR or AHR) SETHI macro bit ; set sig high SIGNAL set SIGNAL OR bit endm SETLOW macro bit ; set sig low SIGNAL set SIGNAL AND NOT bit endm ACT macro bit ; set sig active IF bit AND NEGSIG SIGNAL set SIGNAL AND NOT bit ELSE SIGNAL set SIGNAL OR bit ENDIF endm CLR macro bit ; clear sig IF bit AND NEGSIG SIGNAL set SIGNAL OR bit ELSE SIGNAL set SIGNAL AND NOT bit ENDIF endm ACTS macro bits ; set control signals to active irp bit,<bits> ACT bit endm endm CLRS macro bits ; set control signals to non active irp bit,<bits> CLR bit endm endm PCCLR macro ; set PC to clear-mode CLR PCS0 CLR PCS1 endm PCDEC macro ; set PC to decrement-mode ACT PCS0 CLR PCS1 endm PCLOAD macro ; set PC to load-mode CLR PCS0 ACT PCS1 endm PCINC macro ; set PC to incriment-mode ACT PCS0 ACT PCS1 endm ; uCode last value inc-mode E2B0 LastVal EQU (NEGSIG OR PCS0 OR PCS1 OR PWEN) AND (NOT RD) AND (NOT IRW) AND (NOT MEM) E2B0 SIGNAL SET LastVal CODE macro ; generate control data DW SIGNAL endm ENDCODE macro ; last code of each proc SIGNAL SET LastVal CODE ; set IR endm CSTART macro code ; uCODE start .xlist rept (code) * 2 - $ DB -1 endm .list ORG (code) * 2 endm 0000' ASEG ORG 0 CSTART 00H ; **** RST PCCLR CLR IRW ; have to clear IRW, for IRH's S0 quickly rise CODE 0000 E288 + DW SIGNAL ACT PLW ; set PCL ACT PHW ; set PCH CODE ; clear PC 0002 E28B + DW SIGNAL CLR PLW CLR PHW ACT RD ; memory read PCINC ; set PC to increment-mode CODE 0004 E2B8 + DW SIGNAL ENDCODE 0006 E2B0 + DW SIGNAL CSTART 08H ; **** NOP CLR IRW ; have to clear IRW at top of uCODE(if not then fetch code again) ACT PLW CODE ; inc addr 0010 E2B9 + DW SIGNAL CODE 0012 E2B9 + DW SIGNAL CODE 0014 E2B9 + DW SIGNAL ENDCODE 0016 E2B0 + DW SIGNAL CSTART 0C0H ; **** JP:0C4H JZ:0C0H JNZ:0C1H JC:0C2H JNC:0C3H CLR IRW ; have to clear IRW at top of uCODE(if not then fetch code again) ACT PLW CODE ; inc PC 0180 E2B9 + DW SIGNAL CLR PLW ACT R1W CODE ; save low addr to R1 0182 EAB8 + DW SIGNAL CLR R1W ACT PLW CODE ; inc PC 0184 E2B9 + DW SIGNAL CLR PLW CLR PWEN ; disable PLW,PHW PCLOAD ; set PC to load-mode CODE ; set load-mode 0186 62A8 + DW SIGNAL ACT PHW CODE ; write high addr to PC 0188 62AA + DW SIGNAL CLR PHW CLR RD ACT ALUR ; read R1 register CODE ; read ALU(R1:low addr) 018A 6328 + DW SIGNAL ACT PLW CODE ; set low addr to PC 018C 6329 + DW SIGNAL ACT PWEN CLR ALUR CLR PLW PCINC ; set PC to increment-mode ACT RD CODE ; set inc-mode, activate RD 018E E2B8 + DW SIGNAL ACT PLW CODE ; inc PC 0190 E2B9 + DW SIGNAL ENDCODE ; latch IR and inc PC 0192 E2B0 + DW SIGNAL CSTART 0F8H ; **** HALT code:F8H-FFH CLR IRW ; have to clear IRW at top of uCODE(if not then fetch code again) CODE ; inc PC 01F0 E2B8 + DW SIGNAL ENDCODE 01F2 E2B0 + DW SIGNAL END Macros: ACT ACTS CLR CLRS CODE CSTART ENDCODE PCCLR PCDEC PCINC PCLOAD SETHI SETLOW Symbols: 4000 AHR 1000 ALD 2000 ALR 0080 ALUR 0004 FLW 0008 IRW E2B0 LASTVAL 0040 MEM 63C8 NEGSIG 0010 PCS0 0020 PCS1 0002 PHW 0001 PLW 8000 PWEN 0400 R0W 0800 R1W 0100 RD E2B0 SIGNAL 0200 WR No Fatal error(s)



★追記 2021/02/15
 uCODEの作成もだいぶ慣れてきたので上記のジャンプ命令の部分を最適化してみました。上記のテスト用サンプルプログラム実行時のロジアナ画面を下図に示します。ジャンプ命令の実行時間は従来の10クロックから8クロックに短縮されました。

 PCのローバイトへの書込み信号であるPCLWが常に変化していることからこれ以上の短縮は難しいと思います。

ジャンプ命令の最適化後のロジアナ画面




[TOP] [ 前へ ] 連載記事 [ 次へ ]

nice!(0)  コメント(0) 

SuperSimpleController(その5)開発環境について [OriginalCPU]

 前回の記事でALUの設計がほぼ完了したのでいよいよブレッドボードでの確認作業に入っています。

 リセット後の初期化処理とアドレス0000hからのマシン語を動かすまでの動作は何とかできたのですが、現状使っているロジアナ(16ch)では一部の信号しか見れず確認作業の効率が悪いです。

 下図はジャンプ命令の動作を確認中のロジアナ画面(制御コードのソースは後述します)のキャプチャで、初期化後、0000hのNOPまでは正常に動ています。次のジャンプ命令で、赤矢印の部分でA0とA1が0になるはずが1のままですorz

動作確認作業中のロジアナ画面サンプル


 マシン語の動作を実現するためにuCODEに入れる16個の制御信号の時系列データを作り、動作確認していく作業はかなり手間がかかりそうなので、まずは環境を整えることにしました。

 現在、できている環境について以下にメモしておきます。

  1. 回路図
     確認作業時に回路図を参照したい場面が頻繁にありますが、複数の部分を見たい時等、CAD表示画面では不便なのでポップアップ表示できるようにしています。

     [uCODE]  [IRH]  [IRL]  [ALU]  [PC]  [AD]  [Reg]

  2. uCODEのコーディング
     前々回の記事でも書きましたが、マクロアセンブラ(M80)のマクロ機能を使ってコーディングしています。
     作成中のサンプルを貼っておきます。

    Simple8Z uCODEソース(作成中)
    ;++++++++++++++++++++++++++++++++++ ; uCODE data for Simple8Z ; made by skyriver ; Ver 0.01 2021/02/04 ;++++++++++++++++++++++++++++++++++ ; Simple8Z control signals bit assign (*:active low) 0001 PLW EQU 01H ; write PCL 0002 PHW EQU 02H ; write PCH 0004 FLW EQU 04H ; write FLG 0008 IRW EQU 08H ;*write IR 0010 PCS0 EQU 10H ; PCL, PHL control(S0) 0020 PCS1 EQU 20H ; PCL, PCH control(S1) 0040 MEM EQU 40H ;*select memoy not I/O 0080 ALUR EQU 80H ;*read ALU 0100 RD EQU 0100H ;*read 0200 WR EQU 0200H ;*write 0400 R0W EQU 0400H ; write R0 0800 R1W EQU 0800H ; write R1 1000 ALD EQU 1000H ; load(latch) addr into ADH, ADL 2000 ALR EQU 2000H ;*read ADL(addr low) 4000 AHR EQU 4000H ;*read ADL(addr high) 8000 PWEN EQU 8000H ; enable PLW,PHW independent Z,C flags ; low active control signals 63C8 NEGSIG EQU (IRW or MEM or ALUR or RD or WR or ALR or AHR) ; uCode last value inc-mode E2B0 LastVal EQU (NEGSIG OR PCS0 OR PCS1 OR PWEN) AND (NOT RD) AND (NOT IRW) AND (NOT MEM) E2B0 SIGNAL SET LastVal SETHI macro bit ; set sig high SIGNAL set SIGNAL OR bit endm SETLOW macro bit ; set sig low SIGNAL set SIGNAL AND NOT bit endm ACT macro bit ; set sig active IF bit AND NEGSIG SIGNAL set SIGNAL AND NOT bit ELSE SIGNAL set SIGNAL OR bit ENDIF endm CLR macro bit ; clear sig IF bit AND NEGSIG SIGNAL set SIGNAL OR bit ELSE SIGNAL set SIGNAL AND NOT bit ENDIF endm PCCLR macro ; set PC to clear-mode CLR PCS0 CLR PCS1 endm PCDEC macro ; set PC to decrement-mode ACT PCS0 CLR PCS1 endm PCLOAD macro ; set PC to load-mode CLR PCS0 ACT PCS1 endm PCINC macro ; set PC to incriment-mode ACT PCS0 ACT PCS1 endm CODE macro ; generate control data DW SIGNAL endm ENDCODE macro ; last code of each proc SIGNAL SET LastVal CODE ; set IR endm CSTART macro code ; uCODE start .xlist rept (code) * 2 - $ DB -1 endm .list ORG (code) * 2 endm 0000' ASEG ORG 0 CSTART 00H ; **** RST PCCLR CLR IRW ; have to clear IRW, for IRH's S0 quickly rise CODE 0000 E288 + DW SIGNAL ACT PLW ; set PCL ACT PHW ; set PCH CODE ; clear PC 0002 E28B + DW SIGNAL CLR PLW CLR PHW ACT RD ; memory read PCINC ; set PC to increment-mode CODE 0004 E2B8 + DW SIGNAL ENDCODE 0006 E2B0 + DW SIGNAL CSTART 08H ; **** NOP CLR IRW ; have to clear IRW at top of uCODE(if not then fetch code again) ACT PLW CODE ; inc addr 0010 E2B9 + DW SIGNAL CODE 0012 E2B9 + DW SIGNAL CODE 0014 E2B9 + DW SIGNAL ENDCODE 0016 E2B0 + DW SIGNAL CSTART 0C0H ; **** JP:0C4H JZ:0C0H JNZ:0C1H JC:0C2H JNC:0C3H CLR IRW ; have to clear IRW at top of uCODE(if not then fetch code again) ACT PLW CODE ; inc PC 0180 E2B9 + DW SIGNAL CLR PLW ACT R1W CODE ; save low addr to R1 0182 EAB8 + DW SIGNAL CLR R1W ACT PLW CODE ; inc PC 0184 E2B9 + DW SIGNAL CLR PLW CLR PWEN ; disable PLW,PHW PCLOAD ; set PC to load-mode CODE ; set load-mode 0186 62A8 + DW SIGNAL ACT PHW CODE ; write high addr to PC 0188 62AA + DW SIGNAL CLR PHW CLR RD ACT ALUR ; read R1 register CODE ; read ALU(R1:low addr) 018A 6328 + DW SIGNAL ACT PLW CODE ; set low addr to PC 018C 6329 + DW SIGNAL ACT PWEN CLR ALUR CLR PLW PCINC ; set PC to increment-mode ACT RD CLR IRW CODE ; set inc-mode, activate RD 018E E2B8 + DW SIGNAL ENDCODE 0190 E2B0 + DW SIGNAL CSTART 0F8H ; **** HALT code:F8H-FFH CLR IRW ; have to clear IRW at top of uCODE(if not then fetch code again) CODE ; inc PC 01F0 E2B8 + DW SIGNAL ENDCODE 01F2 E2B0 + DW SIGNAL END Macros: ACT CLR CODE CSTART ENDCODE PCCLR PCDEC PCINC PCLOAD SETHI SETLOW Symbols: 4000 AHR 1000 ALD 2000 ALR 0080 ALUR 0004 FLW 0008 IRW E2B0 LASTVAL 0040 MEM 63C8 NEGSIG 0010 PCS0 0020 PCS1 0002 PHW 0001 PLW 8000 PWEN 0400 R0W 0800 R1W 0100 RD E2B0 SIGNAL 0200 WR No Fatal error(s)

    ★追記 2021/02/25
     上記のようにuCODEをコーディングしていく際にある程度複雑な処理になると混乱してくるので、もっと抽象度の高い下図のようなニーモニックで処理を書いてからuCODEに変換した方が効率がいいことが判りました。
     機械語の処理内容を記述するためのマイクロコードを書くためのニーモニック(ややこしい)は何と呼んだらいいのでしょう?
     メタニーモニック(1階層下のレベルのニーモニック)とでも言うべきかw

    マイクロコード作成用のメタニーモニック

    ★追記 2021/03/19
     CALLとRETの度にR0とR1が壊れてはプログラムの実行効率が悪くなるので、R0は非破壊になるように改善しました。
     マイクロコード作成上のテクニックを垣間見ることができそうなのでメモとして貼っておきます。

    CALL(PUSH PC)とRETの改善版メタニーモニック


  3. テスト用マシン語コード
     これもマクロアセンブラを使い、コーディングしていく予定です。
     冒頭で書いたロジアナ画面での確認対象のソースは下記になります。

    テスト用サンプルソース
    ;++++++++++++++++++++++++++++++++++++++++ ; Simle8Z test program ; by skyriver ; V0.01 2021/02/08 ;++++++++++++++++++++++++++++++++++++++++ RST macro DB 00H endm NOP macro DB 08H endm JP macro adr DB 0C4H DW adr - 1 ; because increment PC after set PC endm JZ macro adr DB 0C0H DW adr - 1 endm JNZ macro adr DB 0C1H DW adr - 1 endm JC macro adr DB 0C2H DW adr - 1 endm JNC macro adr DB 0C3H DW adr - 1 endm HALT macro DB 0FFH endm ;------------------------------ 0000' ASEG ORG 0 NOP 0000 08 + DB 08H 0001 LOOP: JP LOOP 0001 C4 + DB 0C4H 0002 0000 + DW LOOP - 1 ; because increment PC after set PC HALT 0004 FF + DB 0FFH END Macros: HALT JC JNC JNZ JP JZ NOP RST Symbols: 0001 LOOP No Fatal error(s)


  4. 任意時点でのブレーク
     動作確認は基本的にはロジアナを使って確認していますが、冒頭に書いたようにある部分で想定外の状況になった場合、調査し易くするためにリセット信号とクロックをPICから供給し、任意のクロックで動作停止できる環境を作りました。

     冒頭のロジアナ画面の赤矢印の時点で停止した状態のロジアナ画面が下図になります。この状態で止まっているのでじっくり調べることができます。

    任意時点でのブレーク例

     リセットとクロック信号は「I2Cロガーの製作」の記事で書いたI2Cロガー(以前2台目として作成した物で記事とは若干違う)を使って生成しています。

    I2Cロガー(コネクタ側) I2Cロガー(USB、LED側)

    I2Cロガー(基板:部品面) I2Cロガー(基板:半田面)

     自作のセルフコンパイラであるpicle言語が動作するのでこのようなちょっとした制御には(私にとっては)最適です。

     ソースも貼っておきます。

    試験用クロック&リセット信号ジェネレータ(picle言語)
    # Clock and Reset Generator for Simple8Z test 2021/02/25 ver 0.04 # by skyriver var RegLATA,RegLATB; var Reset,Clock,Led; proc SigOn( bit ) { RegLATB[0] = RegLATB[0] | bit; } proc SigOff( bit ) { RegLATB[0] = RegLATB[0] & ~bit; } proc Init() { var Ad1pcfg; Ad1pcfg = $032c; Ad1pcfg[0] = $ffff; # set digital mode RegLATA = $02c4; RegLATB = $02cc; RegLATB[-2] = $6f7f; Reset = $8000; Clock = $1000; Led = $0080; } proc Twait( tim ) { Timer_ = tim; while ( Timer_ ) {} } proc OneClock( tim ) { Twait( tim ); SigOn( Clock ); Twait( tim ); SigOff( Clock ); } proc DispCnt( cnt ) { PrnChar_( $1b ); PrnStr_( "[57Cs" ); PrnDec_( cnt ); PrnStr_( " " ); } proc main() { var tim,until,cnt; tim = 1; Init(); SigOn( Reset ); SigOff( Clock ); PrnStr_( "Clock & Reset generator for Simple8Z debug\n" ); while (1) { SigOff( Led ); PrnStr_( "\nHow many clocks(-2:cont,-1:stop,0:step,minus:cont step) = " ); until = Input_(); if ( until = -1 ) { break; } else if ( until = -2 ) { PrnStr_( "\n** sending clock, ESC:break" ); while (1) { asm_( $A982CD ); # SigOff( Clock ); if ( InpChk_() ) { if ( InpChar_() = $1B ) { break; } } asm_( $A882CD ); # SigOn( Clock ); } } else if ( until < -2 ) { cnt = cnt - until; while ( until < 0 ) { OneClock( tim ); until = until + 1; PrnStr_( "" ); } DispCnt( cnt ); } else if ( until = 0 ) { OneClock( tim ); cnt = cnt + 1; DispCnt( cnt ); } else { SigOff( Reset ); SigOn( Led ); OneClock( tim ); Twait( tim ); SigOn( Reset ); for ( cnt = 0; cnt < until; cnt=cnt+1 ) { OneClock( tim ); PrnStr_( "" ); } } } }
    ★変更 2021/03/27
     Ver 0.04 高速(1MHz)実行機能を追加
    ★変更 2021/02/25
     Ver0.03 継続実行機能を追加
    ★変更 2021/02/24
     Ver0.02 ステップ動作機能を追加


  5. 信号レベルの確認
     調査したい状態でハードをブレークした後、信号のレベルチェックするために数十年前に購入したロジックテスタを使っています。
     長い間、使用しなかったのですが、電池を入れ替えたら問題なく動作しました。
     High/LowレベルはLEDの点灯で判るし、パルスが発生している場合もLED表示できます。

    かなり前に購入したロジックテスタ


  6. LED表示  ★追記 2021/02/12
     今後の確認作業を効率化するためにLED表示により信号レベルを確認できるようにしました。
     ブレッドボードには空スペースがほとんどないのでLED表示を小型化するために表面実装タイプのLEDと抵抗を使っています。
     基板は秋月さんから購入したSMDプロトタイピングガラスユニバーサル基板(0.3mm厚)Cタイプというものを使ってみました。フレキシブルで簡単に切断できるので使い易いです。
     パターン間隔が2.54mmではないのでピンヘッダの取付は8ピンくらいまでが限界です。
     抑える場所が狭かったので最初は2回に分けて半田付けしましたが、2個目はまとめて付けました(今回は2個作成しています)。

    クリーム半田塗布後 ヒートガンで半田付け後

    完成写真(部品面) 完成写真(裏面)


     ブレッドボードに実装した状態が下の写真になります。配線で隠れてちょっと見辛いけどロジックテスタで当たるよりは遥かに効率的です。

    R0,R1レジスタのLED表示


    ★追記 2021/02/20
     確認作業効率アップのためにアドレスバスもLDE表示するようにしました。
     今回は下の写真のSolder Flux Needle Booster(左下)を使ってみました。  半田ペーストの出力制御がちょっと難しい(タイムラグが結構あるし、手からの体温で温まって出てくる)ですが楊枝でつけるよりは効率的になりました。
     右下が半田ペースト塗布後の写真でだいたい思ったように塗れました。

    Solder Flux Needle Booster 半田ペースト塗布後

     下の写真のようにアドレスバスもLED表示できるようになりました。

    追加したアドレスバスのLED表示


    ★追記 2021/03/09
     追加してから2週間くらい経ちましたがデータバスもLED表示するようにしたので写真を貼っておきます。

    データバスのLED表示(中央手前)追加後のブレッドボード



[TOP] [ 前へ ] 連載記事 [ 次へ ]

nice!(0)  コメント(0) 

SuperSimpleController(その4)ALUの検討 [OriginalCPU]

 前回の記事で今回検討中のオリジナルCPUのブロック図を書いてALUの機能指定ビット数を3bitから4bitに増やしたことを書きましたが、この状態でも今回使うUV-EPROM(M27C322:2M x 16bit)の半分しか使っていません。
 もったいないので加減算とシフト命令にキャリーフラグ付きのものを加えることにしました。

 手法としては至って簡単でIR(インストラクションレジスタ)からALUに渡している4bitの機能指定ビットの上位にキャリーフラグを追加して機能指定ビットを5bitにするだけです。回路図の抜粋が下図になります。

オリジナルCPU回路のALU部抜粋


 機能指定ビットに対する機能も下表のように見直しました。キャリーが0の場合と1の場合の2面必要になるのでM27C322の容量すべてを使用することになります。下表の2項演算子の演算順序はインテル系のアセンブラと同じです(ex. OR R0,R1 : R0=R0 OR R1)。
 CP(コンペア)ではレジスタを変更する必要が無いのでALUの出力はゼロとし、レジスタをゼロクリアする時に利用することにしました。

No.addresscontents
000000h - 0FFFFhR0
110000h - 1FFFFhAND R0,R1
220000h - 2FFFFhOR R0,R1
330000h - 3FFFFhXOR R0,R1
440000h - 4FFFFhADD R0,R1
550000h - 5FFFFhADC R0,R1
660000h - 6FFFFhSUB R0,R1
770000h - 7FFFFhSBC R0,R1
880000h - 8FFFFhSHL R0 (shift left)
990000h - 9FFFFhSHLC R0 (shift left with carry)
AA0000h - AFFFFhSHR R0 (shift right)
BB0000h - BFFFFhSHRC R0 (shift right with carry)
CC0000h - CFFFFhDEC R0
DD0000h - DFFFFhINC R0
EE0000h - EFFFFhCP R0,R1 (output is zero)
FF0000h - FFFFFhR1
★変更 2021/02/13
 マシン語構成の検討からの反映で機能指定ビットのアサインを変更


 UV-EPROMの書込みはTL866II Plusを使用しています。
 このROMライタはM27C4096の容量までしか対応していない(これより大きいものは40ピンソケットでは足りない)のでM27C322まで対応しているM27C4096変換アダプタを使って書き込んでいます。

TL866II Plusでの書込み状況


 書き込み中の画面キャプチャを参考に貼っておきます。
 今回使用しているROMはST社のもので、TL866でのROMタイプはuPD27C4096を選択しています(他のいくつかのメーカーのM27C4096でも試しましたがNECのuPDタイプで書込みができました)
 赤のワーニングマーカー?が表示されますがライト及びリード操作で問題は発生しませんでした。

TL866II Plusでの書込み画面(書き込み中)


 今回使用しているM27C322はM27C4096の8倍の容量なので、8回に分けて書込み操作をする必要があります。下図が書込み終了後の画面表示例で各処理の所要時間が表示れさており、書込みには138秒かかっています。
 書込み操作は8回必要になるので全体で20分程度の時間がかかります。

TL866II Plusでの書込み画面(書き込み完了時)


 肝心のALU内に格納するデータについてですが、作成するのが面倒かもと思っていましたが、割合簡単にサクッとできました。
 TL866はHEXファイルの他にバイナリファイルも読込めるので、1回の書込みサイズに分割したバイナリファイルを生成するツールを作成しました。
 機能と生成ファイルとの関係は関数テーブルで保持することで管理しています。ソースファイルも貼っておきます。

Simple8Z ALUデータ生成プログラム(C言語)
/**************************** Simple8Z ALU data generator made by skyriver V0.04 2021/03/13 add Cf to R1 V0.03 2021/02/12 ****************************/ #include <stdio.h> #define FNAME "ALU4_%03d.bin" #define FlagZ 0x01 #define FlagC 0x02 void IncR0( FILE * ); void DecR0( FILE * ); void ShlR0( FILE * ); void ShlCR0( FILE * ); void ShrR0( FILE * ); void ShrCR0( FILE * ); void And( FILE * ); void Or( FILE * ); void Xor( FILE * ); void Add( FILE * ); void Adc( FILE * ); void Sub( FILE * ); void Sbc( FILE * ); void Cp( FILE * ); void R0( FILE * ); void R1( FILE * ); char Buf[ 128 ]; void (*func[])( FILE * ) = { R0, And, Or, Xor , Add, Add, Sub, Sub , ShlR0, ShlR0,ShrR0, ShrR0 , DecR0, IncR0, Cp, R1 , R0, And, Or, Xor , Add, Adc, Sub, Sbc , ShlR0,ShlCR0,ShrR0,ShrCR0 , DecR0, IncR0, Cp, R1 }; #define FuncNum ( sizeof(func) / sizeof( void(*)() ) ) #define ROM_BLK 4 // How many functions in a Rom block void DecR0( FILE *fp ) { int adr, r0, r1, ans; for( adr = 0; adr < 0x10000; adr++ ) { r0 = adr & 0xff; r1 = ( adr & 0xff00 ) >> 8; ans = r0 -1; putc( (char)ans, fp ); putc( ((char)ans == 0 ? FlagZ : 0) | ((ans & 0x0100) == 0 ? 0 : FlagC), fp ); } } void IncR0( FILE *fp ) { int adr, r0, r1, ans; for( adr = 0; adr < 0x10000; adr++ ) { r0 = adr & 0xff; r1 = ( adr & 0xff00 ) >> 8; ans = r0 + 1; putc( (char)ans, fp ); putc( ((char)ans == 0 ? FlagZ : 0) | ((ans & 0x0100) == 0 ? 0 : FlagC), fp ); } } void Add( FILE *fp ) { int adr, r0, r1, ans; for( adr = 0; adr < 0x10000; adr++ ) { r0 = adr & 0xff; r1 = ( adr & 0xff00 ) >> 8; ans = r0 + r1; putc( (char)ans, fp ); putc( ((char)ans == 0 ? FlagZ : 0) | ((ans & 0x0100) == 0 ? 0 : FlagC), fp ); } } void Adc( FILE *fp ) { int adr, r0, r1, ans; for( adr = 0; adr < 0x10000; adr++ ) { r0 = adr & 0xff; r1 = ( adr & 0xff00 ) >> 8; ans = r0 + r1 + 1; putc( (char)ans, fp ); putc( ((char)ans == 0 ? FlagZ : 0) | ((ans & 0x0100) == 0 ? 0 : FlagC), fp ); } } void Sub( FILE *fp ) { int adr, r0, r1, ans; for( adr = 0; adr < 0x10000; adr++ ) { r0 = adr & 0xff; r1 = ( adr & 0xff00 ) >> 8; ans = r0 - r1; putc( (char)ans, fp ); putc( ((char)ans == 0 ? FlagZ : 0) | ((ans & 0x0100) == 0 ? 0 : FlagC), fp ); } } void Sbc( FILE *fp ) { int adr, r0, r1, ans; for( adr = 0; adr < 0x10000; adr++ ) { r0 = adr & 0xff; r1 = ( adr & 0xff00 ) >> 8; ans = r0 - r1 - 1; putc( (char)ans, fp ); putc( ((char)ans == 0 ? FlagZ : 0) | ((ans & 0x0100) == 0 ? 0 : FlagC), fp ); } } void ShlR0( FILE *fp ) { int adr, r0, r1, ans; for( adr = 0; adr < 0x10000; adr++ ) { r0 = adr & 0xff; r1 = ( adr & 0xff00 ) >> 8; ans = r0 << 1; putc( (char)ans, fp ); putc( ((char)ans == 0 ? FlagZ : 0) | ((ans & 0x0100) == 0 ? 0 : FlagC), fp ); } } void ShlCR0( FILE *fp ) { int adr, r0, r1, ans; for( adr = 0; adr < 0x10000; adr++ ) { r0 = adr & 0xff; r1 = ( adr & 0xff00 ) >> 8; ans = ( r0 << 1 ) | 0x01; putc( (char)ans, fp ); putc( ((char)ans == 0 ? FlagZ : 0) | ((ans & 0x0100) == 0 ? 0 : FlagC), fp ); } } void ShrR0( FILE *fp ) { int adr, r0, r1, ans; for( adr = 0; adr < 0x10000; adr++ ) { r0 = adr & 0xff; r1 = ( adr & 0xff00 ) >> 8; ans = r0 >> 1; putc( (char)ans, fp ); putc( ((char)ans == 0 ? FlagZ : 0) | ((r0 & 1) == 0 ? 0 : FlagC), fp ); } } void ShrCR0( FILE *fp ) { int adr, r0, r1, ans; for( adr = 0; adr < 0x10000; adr++ ) { r0 = adr & 0xff; r1 = ( adr & 0xff00 ) >> 8; ans = ( r0 >> 1) | 0x80; putc( (char)ans, fp ); putc( ((char)ans == 0 ? FlagZ : 0) | ((r0 & 1) == 0 ? 0 : FlagC), fp ); } } void And( FILE *fp ) { int adr, r0, r1, ans; for( adr = 0; adr < 0x10000; adr++ ) { r0 = adr & 0xff; r1 = ( adr & 0xff00 ) >> 8; ans = r0 & r1; putc( (char)ans, fp ); putc( ans == 0 ? FlagZ : 0, fp ); } } void Or( FILE *fp ) { int adr, r0, r1, ans; for( adr = 0; adr < 0x10000; adr++ ) { r0 = adr & 0xff; r1 = ( adr & 0xff00 ) >> 8; ans = r0 | r1; putc( (char)ans, fp ); putc( ans == 0 ? FlagZ : 0, fp ); } } void Xor( FILE *fp ) { int adr, r0, r1, ans; for( adr = 0; adr < 0x10000; adr++ ) { r0 = adr & 0xff; r1 = ( adr & 0xff00 ) >> 8; ans = r0 ^ r1; putc( (char)ans, fp ); putc( ans == 0 ? FlagZ : 0, fp ); } } void Cp( FILE *fp ) { int adr, r0, r1, ans; for( adr = 0; adr < 0x10000; adr++ ) { r0 = adr & 0xff; r1 = ( adr & 0xff00 ) >> 8; ans = r0 - r1; putc( 0, fp ); putc( ((char)ans == 0 ? FlagZ : 0) | ((ans & 0x0100) == 0 ? 0 : FlagC), fp ); } } void R0( FILE *fp ) { int adr, r0, r1, ans;; for( adr = 0; adr < 0x10000; adr++ ) { r0 = adr & 0xff; r1 = ( adr & 0xff00 ) >> 8; ans = r0; putc( (char)ans, fp ); putc( ans == 0 ? FlagZ : 0, fp ); } } void R1( FILE *fp ) { int adr, r0, r1, ans;; for( adr = 0; adr < 0x10000; adr++ ) { r0 = adr & 0xff; r1 = ( adr & 0xff00 ) >> 8; ans = r1; putc( (char)ans, fp ); putc( FlagC | ( ans == 0 ? FlagZ : 0 ), fp ); // add Cf } } int main( int argc, char *argv[] ) { FILE *fp; int i,j; int fidx = 0; for( i = 0; i < (FuncNum / ROM_BLK); i++ ) { sprintf( Buf, FNAME, i ); if( ( fp = fopen( Buf, "wb" ) ) == NULL ) { fprintf( stderr, "can not open %s\n", Buf ); break; } else { for( j = 0; j < ROM_BLK; j++ ) { func[ fidx++ ]( fp ); } fclose( fp ); fprintf( stderr, "%s is generated\n", Buf ); } } return( 0 ); }
★変更 2021/03/13
 R1()でCFをセットするように変更
★変更 2021/02/13
 機能指定ビットのアサインの変更に伴いソースをアップデート



[TOP] [ 前へ ] 連載記事 [ 次へ ]

nice!(0)  コメント(0)