SSブログ
English Version

Z80GALの構想(その5)タイマ割込みによるシリアル通信 [Z80]

 前回の記事で書いたようにGAME言語を使ったモニタができて開発に必要な環境が整ってきたので今回はタイマ割込みを使ったシリアル通信の実現に関して書いてみます。

 結論から先に書くと今回のタイマ割込みによるシリアル通信の実装により、バックグランドでの受信が可能となり、目標の一つであるUART用のICを使わないということが達成できました(Z80GALの設計ポリシーは他のマイコンに制御されない「最小限のIC構成」のZ80ボードの実現です)

 これまでは「Z80GALの構想(その3)ブレッドボードでの仮組」の記事に書いたようにソフトウェアでタイミングを計ったシリアル通信(開発環境を整えるための仮の実装)を使っていましたが、キーセンスができないため、GAME言語でリスト出力時の途中停止等ができませんでした。またソースをコピペする場合でもTeraTermで 1ms/char のウェイトを入れていました。
 今回、タイマ割込み処理化したことでリスト表示を途中で停止できるようになり、0ms/char の設定でもソースのコピペで取りこぼしが発生しないようになりました(ライン毎のウェイトは余裕を見て50ms/lineで設定)。

 それではタイマ割込みによるシリアル通信について以下に概要をメモしておきます。

  1. 割込み信号源
     この連載の初回「Z80GALの構想」で書いたように当初はGALでタイマ割込み信号を生成しようと試みましたが、GALのリソースをかなり使ってしまうのでPIC12F683を割込み信号の生成と水晶発振(20MHz)用バッファとして使うことにしました。
     8ピンなので74HCU04等で発信させるよりもハードがコンパクトにまとまります。

     Z80の割込みモードは最も単純なモード1(割込みエントリーアドレスが38H固定)を使いました。
     汎用性を持たせるため

    • タイマ割込み信号は疑似的にオープンドレイン出力とし、ワイヤードORで他の割込みも後付け可能
    • タイマ割込み信号の状態をGAL経由でZ80から読込み可能

    としています。

     Z80側から割込み要求をクリアできればいいのですがGALのリソースを節約するためにタイマ割込み要求は一定時間でクリアされるようにしました。
     このため、割込み処理先頭でタイマ割込み要求を確認でき、割込み処理を抜ける時点では要求信号がクリアされるように調整する必要があります。

     下図は割込み発生時のシンクロ画面で黄色(ch1)がタイマ割込み要求信号で水色(ch2)が割込み処理時間を確認するために割込み処置中に出力しているSDO信号(SD用に用意したビット出力)です。
     タイマ割込み要求信号(黄色)は割込み処理先頭ではアクティブで割込みを抜ける時点ではインアクティブになっています。

    タイマ割込み信号と割込み処理時間

     この時の割込み処理は下のリストのようにPush/popだけのダミー処理です。

    上図確認時の割込み処理
    ;************************************* ; timer int width test ; Ver 0.01 2020/10/31 by skyriver ;************************************* ;*** Z80GAL I/O address *** 0080 adBASE EQU 080H ; I/O base address 00E0 adRAM EQU (adBASE + 60H) ; select memory RD D0=1:RAM, 0:ROM 00C0 adSdCs EQU (adBASE + 40H) ; set SD's CS D0=1:active 00A0 AdSd EQU (adBASE + 20H) ; write D0:SDO D1:CLK, read D0:SDI D1:CS 0080 AdSeri EQU (adBASE + 00H) ; serial D0 write:Tx read:Rx 0009 RGETCAD EQU 0008H + 1 ; getc address 000D RKBHTAD EQU 000CH + 1 ; kbhit address 0011 RPUTCAD EQU 0010H + 1 ; putc address 0039 TINTADR EQU 0038H + 1 ; timer interrupt jump address .Z80 0000' ASEG ORG 5000H 5000 F3 START: DI 5001 ED 56 IM 1 ; set int mode 5003 21 500C LD HL,TINT 5006 22 0039 LD (TINTADR),HL 5009 FB EI 500A 18 FE JR $ 500C F5 TINT: PUSH AF ; 11 500D 3E 01 LD A,1 ; 7 500F D3 A0 OUT (AdSd),A ; 11 5011 C5 PUSH BC ; 11 5012 D5 PUSH DE ; 11 5013 E5 PUSH HL ; 11 5014 E1 POP HL ; 10 5015 D1 POP DE ; 10 5016 C1 POP BC ; 10 5017 AF XOR A ; 4 5018 D3 A0 OUT (AdSd),A ; 11 501A F1 POP AF ; 10 501B FB EI ; 4 501C C9 RET ; 10 END

     PIC12F683のソースも貼っておきます。コンパイラはCC5X C Compilerを使用しています。

    PIC12F683のソース
    /* Clock & IntReq signal genelator for Z80 board * PIC12F683 * Ver 0.01 made by skyriver 2020/10/20 * Ver 0.02 by skyriver 2020/10/25 * * timer int req(GPO0) manage like open draon. * * output timer int status to GP1 */ #include <INT16CXX.H> // define config bit assign(PIC12F683) #define conf_FCMEN (1<<11) // Fail-Safe Clock Monitor Enable #define conf_IESO (1<<10) // Internal External Switchover bit #define conf_BODEN1 (1<<9) // Brown-out Detect Selection bit #define conf_BODEN0 (1<<8) // Brown-out Detect Selection bit #define conf_CPD_ (1<<7) // Data code Protection(0:enable) #define conf_CP_ (1<<6) // Code Protection(0:enable) #define conf_MCLRE (1<<5) // GP3/MCLR_ pin function secet bit(1:MCLR) #define conf_PWRTEN_ (1<<4) // Power-up Timer Enablr bit(0:enable) #define conf_WDTEN (1<<3) // Watchdog Timer enable bit(1:enable) #define conf_FOSC_RC 0b111 #define conf_FOSC_RCIO 0b110 #define conf_FOSC_INTOSC 0b101 #define conf_FOSC_INTOSCIO 0b100 #define conf_FOSC_EC 0b011 #define conf_FOSC_HS 0b010 #define conf_FOSC_XT 0b001 #define conf_FOSC_LP 0b000 #pragma config = conf_CPD_ | conf_CP_ | conf_FOSC_HS /* initialize H/W and workarea */ void init( void ) { GPIO = 0b00000000; // GP0:IntReq WPU = 0b00000000; // 1:Pull-up enable TRISIO = 0b00000001; // 0:output, 1:input CMCON0 = 0b00000111; // Comparator off CMCON1 = 0b00000011; // comp output is asynchronization VRCON = 0b00001000; // Vref is off ANSEL = 0b00000000; // 0:dig, 1:ana OSCCON = 0b01110000; // external clock OPTION = 0b00000010; // not PortB_PullUp PreScaleToTMR0(1/8) // 1000000/(9600*3) = 34.72[uSec] // 20MHz : 1/20[usec] // division 1000000/(9600*3)/(1/20) = 694.4 -> 1/4 : 173.61 T2CON = 0b0000100; // Timer1 on,pre/post scaler 1:1 PR2 = 172; PCON = 0b00000000; } void main( void ) { init(); while ( 1 ) { while ( TMR2IF == 0 ) {} TRISIO.0 = 0; // set timer reqest to 0 GPIO = 2; // GPIO.0 = 0, GOPI.1 = 1 nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); #if 0 nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); nop2(); #endif TRISIO.0 = 1; // set input mode(external pull up) GPIO = 0; // set timer int status to 0 TMR2IF = 0; } }


  2. シリアル通信処理
     通信速度は9600bspとし、タイマ割込みは通信速度の3倍で掛けています。
     受信時のスタートビットをエッジ割込みで拾えればタイマ割込み間隔は通信速度と同じでいいのですが、タイマ割込みだけで処理するためにスタートビットを3倍速でサンプリングするようにしました。

     このため、タイマ割込みの間隔は34.7us(=1000000/(9600x3))になるので20MHz動作のZ80でも悠長なコードは書けません^^

     下図が'A'を受信してエコーバックしている際のサンプル波形です。上側のTx信号がタイマ割込みシリアル通信の出力信号で1bit幅が104usになっています。
     9600bpsの1bt幅は104.17us(=1000000/9600)なので想定通りであることが確認できました。

    タイマ割込みシリアル通信サンプル波形


  3. 直近の予定
     次はいよいよソフトウェアによるSPIインターフェースの実装です。この部分は「レトロマイコン86ボードの構想(その23)16MHz動作実験3」でも評価中の 5V <--> 3.3V のレベル変換の実験も必要になります。


