Posts Tagged with "FPGA"

既に発行済みのブログであっても適宜修正・追加することがあります。
Even in the already published blog, we may modify and add appropriately.

6809の割込み動作(4)

posted by sakurai on August 19, 2019

そこで、CCRを参照して割込みに入る入らないを決定させようとしたら、ソースコードで以下のような定義文を見つけました。

// flags used in MC6809_cpu.v
`define FLAGI regs_o_CCR[5]
`define FLAGF regs_o_CCR[6]
`define FLAGE regs_o_CCR[7]

前稿の図150.1のCCR構成を見てもわかるように、FLAGIのビット位置が誤っています。Iフラグの位置はbit5ではなくbit4です。これを正しいbit位置の4に修正したところ、ベクタフェッチの後に再度IRQステート遷移する動作は無くなり、フェッチしたアドレスに飛び、飛び先である割込みルーチンを実行するようになりました。

ところが、不具合がありました。割込みルーチンで割込み原因クリアをしているにも関わらず、再度の割込みが入りません。

図%%.1
図151.1 割込みタイムチャート

前稿では、図151.1のタイムチャートに示すように、Eフラグと同タイミングで仮にIフラグもセットするように修正しました。ところが、スタックにCCRを退避する前にIフラグを割込み禁止にしたため、割り込み禁止状態のCCRが退避され、割り込みルーチンの最後で回復されたものです。つまりIフラグのセットが早すぎたわけです。

正しくは、図151.2の6809割込みにあるように、IフラグはCCRを退避してからセットしなければなりません。安直にEフラグと同タイミングでセットしたため、割込みが再度かからなくなってしまったものです。

図%%.2
図151.2 割込み動作

ここまで動けば後一息、IフラグセットをCCRの退避後まで遅延させれば、動作するはずです。

左矢前のブログ 次のブログ右矢

6809の割込み動作(3)

posted by sakurai on August 16, 2019

調査の結果、割込み後のIRQマスクをCCRに全く反映していませんでした。ちなみに、CCRの仕様は6809マイコン・システム設計作法によれば、以下の図のようになっています。

図%%.1
図150.1 CCR

これだと動作が分からないのですが、その次に以下のような説明があります。

図%%.2
図150.2 Iフラグの説明

ついでにEフラグの説明も載せておきます。

図%%.3
図150.3 Eフラグの説明

Verilogソースコードを見ると、CCRのうち、Iフラグは全く設定されていないようですが、EフラグはIRQ割込みの際にセットされていたので、とりあえず、Eフラグを立てるタイミングでIフラグにコピーしました。本来は割込みの種類によりIフラグとFフラグのセット仕分けをしなければなりません。

ところが、タイムチャートのようにIフラグをセットしたので、これで正常動作するかと思ったのですが、動作は変わりません。Iフラグは参照もされていないようです。つまり、このIPの割込みは全く動作しないということができます。


左矢前のブログ 次のブログ右矢

6809の割込み動作(2)

posted by sakurai on August 15, 2019

内部ステートを表示します。割込みがかかってから、スタックにプッシュし、ベクタフェッチするまではOKですが、その後にさらにスタックプッシュが入るところが誤っています。

図%%.1
図149.1 割込み動作のタイミングチャート

ステート遷移は以下のようになっています。

\$09 SEQ_FETCH
\$0F SEQ_DECODE
\$33 SEQ_PC_READ_L
\$34 SEQ_PC_READ_L_1
\$35 SEQ_PC_READ_L_2
\$1B SEQ_JMP_LOAD_PC
\$09 SEQ_FETCH
\$03 SEQ_IRQ
(ここから)
\$20 SEQ_PREPUSH
\$22 SEQ_PUSH_WRITE_L
\$23 SEQ_PUSH_WRITE_L_1
\$24 SEQ_PUSH_WRITE_H
\$25 SEQ_PUSH_WRITE_H_1
(16バイトストアを4回繰り返し、PC, US, IY, IX)
\$20 SEQ_PREPUSH
\$22 SEQ_PUSH_WRITE_L
\$23 SEQ_PUSH_WRITE_L_1
(8バイトストアを4回繰り返し、DP, B, A, CC)
\$36 SEQ_MEM_READ_H
\$37 SEQ_MEM_READ_H_1
\$38 SEQ_MEM_READ_H_2
\$3A SEQ_MEM_READ_L_1
\$3B SEQ_MEM_READ_L_2
\$08 SEQ_LOADPC
\$09 SEQ_FETCH
\$03 SEQ_IRQ
(再度IRQ処理)


左矢前のブログ 次のブログ右矢

6809の割込み動作

posted by sakurai on August 14, 2019

メインCPUとサブCPUはお互いに割込みを掛け合います。メインCPUからは通常、サブCPUのbusyを確認し、readyであればサブCPUをHALTし、共有メモリにコマンドを書き込んだ後、HALTをネゲートします。サブCPUはすぐにbusyにした後、コマンドを解析し、処理を行います。

これが通常シーケンスですが、例えば時間のかかる処理をサブCPUがやっていた場合に中止させたい時には割込みをかけるしかありません。これをCANCEL割込みといいます。逆に、サブCPUからメインCPUに用事がある場合にはATTENTION割込みをかけます。

さて、メインCPUの割込みの一つにタイマー割込みがあり、これをシミュレーションしてみます。タイマー割り込みはIRQに接続されているため、IRQ割り込みハンドラをプログラムします。IRQは全レジスタのセーブリストアが自動で行われます。割り込みハンドラでは、まずタイマ割り込み要因のクリアを行った後、要因レジスタは負論理であるため、反転してメモリに書きだした後でRTIで元の命令列に戻ります。

1000         stack  equ   \$1000
2000         ustack equ   \$2000
FE00         start  org   \$fe00
           
FE00 10CE1000        lds   #stack
FE04 CE2000         ldu   #ustack
FE07 8EFD02         ldx   #\$FD02     ; IRQ Mask Reg
FE0A 86FF          lda   #\$FF      ; Timer IRQ unmask
FE0C A784          sta   ,X       ; Timer IRQ enabled
FE0E 20FE          bra   *
           
FE10         firq  equ *
FE10         irq   equ *
FE10 8EFD03         ldx   #\$FD03     ; IRQ event reg
FE13 A684          lda   ,X       ; IRQ clear
FE15 8AFB          ora   #\$FB
FE17 70FE17         eora   #\$FF
FE1A B73000         sta   \$3000
FE1D 3B           rti
           
FFF6             org   \$fff6
FFF6 FE10          fdb   firq
FFF8             org   \$fff8
FFF8 FE10          fdb   irq       ; Timer IRQ
FFFE             org   \$fffe
FFFE FE00          fdb   start
           
0000             end

まずリセット後にFD02をライトし、割込みマスクを許可します。'1'をライトすると許可となります。

図%%.1
図148.1 割込みイネーブルのタイミングチャート

次にタイマー割込みがかかるとスタックに全レジスタを退避します。PC, US, IY, IX, DP, B, A, CCの12バイトです。

図%%.2
図148.2 レジスタ退避のタイミングチャート

次に\$FFF8から2バイトをフェッチします。ところが、\$FFF8の示すアドレスにジャンプする前に再度全レジスタをスタックに退避しています。

図%%.3
図148.3 割込みベクタフェッチのタイミングチャート

割込みが入った場合、直ちに割込みマスクがかからなければ、無限に割込みが入るので、これはCPUのIRQマスクのバグと思われます。


左矢前のブログ 次のブログ右矢

FM7 ROM吸出し器

posted by sakurai on August 13, 2019

最近はPCB作成が安いので、手配線をすることなく、PCBを試作します。今回もFusionPCBに依頼しました。 基本的にGPIOにアドレスバスと制御信号の出力を行わせ、データバスをリードすることにより1バイトのデータを取得します。メインシステムはこれでOKですが、サブシステムは直接見えないため、サブCPUのプログラムをサブシステム側に送り込み、読み出すことにします。

図147.1にZ80カード端子表を示します。これは、FM7ユーザーズマニュアルから頂きました。

図%%.1
図147.1 Z80カード端子表

図%%.2
図147.2 論理回路図

図147.2に基板図を示します。上側の長方形にArduino Mega 2560 Proを搭載します。2.54mmピッチスルーホールに基板接続用コネクタで上下に接続することも考えましたが、厚みが大きくなるのと挿抜する必要は無いため、全ピンハンダ付けすることにします。

下側の長方形にはZ80カード用40Pコネクタを搭載します。本来はFCN-365P040/AUというコネクタを取り付けるのですが高価なので、汎用2.54mmピッチの40Pのコネクタで代用します。代用品は2.54mmピッチ40Pコネクタで一見良さそうだったのですが、試作したところコネクション不良となるため、やむなくFCN-365P040/AUをオーダーしました。

図%%.3
図147.3 レイアウト図


左矢前のブログ 次のブログ右矢

Arduino Megaスケッチの作成

posted by sakurai on August 12, 2019

USBによる通信の確認として、Blinkスケッチを実行してみます。

aruduinoのページからSoftware Downloadsを選び、IDEをダウンロードして解凍します。次にarduino.exeをクリックしてIDEを立ち上げます。

図%%.1
図146.1 解凍フォルダの内容

図146.2を参考に、以下のように設定します。

  • ツール⇒ボード⇒Arduino/Genuino Mega or Mega 2560
  • ツール⇒プロセッサ⇒ATmega2560 (Mega 2560)
  • ツール⇒シリアルポートでCOM ?(場合により変わる)

図%%.2
図146.2 ボード、プロセッサ、シリアルポートの設定

シリアルポートを確認する場合は、デバイスマネジャーを立ち上げて、ポート(COMとLPT)の下の階層にて、USB-SERIAL CH340 (COM ?)の番号を選択します。

  • ファイル⇒スケッチブック⇒Blink

スケッチブックからポートD13をON/OFFするBlinkというスケッチを選択します。オシロスコープで見やすいように、以下の赤字のように修正しました。

// the setup function runs once when you press reset or power the board
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1); // wait for a millisecond
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1); // wait for a millisecond
}

これをコンパイルして、以下のステップでボードにプログラムを書き込んだところ、正しく実行できました。

  • スケッチ⇒検証・コンパイル
  • スケッチ⇒マイコンボードに書き込む

図%%.3
図146.3 D13の波形

ポート13の波形は、5.28Vp-p、496Hzの方形波となりました。Arduinoから5V出力ができるということで、レベル変換バッファは不要となります。IOLの計算が必要ですが、FM7バス直結可能と思われます。通常ならユニバーサル基板で試作するところですが、配線が多くて面倒なので、最初からボードを起こすことにします。

前稿のシーケンスの通りにI/Oポートを設定します。


左矢前のブログ 次のブログ右矢

Arduino Mega 2560 Proの入手

posted by sakurai on August 10, 2019

FM7のROMデータ抜き出し法は各種ありますが、RS232Cやフロッピードライブ、カセットテープ等、いずれもFM7のI/Oを用いています。例えばRS232Cの場合は、FM7上にプログラムを配置し、シリアルデータとして抜き出す方法。この場合はそのプログラム配置ができないため、手で入力するしかありません。別の方法としてはフロッピーディスクを接続し、そこからプログラムをロードし、それを実行してフロッピーに書きこむ方式。

フロッピーディスクドライブは高価で場所も取るため、別の手段を考えます。条件はFM7側に一切プログラムを置かない方法です。カセットテープというわけにもいかず、FM7のメディアが無い以上、FM7側のプログラムを一切使わない方法として、Z80インタフェースを使用することを考えます。

Z80カードは、FM7の内部バスに対して、別のバスマスタを使用可能にするものであり、メインCPUである6809から\$FD05のMSBを1にすれば、メインCPUにHALTがかかり、外部バスマスタであるZ80による内部バスアクセスが可能になるものです。

手持ちのBeagleBoneで変換ボードを作成しようと思っていましたが、物理的に高さがあり、ボードが大きくなりそうであるため、別の方法を検討したところ、Arduino Mega 2560 Proという薄型のボードを入手しました。原理としては同じで、シリアル通信でボード上のCPUと通信し、そこからZ80インタフェースを通してFM7のバスマスタとして内部をアクセスする方式です。

図%%.1
図145.1 Arduino Mega 2560 Proボード

図145.2にボード接続図を示します。Arduino MegaはI/Oが非常に多いため、このような30本近くあるI/Oも楽に制御可能です。

図%%.2
図145.2 ボード接続図

アクセス法は図145.3にあるように、アドレスをセットし、1バイト読み出しを行います。これを繰り返すことでROMの読み出しを行います。例外的にサブシステムのROMはメンテナンスコマンドにより、サブCPUにプログラムを実行させて、サブシステム内部ROMを読み出します。

図%%.3
図145.3 アクセス方法


左矢前のブログ 次のブログ右矢

posted by sakurai on August 9, 2019

アドレスデコード及びレジスタ類を実装していきます。FM7のI/Oの情報は参照場所にあります。

メインシステム

FM7メインシステムは64KbitDRAMが存在するため、フルデコードされています。

  • $FD00 R キーボードデータ bit8
  • $FD00 R キーボードデータ bit7-bit0
  • $FD02 W 割込み(IRQ)マスクレジスタ(タイマ、キーボード)
  • $FD03 R 割込み(IRQ)フラグレジスタ(タイマ、キーボード)
  • $FD04 R 割込み(FIRQ)フラグ (ブレーク、アテンション)
  • $FD05 R サブ状態 (busy)
  • $FD05 W サブ・Z80制御 (HALT, Cancel, Z80)
  • $FD37 W マルチページレジスタ (リセット時イネーブル)
  • $FD38 R パレットレジスタ0から読み出し(初期値=黒)
  • $FD38 W パレットレジスタ0へ書き込み
  • $FD39 R パレットレジスタ1から読み出し(初期値=青)
  • $FD39 W パレットレジスタ1へ書き込み
  • $FD3A R パレットレジスタ2から読み出し(初期値=赤)
  • $FD3A W パレットレジスタ2へ書き込み
  • $FD3B R パレットレジスタ3から読み出し(初期値=マゼンタ)
  • $FD3B W パレットレジスタ3へ書き込み
  • $FD3C R パレットレジスタ4から読み出し(初期値=緑)
  • $FD3C W パレットレジスタ4へ書き込み
  • $FD3D R パレットレジスタ5から読み出し(初期値=シアン)
  • $FD3D W パレットレジスタ5へ書き込み
  • $FD3E R パレットレジスタ6から読み出し(初期値=黄)
  • $FD3E W パレットレジスタ6へ書き込み
  • $FD3F R パレットレジスタ7から読み出し(初期値=白)
  • $FD3F W パレットレジスタ7へ書き込み

図144.1にメインシステムブロック図を示します。今回はアドレスデコーダとそれからアクセスされるレジスタを分離して設計しました。モジュラー化のためです。図では上方左側にアドレスデコーダ、上方右側にレジスタが配置されています。

図%%.1
図144.1 メインシステムブロック図

サブシステム

FM7サブシステムはフルデコードされていません。

  • $D400 R キーボードデータ bit7-bit0
  • $D401 R キーボードデータ bit8
  • $D402 R キャンセルIRQ ACK
  • $D404 R メインCPUへのアテンションIRQ
  • $D408 R CRT ON
  • $D408 W CRT OFF
  • $D409 R VRAM Access Set
  • $D409 W VRAM Access Reset
  • $D40A R Ready
  • $D40A W Busy
  • $D40D R INS LED ON
  • $D40D W INS LED OFF
  • $D40E W VRAM Offset Address bit13-8
  • $D40F W VRAM Offset Address bit7-0

図144.2にサブシステムブロック図を示します。図では真ん中にアドレスデコーダとレジスタを階層ブロックにしたモジュールが配置され、その内部左側にアドレスデコーダ、右側にレジスタが配置されています。

図%%.2

図144.2 サブシステムブロック図

左矢前のブログ 次のブログ右矢

posted by sakurai on August 8, 2019

もう少し詳しく見てみます。HALTREQがアサートされたのは、たまたま\$E021のblo命令の第1バイト\$25をフェッチした直後でした。図143.2に示すように、blo命令の\$25と\$F4のフェッチの間に入ったことになります。

図%%.1
図143.1 HALTREQアサート時バスサイクルと内部状態

図%%.2
図143.2 アセンブルリスト

HALTREQが"blo l2"命令の間であるため、すぐにはHALTステートに遷移せず、blo命令の第2バイトである\$E022の\$F4のフェッチが行われます。

図%%.3
図143.3 blo第2バイトフェッチ

前稿で新設したhalt_save_stateは、HALTに入る直前のステートを保存するようにしたため、HALTステートに入るまではサイクル毎に書き換えられています。

図%%.4
図143.4 ステートセーブレジスタ更新

分岐命令をデコードし分岐が実行された後に、アドレスが分岐先である\$E017に変化しています。分岐命令はPCを変更するだけであり、これで分岐命令の実行は完了です。

図%%.5
図143.5 分岐先アドレス出力

分岐命令フェッチの前で新設のHALTステート(\$40)に遷移し、次のサイクルでHALTACKがアサートされています。HALTステートに遷移したので、halt_save_stateは、HALTステートの前の値(\$09)を保持し、更新は止まります。

図%%.6
図143.6 HALTステートへの遷移

一方、HALTREQネゲート時の状態を観測すると、前稿でも見たように、内部ステートが回復され、分岐先である\$E017、\$E018の命令を順次フェッチしています。このように分岐先命令のフェッチが実行されており、命令の切れ目、具体的には分岐命令の実行後にHALTに入り、分岐命令の実行前にHALTから出る動作を正しく実行しているようです。

図%%.6
図143.7 HALTREQネゲート時バスサイクルと内部状態


左矢前のブログ 次のブログ右矢

posted by sakurai on August 6, 2019

前稿のコードを追加し、シミュレーションしたところ、HALTをアサートしてもHALTステートへの遷移ができないことが判りました。前稿のコードのように、外部信号により内部フラグを立て、内部フラグが立っていたらすぐにstateをHALTステートに遷移させたのですが、別の要因によりstateが上書きされてしまうようです。

そこで、同種と思われるNMIのコードのように、HALTステート遷移を「NMIの内部フラグを見てNMIステートに遷移する場所」に移動したところ、正しく動作しました。シミュレーション結果を見ると、HALTがかかっても直ちにstateを遷移するのではなく、継続したバスサイクルを終了した命令の切れ目でHALTがかかっていることがわかります。

つまり、前稿のようにすぐにstate遷移させても上書きされてしまい、NMIのように命令の切れ目で判定して遷移させるのが正しいということです。

図%%.1
図142.1 HALTREQアサート時

HALTREQがアサートされてから、次のサイクルで内部フラグが立ちますが、数サイクル遅延した後にHALTステートに入っています。その後HALTACKが出力されます。

図%%.2
図142.2 HALTREQネゲート時

一方、HALTREQがネゲートされると、次のサイクルで内部フラグが落ち、次のサイクルでHALTACKが落ちると同時にHALTステートに入る前に保存されていたステートから再開します。こちらは遅延していません。


左矢前のブログ 次のブログ右矢


ページ: