« FPGAで7セグLEDのカウンターがやっと動く | トップページ | 外付けSRAMをFPGAにつなぐ »

2010年4月 3日 (土)

FPGAのUARTで7セグLEDを動かすことに成功

順序回路の4桁十進変換が動いた(3/31/2010)
 FPGAの初の自作プログラムで7セグLEDのカウンターがやっと動き始めた。次のステップはUART機能の組み込みである。UARTは、これまでの雑誌にいくつかサンプルコードがある。これをコピーするだけで簡単に動くはずだ。

A4032785

 UARTの目的は、コーディングの練習以外にもある。これを通してモニターのようなものをFPGAで動かし、デバッグツールにできないかということである。いわゆるステートマシンにしてUARTからの入力を受付け、指示された(内部変数の表示、メモリダンプ)処理を行い、次の外付けメモリ(SRAM)接続ステップのデバッグに使いたい。しかし、雑誌のソースコードを丸写ししているだけでは経験値を上げられない。

 ということもあって、今動いている7セグLEDのカウンターをもう少し拡張することにした。このカウンターは、16ビットのバイナリ入力を7セグLED4桁に十進表示する機能だったが、バイナリーから10進数(BCD)への変換が思ったように動かず、ウェブで、2桁の10進表示の組み合わせ回路を見つけて、それを3桁に拡張して何とか動かした。しかし4桁あるLEDのひとつは0の定数のままで、まだ全部動いていない。このままでは気分が悪い。

 この変換は最初、割り算が2の倍数でしか出来ないというのでwhileを使って引き算を繰り返すロジックにしたら、ISEが暴走するという騒ぎを起こしたいわくつきのロジックである。このときはわけもわからず組み合わせ回路にwhileを入れたが、あとから考えると恐ろしいことをしていたということがわかる。

 その後、沢山ある雑誌の特集記事を精読し(デジタルデザインテクノロジのHDL超入門の号はソフト屋には眼から鱗で面白い)、HDLが大分、分かり始めた。みかけはソフトウエアに見えるが、あくまでもフリップフロップやセレクターなどの論理回路を組み立てるための道具なのだ。これを常に頭に入れておかないと、さっきのようなことが起きる。

 それはとにかく、4桁の十進表示である。考えてみたら33Mhzものクロックが入っている。always @(クロック)の構文を使えば、割り算の代わりに、引き算を繰り返せばあっという間に計算が出来るはずである。バイナリの入力が変化したのをトリガーにクロックごとに引き算をして、1位の桁が出たところで止めればよい。

 うむ、うまく行きそうだ。引き算構文の前にフラグを立て、このフラグを1位の桁のところで下ろせば、そのときは望みの10進デジット(BCD)が揃っている。このフラグは、別のalwaysでバイナリー値が変わるたびに上げてやればよい。一種のステートマシンだ。

 よし、コーディングに入る。良いぞ、組み合わせ回路よりステップ数も少ない。勇んで論理合成に入った。おやあ、エラーが沢山でてくる。いくつかのケアレスミスを直して、最後まで残ったのが、フラグの変数を複数のソースがドライブしているという

Xst:528 - Multi-source in Unit <seg7andUART> on signal <FLAG>; this signal is connected to multiple drivers.

というエラーだ。

 ウェブを漁る。どうも別の独立したループの間で共通の変数はいじれないようだ。おかしい。これが出来ないならステートマシンという考え方が成り立たないはずだ。雑誌記事をさらに読み込む。うーむ、なにやら難しいことをしている。それぞれが、リクエストフラグとビジーフラグを持ち、それを見合いながら動いている。結構複雑だ。こんな割り算のような簡単なロジックにいれるには大げさすぎる。

 確かに、ハード回路を組む時に、一本のロジック線に別々のところからの信号を一緒にすることは出来ないはずなので理屈はわかる。しかし、マイコンプログラムでは苦もなく出来るしかけが、ここでは出来ないというのはどうも納得できない。

 出来ないとなると何とかしてやろうといういつもの悪い癖が出る。もっと簡便な方法があるはずだ。相当しつこい性格である。2日ばかり、暇さえあれば考え、何度かコーディングまで行ったが、やっぱり、同じエラーではねられるか、LEDはカウントアップしてくれない。

 昨日の深夜、寝る前に入った風呂の中で、突然閃いた。2つのalwaysで変数を共有するからいけないのだ。ひとつのalwaysの中に入れてしまえばそもそも共有を考える必要がない。それに入力が変化したことは、alwaysのセンシティビティリストに入れなくても、常に前の入力を保存しておけばよい。今まで共有しようとして苦労したが、それをせずに済む方法があった。

 これだ、クロックの度に動くalwaysで、入力を以前、BCDを出した時にセーブした値と比較し、同じならスルー、違えば、1位の桁が出るまでループ、1位の桁がでたところでセーブした値を今出した入力値に変える。うまいぞ、できそうだ。

 次の日、喜び勇んでコードを作り直す。出来たコードの確認ももどかしく論理合成をかける。良かった、エラーはでない。コンフィギュレーションする。LEDがついた。祈る気持ちで、タクトスイッチを押す。おおお、1が出た。2になった。順調に増えていく。さあ、桁上がりするか。いけないa(この7セグは16進表示ができる)が表示されて桁上がりしない。

 ははは、これは比較をA>10でやっているからだ。これをA>9に変える。よーし、順調に10の桁も増えていく。良いぞ。無性に嬉しい。久しぶりの快感だ。自分の考えたロジックの通りFPGAが動いてくれている。達成感で胸がふくらむ。何度も書くが、実に安上がりな娯楽だ。人間というのはこんなささいなことでも無上の喜びを得ることが出来るのだ。

 喜んだ勢いで、4桁に拡張(検証のため千回押すのは大変だった)したり、前のロジックとのリソース使用量の比較もしてみた。組み合わせ回路で999まで出せるコードは、スライスを150使っている(フリップフロップ72、ルックアップテーブル280)のに対して、順序回路にした改良型は、スライスが107(フリップフロップ118、ルックアップテーブル111)と30%も消費リソースが少なくなった。いやあ、鼻が高い。天井に届きそうだ。あんまり嬉しくてVerilogHDLのソースコードを公開してしまう(あくまでも習作です。間違いがあっても責任取れませんのでよろしく)。

//
// 16ビットバイナリを4桁の10進表示に換えるサブモジュール
//
module bin2d1000(pRST, pCLK, CTR, d4, d3, d2, d1);
input pRST;           // RESET
input pCLK;           // System Clock
input [15:0] CTR;     // source 16bit Binary
output [3:0] d4;      // BCD 1000
output [3:0] d3;      //     100
output [3:0] d2;      //     10
output [3:0] d1;      //     1

reg[15:0] WK,SAVE;   // work regs
reg FLAG;            // start/stop flag
reg[3:0] dg4,dg3,dg2,dg1;  // each order counters

always @(posedge pCLK or negedge pRST ) begin  //count loop
  if(!pRST) begin            // initialize variables
    SAVE <= 0; FLAG <= 0;
    dg4 <= 0; dg3 <= 0; dg2 <= 0; dg1 <= 0;
  end else
  if(CTR != SAVE) begin     // begin loop when CTR changes
   if(FLAG == 0) begin      // setup regs at the beginning
     WK <= CTR;
     dg4 <= 0;
     dg3 <= 0;
     dg2 <= 0;
     dg1 <= 0;
     FLAG <= 1'b1;           // do it only once.
   end
  else begin
   if(WK > 999) begin      // over 1000? then subtract 1000
   WK <= WK - 1000;
   dg4 <= dg4 + 1'b1;     // count up 1000 order
  end
  else
   if(WK > 99) begin      // over 100? then subtract 100
     WK <= WK - 100;
     dg3 <= dg3 + 1'b1;
   end   
   else   
    if(WK > 9) begin      // over 10? then subtract 10
      WK <= WK - 10;
      dg2 <= dg2 + 1'b1;
    end         
    else begin
      dg1 <= WK;
      FLAG <= 0;       //stop iteration
      SAVE <= CTR;     //save CTR for next request
    end
   end   // FLAG else if
  end   // SAVE if
end    // always begin

assign d4 = dg4;  // put each BCD
assign d3 = dg3;
assign d2 = dg2;
assign d1 = dg1;
endmodule         // end of module

UARTのコードを入れる。なかなか言うことを聞かない(4/2/2010)

A4032790

 練習課題の次のテーマのUARTである。仕様は、端末からのキーボード入力をエコーバックして端末に戻し、リターンキーを押すと、7セグLEDがカウントアップするというものである。余裕があれば、UARTから送られてきたASCIIコードを7セグLEDに16進表示させたい。

 UARTのソースコードは、色々な雑誌にサンプルとして載っている。VerilogHDLでは、デザインウェーブマガジン誌2008年10月号のものが、わかりやすかったので、このソースを拝借することにする。これ以外では、インターフェース誌2009年9月号、デジタルデザインテクノロジ2号にも出ている。

 別ファイルを起こし、サブモジュールとして作っていく。大した量ではないのでWebからダウンロードしないで手入力で入れてしまう。サブモジュールの練習をしてあったおかげで、接続も慣れてきて、UARTを入れたプログラムの上位レベルの論理合成はNoErrorとなった。下位(実際の結線のチェック)レベルの論理合成では山ほど警告メッセージが出るが、Webによれば無視して良い警告メッセージのようなので(Xst:1285 設定したピンの初期値が不定と言ったWarningなど)、そのままコンフィギュレーションまで進む。

 さあ、ファームが出来た。ハードウエアの準備をする。TTL-UARTは、例によって秋月のUSB-UARTモジュールを活用する。ブレッドボードに載せてジャンパーで接続する。そろそろ新しいブレッドボードを増やさなければならない。これまでのPCMプレーヤーのテストベンチの横を間借りする。

 わくわくしながら、ターミナルソフトを立ち上げ、テストを開始した。祈る気持ちでPCのターミナルのキーボードを打つ。エコーバックはない。リターンキーでカウントアップもしない。しかし、LEDを見ると、おお、何やら16進表示が出ている。受信はちゃんと動いているようだ。1を入れて31、jをいれて6A、素晴らしい。問題なくASCIIコードが出ている。皮肉なことに、つけたしで入れた機能が最初に動いた。

 タクトスイッチを押す。あれえ、いけない。とんでもない数字が表示され、押していると数字がくるくると動きっぱなしになる。何故だろう。最初、クロックでalwaysをまわしていたのを、UARTを入れる際、キー入力の立ち上がりでalwaysをまわすようにした。

   always @( posedge キー入力 or  posedge  UART入力)begin

というステートメントだ。この条件だと、always節内の処理は、キー入力が一回されたときのみ、動くはずなのだが、実際には、キーを押している間中、廻りっぱなしである。わけがわからない。何か大きな勘違いをしているのかもしれない。

 UARTの方は送信が動いていない。ターミナルに文字が戻ってこない。ソースを調べて送信リクエストのビットを上げていないことがわかり、このビットを上げると、今度は立て続けに同じコードを送り続けて止めるタイミングがわからない。やれやれ難しいものである。同時に色々なモジュールが一斉に動いているものを制御するのは大変だ。この日はこれで時間切れ。

UARTが無事動いた(4/3/2010)

A4032791

 久しぶりに電子工作に熱中している。マイコンをやっていますと言うと、何とか電子工作として理解してもらえるが、FPGAとなるともう誰も知っている人がいない。理解されない孤独な道を突き進んでいるという疎外感と、自分の趣味も遂に現代の電子技術の最先端まで来たか(まあ、まだ、ほんの入り口ですが)という誇らしい気持ちが入り混じって、このところ気分が安定しない。

 ともかく、この3日間はFPGAのコーディングに没頭していた。そして、遂にUARTと7セグLEDを使った練習プログラムは、所期の機能を実現した。キーボードを叩けば、そのまま文字が端末に表示され、リターンキーを押せば、カウンターが1つづつ増えていく。人から頂いたソースなので前ほどではないが、それでも嬉しい。

 スイッチを押すタイミングでしか動かないはずのalwaysの解析はあきらめて、クロックを使った前の方式に戻したら、スイッチは何事もなく確実に1つづつカウントするようになった。やれやれ。前のコードの解析はまたあとでやろう。

 UARTの送信が出来ない原因は、ここに書くのも恥ずかしい、変数の定義漏れだった。サブモジュールとの接続は慣れたとはいえ、wireとregの使い分けが難しい。それに親(top)モジュールで使わない変数は、親で定義しないでもつながるときがある。警告メッセージにはでていたのだろうが、他のメッセージの中で見落としたようだ。トップモジュールで、受信側と送信側をそれぞれ、wireで定義し、assignで接続したら、送信データは無事、ターミナルに戻ってきた。

Fpga_uart

 UARTがつながった。キーボードの入力文字が端末に表示されるだけで、何かディバイスが急に身近に感じられるというのも不思議なものだ。これでFPGAの開発は一段と楽になる。秋月電子のホームページを久しぶりに覗いたら、あの高速SRAMの在庫が戻っていた。アイテムラボにはこのSRAM用の変換基板を発注してある。次の第二ステップの準備は進んでいる。いよいよ次の難関、外付けメモリの接続が待っている。

|

« FPGAで7セグLEDのカウンターがやっと動く | トップページ | 外付けSRAMをFPGAにつなぐ »

FPGA」カテゴリの記事

コメント

すみません。2009年ではなくて、2008年10月号でした。FPGAの話題が続いてアクセスが急減していたのですが、読んでいただいていたのですね。早速修正しておきました。ありがとうございました。

投稿: がた老 | 2010年4月12日 (月) 21時14分

興味があるので、わかりやすかったと書かれている、デザインウェーブマガジン誌2009年10月号を買ってこようとおもったのですが、そもそも、デザインウェーブマガジンにこの号は無かった気がするのですが…。
間違いだったらすみません。

投稿: jujurou | 2010年4月12日 (月) 19時17分

コメントを書く



(ウェブ上には掲載しません)


コメントは記事投稿者が公開するまで表示されません。



トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/1089557/34065564

この記事へのトラックバック一覧です: FPGAのUARTで7セグLEDを動かすことに成功:

« FPGAで7セグLEDのカウンターがやっと動く | トップページ | 外付けSRAMをFPGAにつなぐ »