★追記 2020/11/01 {
 タイマ割込み処理の速度への影響が気になったので「MSX-DOS上でのGAME言語のベンチマーク」の記事で書いたベンチマークで確かめてみました。

 GAME言語インタープリタでの実行時間は

   従来のソフトシリアル  : 33秒
   タイマ割込みシリアル時 : 44秒 43秒 ※若干高速化

でタイマ割込み処理により3割(1.30=43/33)程度、遅くなっています。

 CP/M-80 GAME80 Interpreter(Pic24CPM Z80:16MHz)が52秒なのでクロック比例計算すると20MHzの場合は41.6秒になりますが「従来のソフトシリアル」ではキー入力チェックがダミー(OR A:RET)なので速いのだと思います。
 タイマ割込みシリアルではキー入力チェックがバッファ内受信データのカウンタチェックだけなのでそれなりに速いですね^^

 尚、処理速度にはあまり関係ないですが、タイマ割込みシリアルの場合、シリアル送信も割込みで処理しています(ソフトシリアル送信はタイマ割込みによるジッタがでて使えなかった)。
}



[TOP] [ 前へ ] 連載記事 [ 次へ ]
nice!(0)  コメント(0) 
共通テーマ:趣味・カルチャー

Z80GALの構想(その4)簡易モニタの製作 [Z80]

 GALを使用したZ80ボードについて前回の記事ではソフトウェアシリアルを実装し、GAME言語を移植したところまで書きました。

 今回はHexファイルローダーと簡易モニタ(Gamon)の製作について書いてみます。
 前回記事の最後の方に、まずはHexファイルをロードできる環境が必要であることを書きましたが、メモリ64KBの8bit系の場合、HEXファイルは非常に単純なのでアセンブラでサクッとできました。

 リストを以下に貼りました。GetcとPutcが前回記事で書いたソフトウェアシリアルの1文字入出力処理でPutsは文字列出力処理です。

Hexファイルローダー
C ;********************************* C ; HexLoader for Z80 C ; Ver0.01 2020/10/26 by skyriver C ;********************************* C C 0100 21 015E C HexLd: LD HL,HexMsg 0103 CD 0064 C CALL Puts C 0106 CD 0010 C LOAD: CALL Getc 0109 FE 3A C CP ':' 010B 20 F9 C JR NZ,LOAD C 010D CD 0148 C CALL RDBYTE ; get byte count 0110 47 C LD B,A 0111 CD 0155 C CALL RDWORD ; get address 0114 CD 0148 C CALL RDBYTE ; get record type 0117 5F C LD E,A ; save type 0118 FE 01 C CP 1 ; check end of data 011A 28 15 C JR Z,DSPTYP C 011C B7 C OR A ; check data record or not 011D 20 0C C JR NZ,LdErr C 011F CD 0148 C RDDATA: CALL RDBYTE 0122 77 C LD (HL),A 0123 23 C INC HL 0124 10 F9 C DJNZ RDDATA C 0126 CD 0131 C CALL DSPTYP 0129 18 DB C JR LOAD C 012B 21 0170 C LdErr: LD HL,ErrMsg ; error occur 012E C3 0064 C JP Puts C C 0131 CD 0148 C DSPTYP: CALL RDBYTE ; read check sum 0134 CD 0010 C CALL Getc ; read delimitor 0137 7B C LD A,E ; get type 0138 C6 30 C ADD A,'0' 013A C3 0008 C JP Putc C C C ; read hex nible C ; A -> data C 013D CD 0010 C RDNBL: CALL Getc 0140 D6 30 C SUB '0' 0142 FE 0A C CP 10 0144 D8 C RET C C 0145 C6 F9 C ADD A,10 + '0' - 'A' 0147 C9 C RET C C C ; read byte data C ; A -> byte data C 0148 CD 013D C RDBYTE: CALL RDNBL 014B 07 C RLCA 014C 07 C RLCA 014D 07 C RLCA 014E 07 C RLCA 014F 4F C LD C,A 0150 CD 013D C CALL RDNBL 0153 B1 C OR C 0154 C9 C RET C C C ; read hex word C ; HL -> data C 0155 CD 0148 C RDWORD: CALL RDBYTE 0158 67 C LD H,A 0159 CD 0148 C CALL RDBYTE 015C 6F C LD L,A 015D C9 C RET C 015E 48 65 78 4C C HexMsg: DB 'HexLoader Start',13,10,0 0162 6F 61 64 65 C 0166 72 20 53 74 C 016A 61 72 74 0D C 016E 0A 00 C 0170 20 65 72 72 C ErrMsg: DB ' err!',13,10,0 0174 21 0D 0A 00 C C


 GAME言語を移植済みなのでメモリダンプ等をGAME言語で作成しているうちに必要最小限のメモリ操作とHexファイルリード/ライト機能が揃い、簡易モニタが出来てしまいました。
 最初から作ろうと思って設計したわけではないので若干力づくの部分等もありますが、100行程度のソースで手軽に簡易モニタが作れてしまうのは流石GAME言語と言ったところですね。

 この簡易モニタはGAME言語で作ったのでGamonと命名しました。
 GAME言語はアセンブラよりは高級言語なので手軽に変更できて移植性も高いのではないかと思います。

 下記の操作例の中のヘルプ表示部分で実装コマンドが判りますが、ReadとWriteコマンドはHexファイルの読み書き用コマンドでRead処理だけは読みこぼし対策として上記のようにアセンブラで作っています。

 また、Hexファイルのロード中は一行処理毎にレコードタイプを表示しています。

Gamon操作例
*READY :#=1 ** Gamon(Game Monitor) V0.01 2020/10/27 by skyriver ?:Help ** ]? DUMP: Daaaa,aaaa FILL : Faaaa,aaaa,dd GO : Gaaaa SET : Saaaa IN ; Iaa OUT : Oaa,dd READ: R WRITE: Waaaa,aaaa QUIT: Q HELP : ? ]fc000,c03f,e5 ]d89abc000,c048 C000 : E5 E5 E5 E5 E5 E5 E5 E5 - E5 E5 E5 E5 E5 E5 E5 E5 ........ - ........ C010 : E5 E5 E5 E5 E5 E5 E5 E5 - E5 E5 E5 E5 E5 E5 E5 E5 ........ - ........ C020 : E5 E5 E5 E5 E5 E5 E5 E5 - E5 E5 E5 E5 E5 E5 E5 E5 ........ - ........ C030 : E5 E5 E5 E5 E5 E5 E5 E5 - E5 E5 E5 E5 E5 E5 E5 E5 ........ - ........ C040 : 30 31 32 33 34 35 36 37 - 38 01234567 - 8 ]sc000 C000 E5 - 1 C001 E5 - 2 C002 E5 - C003 E5 - - C002 E5 - 3 C003 E5 - . ]dc000,c00f C000 : 01 02 03 E5 E5 E5 E5 E5 - E5 E5 E5 E5 E5 E5 E5 E5 ........ - ........ ]r HexLoader Start 0001 ]dc000,c03f C000 : 30 31 32 33 34 35 36 37 - 38 39 41 42 43 44 45 46 01234567 - 89ABCDEF C010 : 00 01 02 03 04 05 06 07 - 08 09 0A 0B 0C 0D 0E 0F ........ - ........ C020 : 30 31 32 33 34 35 36 37 - 38 39 41 42 43 44 45 46 01234567 - 89ABCDEF C030 : 00 01 02 03 04 05 06 07 - 08 09 0A 0B 0C 0D 0E 0F ........ - ........ ]


 最後に現時点でのGamonソースを貼っておきます。

Gamonソース(GAME言語)
1' simple monitor(Gamon) V002 2020/10/30 by skyriver 10 #=10000 100'*** init 110 M=&+1 120 M:1)=$DB M:3)=$32 M(2)=M M:6)=$C9 130 M:7)=$3E M:9)=$D3 M:11)=$C9 140 M:12)=$3E M:14)=$E6 M:15)=$0F M(8)=$3200 M(9)=M M:20)=$C9 150 M:21)=$3E M:23)=$0F M(12)=$0F0F M(13)=$320F M(14)=M M:30)=$C9 160 B=M+31 Y=B+256 170 /"** Gamon(Game Monitor) V0.02 2020/10/30 by skyriver H:Help **"// 180 ] 200'*** get line 210 P=B Q=B $=Z 220 @ 230 C=$ ;=C<>8 #=260 240 ;=Q=B $=Z #=260 250 Q=Q-1 " " $=8 260 ;=(C=$18)*(Q>B) @ $=8 " " $=8 Q=Q-1 @=(Q=B) 262 ;=Q-B>253 $=8 " " $=8 #=280 270 ;=C>=" " Q:0)=C Q=Q+1 280 @=(C=13) 290 ] 300'** Getc from buf 310 C=0 ;=P<Q C=P:0) P=P+1 ;=(C>="a")*(C<="z") C=C-("a"-"A") 320 ] 400'** Get Hex byte D-> 0..15, -1:NG 410 D=-1 420 !=300 430 ;=(C>="0")*(C<="9") D=C-"0" 440 ;=(C>="A")*(C<="F") D=C-"A"+10 450 ] 500'** Get value A->value F->1:OK,0:NG 510 A=0 F=0 520 @ 530 !=400 ;=D>=0 A=16*A+D F=1 540 @=(D=-1) 550 ] 600'** PutHex byte D<-data,G<-sum 610 M:22)=D >=M+21 M:13)=M:0) >=M+12 620 M:13)=M:0) >=M+12 C=M:0) 630 C=C+"0" ;=C>"9" C=C+"A"-"0"-10 640 $=C 650 M:13)=D >=M+12 C=M:0) 660 C=C+"0" ;=C>"9" C=C+"A"-"0"-10 670 $=C G=G+D 680 ] 1000'** dump 1010 ;=P<Q !=500 S=A 1020 E=S+$7F ;=P<Q !=500 ;=A>S E=A 1030 @ ??=S " :" 1040 I=0,15 1050 " " 1060 ;=S+I>E " " #=1080 1070 ?$=S:I) 1080 ;=I=7 " -" 1090 @=I+1 1100 " " 1110 I=0,15 1120 T=S:I) ;=(T<" ")+(T>"z") T="." 1130 $=T ;=I=7 " - " 1140 ;=S+I>=E I=15 1150 @=I+1 1160 / S=S+I 1170 @=(S>E) 1180 ] 2000'** Set Memory 2010 ;=P<Q !=500 S=A 2012 ;=P<Q !=500 ;=F ??=S " " ?$=S:0) S:0)=A " - " ?$=A / #=2100 2020 @ 2030 ??=S " " ?$=S:0) " -" 2040 Z=" " !=200 / 2050 ;=P=Q S=S+1 F=1 #=2090 2060 !=500 2070 ;=F S:0)=A S=S+1 2080 ;=C="-" S=S-1 F=1 2090 @=(F=0) 2100 ] 3000'** Fill 3010 G=-1 3020 ;=P<Q !=500 S=A ;=P<Q !=500 ;=A>S E=A+1 !=500 ;=F G=A 3030 ;=G=-1 " ?"/ 3040 ;=G>=0 @ S:0)=G S=S+1 @=(S=E) 3050 ] 4000'** Go 4010 F=0 4020 ;=P<Q !=500 ;=F "G:" ??=A " " >=A / 4030 ;=F=0 " ?"/ 4040 ] 5000'** IN 5010 !=500 ;=F M:2)=A >=M+1 ?$=A " -> " ?$=M:0) / 5020 ] 6000'** OUT 6010 !=500 ;=F S=A !=500 ;=F M:8)=A M:10)=S >=M+7 #=6030 6020 " ?"/ #=6040 6030 ?$=S " <- " ?$=A / 6040 ] 7000'** Help 7010 " *DUMP: D[aaaa[,aaaa]] *FILL : Faaaa,aaaa,dd"/ 7020 " *GO : Gaaaa *SET : S[aaaa[,dd]]"/ 7030 " *IN ; Iaa *OUT : Oaa,dd"/ 7040 " READ: R WRITE: Waaaa,aaaa"/ 7050 " QUIT: Q HELP : H"/ 7060 " *:can use multi statement separated with ':'"/ 7070 " command set:= exec:! display:?"/ 7080 ] 8000'** Write 8010 !=500 ;=F=0 #=8900 8020 S=A !=500 ;=F=0 #=8900 8030 ;=S>A #=8900 8040 @ 8050 G=0 E=32 ;=A-S+1<E E=A-S+1 8060 ":" D=E !=600 D=S/256 !=600 D=S !=600 D=0 !=600 8070 @ D=S:0) S=S+1 E=E-1 !=600 @=(E=0) 8080 D=-G !=600 / 8090 @=(S>A) 8100 ":00000001FF"/ 8110 ] 8900 " ?"/ 8910 ] 9000'** set cmd 9010 Y:0)=Q-P ;=P<Q I=1 @ Y:I)=P:0) I=I+1 P=P+1 @=(P=Q) 9020 ] 9100'** Display cmd 9110 ;=Y:0)>0 " " I=1,Y:0) $=Y:I) @=I+1 / 9120 ] 9200'** exec cmd 9210 P=B+1 Q=P 9220 ;=Y:0)>0 I=1,Y:0) Q:0)=Y:I) Q=Q+1 @=I+1 9230 C=":" 9240 ] 10000'** main 10010 !=100 10020 @ 10030 Z="]" !=200 !=300 / 10040 ;=C="H" !=7000 #=11000 10050 ;=C="R" >=$100 / #=11000 10060 ;=C="W" !=8000 #=11000 10070 ;=C="=" !=9000 #=11000 10080 ;=C="?" !=9100 #=11000 10090 ;=C="!" !=9200 #=11000 10100 ;=C="G" !=4000 #=11000 10110 ;=C="D" !=1000 #=11000 10120 ;=C="S" !=2000 #=11000 10130 ;=C="F" !=3000 #=11000 10140 ;=C="I" !=5000 #=11000 10150 ;=C="O" !=6000 #=11000 10160 ;=(C<>"Q")*(C>0) $=C " ??"/ 11000 ;=C=":" !=300 #=10100 11010 @=(C="Q")

★更新 2020/10/30 最新版に更新
 Ver0.02 マルチステートメント対応、コマンド記憶機能追加


 これで今後の予定であるタイマー割込みによるシリアル受信処理とSPIインターフェースの開発環境が揃いました^^


★追記 2020/10/30 {
 モニタを操作していると同じようなコマンドの組合せを何度も実行するよう場面が出てきます。
 コマンドをマルチステートメント対応にしてコピペできるようにしました。
 更にコマンド記憶機能を追加し、コマンドをまとめて実行できるようにしました。
 ちょっとした機能を簡単に追加できるのもGAME言語で作った効用ですね。なにげに便利です^^

マルチステートメントとコマンド記憶機能の使用例
}


★追記 2020/11/10
 パソコン内のバイナリファイルをモニタでダウンロードする際にHEXファイルに変換する必要があります。
 パソコン上のファイルを開始アドレスを指定してインテルヘキサフォーマットに変換するバイナリヘキサ変換ツールを探したけど見つからなかった・・・
 処理自体は非常に簡単で探すより作った方が早いのでDOS窓で実行する変換プログラムを作りました。
 需要は少ないでしょうがアップしておきます。使い方は下記のヘルプ表示を参照してください。
  • Bin2Hex20201109.zip

    C:¥tool>bin2hex
    convert binary file to intel hex format(can set start address)
    usage : bin2hex [-sxxxx] InputFile [>OutputFile]
      -s : set start address of hex file
        ver 0.01 2020/11/09 by skyriver
    C:¥tool>
    

  • BIN2HEX_CPM80_20210117v001a.zip
    ★変更 2021/01/17
     Ver0.01a HEXファイル名生成でNGになる場合があったので修正
    ★追記 2020/12/05
     CP/M-80用のbin2hexです。CP/Mではリダイレクトできないので対象ファイル名のセカンドファイル名が".HEX"のファイル名でHEXファイルが生成されます。
     HI-TECH Cでコンパイルしているので動作させるためにはCPUがZ-80相当品である必要があります。



★追記 2020/11/19
 「Z80GALの構想(その9)簡易モニタの製作(その2)ブレーク機能の実装」の記事にブレーク機能の追加実装について書きました。


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

nice!(0)  コメント(0) 
共通テーマ:趣味・カルチャー

Z80GALの構想(その3)ブレッドボードでの仮組 [Z80]

 前回の「Z80GALの構想(その2)GALの設計」でGALの基本的な設計はできたのでブレッドボードを使って組んでみました。

 現時点でのチップ構成は下記の5チップ構成となっています。
  • Z80CPU
     Z84C0020PEG(40PIN):クロック上限の20MHzで動作
  • RAM
     UM61512-15(32PIN):64K x 8bitのスタティックメモリ アクセスタイム:15ns
  • EEPROM
     W27C512-45Z(28PIN):64K x 8bitのEEPROM アクセスタイム:45ns
  • GAL
     GAL22V10D(24PIN):10個のOLMC(Output Logic Macrocell)を持つ、GALの中では最も容量が大きく新しいチップ
  • クロック生成&タイマー割込み
     PIC12F683(8PIN):ピン数が少ないので74HCU04でクロック発振するより実装効率が良い(シリアル受信用のタイマー割込み信号も生成)

 それではブレッドボードで結線後の作業概要をメモしておきます。

  1. ROM上でのループ試験
     最初にROM上でのループ試験を行いました。シリアル出力(Tx)への出力データをトグルにして矩形波を出力しています(Lチカのようなもの)。この時点ではまだハンドアセンブルで、E2PROMに書込み無事動作しました。

    ■テストプログラム1
    0000 F3         DI                     ; 4
    0001 AF         XOR     A              ; 4
    0002 D3 80      OUT     (080h),A       ; 11 loop:28 states
    0004 EE 01      XOR     1              ; 7   ->(1/20*28)*2=2.8us
    0006 C3 02 00   JP      0002H          ; 10
    

     下図はTx信号の波形です。

    ROM上でのループ処理


  2. RAM上でのループ試験
     ROMの内容をRAMに書込み後、リードもRAM側になるように切替えてループする試験です。

    ■テストプログラム2
    0000 21 00 00   LD      HL,0
    0003 11 00 00   LD      DE,0
    0006 01 20 00   LD      BC,0020H
    0009 ED B0      LDIR
    000B 3E 01      LD      A,1
    000D D3 E0      OUT     (0E0H),A        ; select RAM
    000F D3 80      OUT     (080H),A        ; 11 loop:30 states
    0011 EE 01      XOR     1               ;  7  ->(1/20*30)*2=3.0us
    0013 18 FA      JR      000FH           ; 12
    
    ※ここまではハンドアセンブルですが、以降はアセンブラを使用しています。

     しかし、ソフトでRAMレジスタをセットしても直ぐにクリアされてしまう現象が発生しました。

     尚、リセット回路は下図のような一般的なものです。

    リセット回路

     電源ON時の電源電圧リセット信号 の推移が下図で、Z80が走り始めてもGALの方がリセット状態になっているようでZ80側の初期処理でRAMレジスタをセットしてもレジスタが直ぐにクリアされる状態でした。
     黄色(ch1)が電源電圧 リセット信号 で水色(ch2)が水晶発振部分の波形です。

    ★修正 2020/10/29 (誤)電源電圧 → (正)リセット信号

    電源ON時のリセット信号変化

     対処としては、ROMからRAMへ16KBコピー(21state/byteなので約17.2ms)後にRAMレジスタをセットすることでうまく動作しました。
     下図は黄色(ch1)がRAMレジスタの出力(RAMが選択状態になっている)で水色(ch2)がTxの波形です。

    RAM上でのループ試験


  3. ソフトウェアシリアルの実装
     最初から割込みでのシリアル受信を作るのは大変なので開発環境を整えるためにまずは割込みを使わないシリアル通信を実装しました。
     ステート数を数えて通信速度に合わせればいいだけなので実装は簡単です。
     下図はTx信号のサンプル波形で通信速度は9600bpsです。中央右寄りの箇所で1bit分の信号幅を計測していて丁度1/9600sであることを確認しています。

    シリアル送信信号(Tx)のサンプル波形

    ★追記 2020/10/27
     ソフトウェアで実現したシリアル通信部のリストを追記します。

    ソフトウェアシリアル通信処理部のリスト
    ;*** softqare serial *** ; 9600bps 1bit length = 1000000/9600 = 104.17 us ; = (1000000/9600)/(1/20) = 2083.3 states(20MHz) 0823 STIMST EQU 2083 ; (10000/96*20) ; state par serial 1bit 0099 STICNT EQU (STIMST-11-4-12-17-13-11-7-8-10-10)/13+1 ; serial out ; A <- data 0031 B7 Putc: OR A ; 4 clear Cy(start bit) 0032 17 RLA ; 4 0033 06 09 LD B,9 ; 7 set bit counter 0035 D3 80 Putc10: OUT (AdSeri),A ; 11 0037 1F RRA ; 4 0038 18 00 JR Putc20 ; 12 = 4x3 dummy state 003A CD 0043 Putc20: CALL STimer ; 17 003D 10 F6 DJNZ Putc10 ; 13/8 003F F6 01 OR 1 0041 D3 80 OUT (AdSeri),A ; out stop bit 0043 C5 STimer: PUSH BC ; 11 0044 06 99 LD B,STICNT ; 7 0046 10 FE DJNZ $ ; 13/8 0048 C1 POP BC ; 10 0049 C9 RET ; 10 00EF STICNT2 EQU (STIMST*3/2-11/2-7-7-7)/13+1 004D STICNT3 EQU (STIMST/2-8-4-7-10-10-10)/13+1 ; serial in ; A -> data 004A C5 Getc: PUSH BC 004B DB 80 Getc10: IN A,(AdSeri) ; 11 004D E6 01 AND 01H ; 7 004F 20 FA JR NZ,Getc10 ; 12/7 0051 06 EF LD B,STICNT2 ; 7 0053 10 FE DJNZ $ ; 13/8 0055 06 08 LD B,8 0057 DB 80 Getc20: IN A,(AdSeri) ; 11 get Rx(LSB) 0059 1F RRA ; 4 005A 79 LD A,C ; 4 005B 1F RRA ; 4 005C 4F LD C,A ; 4 005D CD 0043 CALL STimer ; 17 0060 10 F5 DJNZ Getc20 ; 13/8 0062 79 LD A,C ; 4 0063 06 4D LD B,STICNT3 ; 7 0065 10 FE DJNZ $ ; 13/8 0067 C1 POP BC ; 10 0068 C9 RET ; 10


  4. エコーバック試験
     シリアル通信ができるようになったのでエコーバックの試験を行いました。結果は問題なく動作しました。

    エコーバック試験


  5. GAME言語の移植
     今後、割込み処理やSPIを実装していくための環境として簡単なモニタを作って実装しようかとも思いましたが、メモリ確認等の作業効率を考えてGAME言語を移植することにしました。
     シリアル送受信ができているので移植は簡単です。
     但し、今の時点では割込みを使わないキー入力処理なのでキーセンスができない状態で使い勝手が少しよくありません。

    GAME言語の画面サンプル

     以前GAME言語を使って作成した迷路生成&探索プログラムも動作しました。

    迷路プログラム画面例


  6. 直近の予定
     パソコン側からファイルを手軽に持ってこれるようにしないと開発効率が良くないのでHEXファイルの受信処理を実装する予定です。



★追記 2021/01/26
 ブレッドボードで仮組したZ80GALの写真を追記します。

ブレッドボードで組んだZ80GAL



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

nice!(0)  コメント(0) 
共通テーマ:趣味・カルチャー

Z80GALの構想(その2)GALの設計 [Z80]

 前回の記事ではシリアル通信用のタイマー割込みを発生させるためのGAL22V10による分周器の実験について書き、GALで実現するには不適切なので8ピンのPICで対応予定であることを書きました。

 今回はシリアル通信用のタイマー以外の周辺機能(ROM/RAM切替え、SD Card用のSPI、調歩同期シリアル通信)のインターフェース部分をGALに実装すべく、GALの設計を行いましたのでメモを残しておきます。

 GALはPALにレジスタ(D-FF)が追加になったりしていますが、1個でSPIと調歩同期シリアルのシリパラ変換を実現できるほどパワフルではありません。今回はそれぞれのインターフェースに必要なビット単位でのインターフェースをGALで実現しました。

 現時点でのソースは下記になります。
 メモリ切替え用の23ピンに配置した「RAM」は外部に出す必要が無いのですが前回の記事に書いたようにNODE宣言してピンにアサシンしないようにすることができないようなのでもったいないですが外部ピンにアサインしています。

Z80ワンボードマイコン(Z80GAL)用GALソース
Name Z80GAL; Partno SA0001; Date 2020/10/22; Revision 001; Designer skyriver; Company Piclabo; Assembly None; Location None; Device G22V10; /*****************************************************************/ /* */ /* Memory select,Serial and SD-Card interface for Z-80 one borad */ /* by skyriver */ /*****************************************************************/ /* GAL22V10 +--- ---+ clock 1| |24 +5V /RESET 2| |23 RAM REG /MREQ 3| |22 /ROMRD /IORQ 4| |21 /RAMRD /RD 5| |20 SDCK REG /WR 6| |19 SDO REG A7 7| |18 SDI A6 8| |17 /SDCS REG A5 9| |16 DAT1 A4 10| |15 DAT0 A3 11| |14 TX REG GND 12| |13 RX +-------+ */ /** Inputs **/ PIN 1 = clock; /* Register Clock */ PIN 2 = !RESET; /* reset */ PIN 3 = !MREQ; /* memory request */ PIN 4 = !IORQ; /* I/O request */ PIN 5 = !RD; /* read */ PIN 6 = !WR; /* write */ PIN [7..11] = [A7..3]; /* address */ PIN 13 = RX; /* serial in */ PIN 18 = SDI; /* SPI data in */ /** Outputs **/ PIN 23 = RAM; /* select RAM 0:ROM 1:RAM */ PIN 22 = !RAMRD; /* RAM read */ PIN 21 = !ROMRD; /* ROM read */ PIN 20 = SDCK; /* SPI clock */ PIN 19 = SDO; /* SPI data out */ PIN 17 = !SDCS; /* SD chip select */ PIN 16 = DAT1; /* read pnly */ PIN 15 = DAT0; /* LSB of data : I/O */ PIN 14 = TX; /* serial out */ /* can't use NODE NODE N_ROM; NODE N_SDCK; */ field adr = [A7..3]; field baseadr = [A7..5]; field register = [RAM,SDCK,SDO,SDCS,TX]; /** address definition **/ $DEFINE AdBase 'h'e0 /* Base of I/O Address */ $DEFINE AdSetRAM 'h'f8 /* DAT0: 0:ROM, 1:RAM */ $DEFINE AdSdCs 'h'f0 /* SDCS DAT0: 1:act 0:disable,can write & read */ $DEFINE AdSdOut 'h'e8 /* SD clock, SDO */ $DEFINE AdSdIn 'h'e8 /* SD SDI */ $DEFINE AdSeri 'h'e0 /* serial in/out use dat0 */ /** logic definition **/ register.ar = RESET; register.sp = 'b'0; append RAM.d = DAT0 & IORQ & WR & adr:AdSetRAM; append RAM.d = RAM & !(IORQ & WR & adr:AdSetRAM); append RAM.oe = 'b'1; append RAMRD = RAM & MREQ & !IORQ & RD; append RAMRD.oe = 'b'1; append ROMRD = !RAM & MREQ & !IORQ & RD; append ROMRD.oe = 'b'1; append SDCK.d = DAT1 & IORQ & WR & adr:AdSdOut; append SDCK.d = SDCK & !(IORQ & WR & adr:AdSdOut); append SDCK.oe = 'b'1; append SDO.d = DAT0 & IORQ & WR & adr:AdSdOut; append SDO.d = SDO & !(IORQ & WR & adr:AdSdOut); append SDO.oe = 'b'1; append SDCS.d = DAT0 & IORQ & WR & adr:AdSdCs; append SDCS.d = SDCS & !(IORQ & WR & adr:AdSdCs); append SDCS.oe = 'b'1; append TX.d = DAT0 & IORQ & WR & adr:AdSeri; append TX.d = TX & !(IORQ & WR & adr:AdSeri); append TX.oe = 'b'1; append DAT0.oe = IORQ & RD & baseadr:AdBase; append DAT0 = SDI & IORQ & RD & adr:AdSdIn; append DAT0 = RX & IORQ & RD & adr:AdSeri; append DAT0 = SDCS & IORQ & RD & adr:AdSdCs; append DAT0 = RAM & IORQ & RD & adr:AdSetRAM; append DAT1.oe = IORQ & RD & adr:AdSdIn; append DAT1 = SDCS & IORQ & RD & adr:AdSdIn;


 WinCuplもだいぶ慣れてきたのでソース作成はあまり問題がないのですが、シミュレータを動かす度にSIファイルにヘッダ情報が無かったり、エラーが発生したりである程度複数なパターンをシミュレーションして確認するのは苦労します(一つのファイルで全て試験すればいいのかも)

 今回行ったそれぞれの機能についてのシミュレーション結果をメモとして残しておきます。

  1. ROM/RAM切替機能
     リセット後のメモリリードはROM側が選択されていて、メモリ切替え用I/Oアドレスに書き込むことでDAT0(LSB)が1の場合はRAM、0の場合はROMが選択されます。
     下図のシミュレーションは以下の動作のシミュレーションです。
    1. ROM選択状態でのメモリリード
    2. RAM選択操作(RAMが1になる)
    3. RAM選択状態でのメモリリード
    4. メモリ選択状態のリード(DAT0にRAMの状態が出力される
    5. ROM選択操作(RAMが0になる)

    ROM/RAM切替機能のシミュレーション結果

  2. SPIインターフェース
     GALに実装しているのはSPI用のクロックと選択信号(SDCS)の出力とSDI/SDOそれぞれの入出力機能です。実装するのはビット単位でのインターフェースだけでシリパラ変換等はZ80上のソフトで実現する予定です。
     下図のシミュレーションは以下の操作を確認しています。
    1. SDCS(選択信号)をactive(0)に設定
    2. クロックとSDOを1に設定
    3. クロックを0に変更
    4. クロックを1、SDOを0に設定
    5. SDI(1)とSDCS(1)の読込み
    6. SDI(0)とSDCS(1)の読込み

    SPIインターフェース機能のシミュレーション結果

  3. 調歩同期シリアル通信インターフェース
     シリアル通信に関してはGALに実装するのは送受用の2ビットのインターフェースのみです。
     下図のシミュレーションは以下の操作を確認しています。
    1. TXを1に設定
    2. TXを0に設定
    3. RX(1)の読込み
    4. RX(0)の読込み
    5. 0xE8のI/Oアドレス(SPI関連の読込み)の読込み(SDIを設定していなかったのでLSBは不定になっている)

    調歩同期シリアル通信インターフェース機能のシミュレーション結果


 シミュレーション時にSIファイルのヘッダ情報が空になるエラーが高い頻度で発生し手動で設定し直してもすぐになくなることもあり、回避方法がまだつかめていません。
 プロジェクトのファイル構成ツリーを見ると複数パターンのシミュレーションを管理できるように見えますがやり方が判っていません。
 まぁ、GALにはあまり複雑な機能は実装できないのでシミュレーターでの確認がなんとかできました。


★追記 2020/10/23 {
 WinCuplでコンパイルして生成されたJEDACファイルをTL866II Plusに読込む際に「format error」が表示される場合があります。
 ネットで探すと「GALを使ってみる.1」にもWinCuplで作ったJEDACファイルは読み込みエラーが発生するようなことが書いてあります。

 エラーが発生したJEDACファイルを調べてみると
  • JEDECファイルの末尾のチェックサムでエラーが発生しているのか確かめるためにエラーが発生しないJEDECファイルの末尾の^C前後のチェックコードを変更してからTL866で読込んでもエラーは発生しなかった
  • *Lの行を1行削除するとエラーは発生しなくなった
  • 読込みでエラーが発生してもTL866で読込んだ内容をファイルに書き出し、元のJEDECファイルと内容を比較すると違いは無かった
  • エラーが発生した場合(当然コンパイルは通るもの)でもPLDファイル内の論理式などを変更してJEDACファイルの行数が変わるとエラーが発生しなくなった

 以上のことからTL866で読込み時にエラーが表示されてもJEDECファイルの内容は読込まれているようです。
 エラーの表示は行数に依存する(ように見える)のでTL866側の問題の可能性が高いと思います。
 TL866II Plusのソフトを最新(App:Ver10.41,Firm:Ver04.2.122)にしても状況は同じでした。

 尚、JEDECファイルのフォーマットに関してはChaNさんの「PLDフューズファイルについて」を参考にさせて頂きました。
}
★追記 2020/10/24
 TL866で発生する「file format error」の画面キャプチャ追加

TL866II PlusでWinCuplで生成したJEDECファイル読込み時のエラー



★追記 2020/10/23 {
 今後も変更する可能性がありますが最新版を貼っておきます。
 ・メモリ後半のアクセスは必ずRAMになるように変更
 ・余分なIORQチェックを削除

★更新 2020/10/25 Ver0.02
 ・PICからのタイマー割込み信号を疑似的なopen drain出力にし、PullUp抵抗を追加
 ・タイマー割込み要求をGAL経由で読み込み可能とした
 ・これに伴いI/Oアドレスディコード用のアドレス線を一本削減

Z80ワンボードマイコン(Z80GAL)用GALソース(最新版)
Name Z80Gal; Partno SA0001; Date 2020/10/25; Revision 002; Designer skyriver; Company Piclabo; Assembly None; Location None; Device G22V10; /*****************************************************************/ /* */ /* Memory select,Serial and SD-Card interface for Z-80 one borad */ /* by skyriver */ /*****************************************************************/ /* GAL22V10 +--- ---+ clock 1| |24 +5V /RESET 2| |23 RAM REG /MREQ 3| |22 /ROMRD /IORQ 4| |21 /RAMRD /RD 5| |20 SDCK REG /WR 6| |19 SDO REG A7 7| |18 SDI A6 8| |17 /SDCS REG A5 9| |16 DAT1 TINT 10| |15 DAT0 A15 11| |14 TX REG GND 12| |13 RX +-------+ */ /** Inputs **/ PIN 1 = clock; /* Register Clock */ PIN 2 = !RESET; /* reset */ PIN 3 = !MREQ; /* memory request */ PIN 4 = !IORQ; /* I/O request */ PIN 5 = !RD; /* read */ PIN 6 = !WR; /* write */ PIN [7..9] = [A7..5]; /* address */ PIN 10 = TINT; /* Timer interrupt 1:active */ PIN 11 = A15; /* MSB of address */ PIN 13 = RX; /* serial in */ PIN 18 = SDI; /* SPI data in */ /** Outputs **/ PIN 23 = RAM; /* select RAM 0:ROM 1:RAM */ PIN 22 = !RAMRD; /* RAM read */ PIN 21 = !ROMRD; /* ROM read */ PIN 20 = SDCK; /* SPI clock */ PIN 19 = SDO; /* SPI data out */ PIN 17 = !SDCS; /* SD chip select */ PIN 16 = DAT1; /* data-bus(I/O) */ PIN 15 = DAT0; /* LSB of data-bus(I/O) */ PIN 14 = TX; /* serial out */ /* can't use NODE NODE N_ROM; NODE N_SDCK; */ field adr = [A7..5]; field baseadr = A7; field register = [RAM,SDCK,SDO,SDCS,TX]; /** address definition **/ $DEFINE AdBase 'h'80 /* Base of I/O Address */ $DEFINE AdSetRAM 'h'e0 /* DAT0: 0:ROM, 1:RAM */ $DEFINE AdSdCs 'h'c0 /* SDCS DAT0: 1:act 0:disable,can write & read */ $DEFINE AdSdOut 'h'a0 /* SD clock, SDO */ $DEFINE AdSdIn 'h'a0 /* SD SDI(=above) */ $DEFINE AdSeri 'h'80 /* serial in/out use dat0 */ /** logic definition **/ register.ar = RESET; register.sp = 'b'0; append RAM.oe = 'b'1; append RAM.d = DAT0 & IORQ & WR & adr:AdSetRAM; append RAM.d = RAM & !(IORQ & WR & adr:AdSetRAM); append RAMRD.oe = 'b'1; append RAMRD = RAM & MREQ & RD; append RAMRD = A15 & MREQ & RD; append ROMRD.oe = 'b'1; append ROMRD = !RAM & !A15 & MREQ & RD; append SDCK.oe = 'b'1; append SDCK.d = DAT1 & IORQ & WR & adr:AdSdOut; append SDCK.d = SDCK & !(IORQ & WR & adr:AdSdOut); append SDO.oe = 'b'1; append SDO.d = DAT0 & IORQ & WR & adr:AdSdOut; append SDO.d = SDO & !(IORQ & WR & adr:AdSdOut); append SDCS.oe = 'b'1; append SDCS.d = DAT0 & IORQ & WR & adr:AdSdCs; append SDCS.d = SDCS & !(IORQ & WR & adr:AdSdCs); append TX.oe = 'b'1; append TX.d = DAT0 & IORQ & WR & adr:AdSeri; append TX.d = TX & !(IORQ & WR & adr:AdSeri); append DAT0.oe = IORQ & RD & baseadr:AdBase; append DAT0 = SDI & IORQ & RD & adr:AdSdIn; append DAT0 = RX & IORQ & RD & adr:AdSeri; append DAT0 = SDCS & IORQ & RD & adr:AdSdCs; append DAT0 = RAM & IORQ & RD & adr:AdSetRAM; append DAT1.oe = IORQ & RD & (adr:AdSdIn # adr:AdSeri); append DAT1 = SDCS & IORQ & RD & adr:AdSdIn; append DAT1 = TINT & IORQ & RD & adr:AdSeri;

}


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

nice!(0)  コメント(0) 
共通テーマ:趣味・カルチャー

Z80GALの構想 [Z80]

 PICとZ80を使った3チップ構成のCP/Mボード(Pic24CPM)を開発しましたが、周辺チップをPICで代行せず、GALとソフトでの対応でチップ数の少ないZ80ボードを構想中です。

 ネット上ではRAMまでもマイコン側で受け持とうとする試みもある中で逆行するようですが、Z80でも20MHzで動かせばソフトで周辺チップの機能を代行できるのではないかという発想です。

 まずはシリアル通信ですが、Z80が処理中でもキー入力を受け付けられるようにしたいのでタイマー割込みを使って受信する予定です。

 そのためにはボーレートの3倍でタイマー割込みする必要があり、GAL(22V10D)を使った分周回路を検討してみました。

 CUPLコンパイラはWinCuplを使う予定ですが、Windows10環境では結構不安定で関連する実行ファイルを全て「互換性のトラブルシューティング」で互換性設定することで少し安定するようになりました。

 試行錯誤で作ったCuplソースが下記です。
 PLDによるカウンターの実現に関してはネットで探すといくつか例が見つかりますが、そのままでは使えなかったので「下位ビットが全て'1'の場合、反転する」というようにして論理式を作成することで割合すっきりしたものができました。

GALを使った分周器のCuplソース
Name Z80GAL; Partno SA0001; Date 2020/10/19; Revision 01; Designer skyriver; Company Piclabo; Assembly None; Location None; Device g22V10; /****************************************************************/ /* */ /* freq divider and control signal genelator for Z-80 one borad */ /* by skyriver */ /****************************************************************/ /* GAL22V10 +--- ---+ clock 1| |24 +5V /RESET 2| |23 /INTRQ /MREQ 3| |22 Q7 /IORQ 4| |21 Q6 /RD 5| |20 Q5 /WR 6| |19 Q4 A7 7| |18 Q3 A6 8| |17 Q2 A5 9| |16 Q1 A4 10| |15 Q0 A3 11| |14 NC GND 12| |13 NC +-------+ */ /** Inputs **/ PIN 1 = clock; /* Register Clock */ PIN 2 = !RESET; /* reset */ PIN 3 = !MREQ; /* memory request */ PIN 4 = !IORQ; /* I/O request */ PIN 5 = !RD; /* read */ PIN 6 = !WR; /* write */ PIN [7..11] = [A7..3]; /* address */ /** Outputs **/ PIN 23 = !Q8; /* timer interrupt request */ PIN [15..22] = [Q0..7]; /* counter */ /* NODE [Q0..7]; cann't use NODE !! */ field output = [Q7..0]; field adr = [A7..3]; Q0.d = !Q0; append Q1.d = !Q1 & Q0; append Q1.d = Q1 & !Q0; append Q2.d = !Q2 & Q0 & Q1; append Q2.d = Q2 & (!Q0 # !Q1); append Q3.d = !Q3 & Q0 & Q1 & Q2; append Q3.d = Q3 & (!Q0 # !Q1 # !Q2); append Q4.d = !Q4 & Q0 & Q1 & Q2 & Q3; append Q4.d = Q4 & (!Q0 # !Q1 # !Q2 # !Q3); append Q5.d = !Q5 & Q0 & Q1 & Q2 & Q3 & Q4; append Q5.d = Q5 & (!Q0 # !Q1 # !Q2 # !Q3 # !Q4); append Q6.d = !Q6 & Q0 & Q1 & Q2 & Q3 & Q4 & Q5; append Q6.d = Q6 & (!Q0 # !Q1 # !Q2 # !Q3 # !Q4 # !Q5); append Q7.d = !Q7 & Q0 & Q1 & Q2 & Q3 & Q4 & Q5 & Q6; append Q7.d = Q7 & (!Q0 # !Q1 # !Q2 # !Q3 # !Q4 # !Q5 # !Q6); /*************************************************/ /* baud rate : 14400 */ /* x3 sampling -> 1000000/(38400*3)=8.68[usec] */ /* 20MHz : 1/20[usec] */ /* divid rate */ /* -> 1000000/(38400*3)/(1/20) */ /* = 173.61 */ /*************************************************/ $DEFINE MAXNUM 'd'174 Q0.ar = output:MAXNUM; Q1.ar = output:MAXNUM; Q2.ar = output:MAXNUM; Q3.ar = output:MAXNUM; Q4.ar = output:MAXNUM; Q5.ar = output:MAXNUM; Q6.ar = output:MAXNUM; Q7.ar = output:MAXNUM; /* output.oe = 'b'1; __ only Q0 is setted, others do not output !! */ Q0.oe = 'b'1; Q1.oe = 'b'1; Q2.oe = 'b'1; Q3.oe = 'b'1; Q4.oe = 'b'1; Q5.oe = 'b'1; Q6.oe = 'b'1; Q7.oe = 'b'1; output.sp = 'b'0; /* Q8.ar = output:10; Q8.sp = output:MAXNUM; */


 カウントの出だしの部分をシミュレータで確認した結果が下図です。

シミュレータでのカウント動作の確認結果


 ライターはTL866II pulsを使い、実際にGAL22V10Dに書き込んで動作確認した結果が下図です。
 上記のソースにも書いてあるように20MHzのクロックを入力して、38400bps(カウンタのレジスタ数を8個としたことから決めた速度)の3倍の8.68[usec]が目標周期なのでほぼ目標通りの値になっています。

分周した波形(Q7)


 しか~し、
  • CUPLのNODEが使えない
     NODE宣言でPINに出さないレジスタを宣言するとエラーになるのでGAL22V10ではNODE機能が使えないようですorz
     そのため、カウンターで使用しているレジスタを全てPINに出力する必要があり、出力PINが非常に無駄になります。

  • 9個目のレジスタを使うとエラーが発生
     GAL22V10には10個のレジスタがあるのに9個以上使用すると下記のエラーが発生し、コンパイルできませんでした。



 等の理由から、タイマー割込みをGALで生成するのは諦めましたw
 ここは素直に8ピンのPIC(水晶発振用ICも兼ねる)を使う予定です^^


★2020/10/22 追記 {
 上記のG22V10でレジスタが8個までしか使えないと書きましたが、エラーの原因が判りました。
 下図の赤マーク部分に記載があるようにresetは全レジスタで共通になっているので個別に別々の論理式を書くと上記のエラーが発生します。
 同様にsp(SYNCHRONOUS PRESET)も全レジスタで同じ論理式にしないとエラーになります。

GAL22V10ダイアグラム(抜粋)
}


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

nice!(1)  コメント(1) 
共通テーマ:趣味・カルチャー