« 2010年3月21日 - 2010年3月27日 | トップページ | 2010年4月11日 - 2010年4月17日 »

2010年3月28日 - 2010年4月3日の2件の記事

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用の変換基板を発注してある。次の第二ステップの準備は進んでいる。いよいよ次の難関、外付けメモリの接続が待っている。

| | コメント (2) | トラックバック (0)

2010年3月29日 (月)

FPGAで7セグLEDのカウンターがやっと動く

7セグLEDが点灯した(3/26/10)
 VerilogHDLで書いた自前の7セグLED表示回路にタクトスイッチの回路を組み込む。生意気にシステムクロックを使ったチャタリング防止回路を入れる。always節の中の処理は順序処理なのでソフト屋にはコーディングしやすい。

 ただサブモジュール間のデータの受け渡しは、まだ完全に理解できていない。手探りである。基本は、regでデータを作り、ネット(wire)で受けるというのだが、サブモジュール間のデータの受け渡しのとき、メインモジュールに変数を定義しておかなくてもよいのか、サブモジュールでwireで受けた変数をそのまま変化させてよいのか、まだ確信がない。エラーになったところは、適当に定義をいじってNo Errorにするという対症療法でしのぐ。

 やっとのことで、タクトスイッチを押すと、その回数を99まで数えて7セグLEDに表示する練習課題のソースが論理合成でNo Errorとなった。次は、いよいよピンアサインである。おっと、まだFPGA基板の配線をすましていない。バラックでも良いが、デバッグに専念したいので、ベース基板にテスト用のソケットをつけてFPGA基板とLED基板の間を接続することにする。

 これが結構、時間がかかる。たかが10数本の配線だが、ピンアサインを良い加減にするとあとでひどいことになるので、なるべく元の基板の配置と同じで、しかも流用したストレートの電光掲示板のコネクターが入るようにピンアサインを決めようと苦心する。 7セグのエレメントの接続も気を抜いてハンダ付けするとすぐ間違える。メモをとり何度もテスターで導通を確かめながらの作業だ。2列20ピンのソケットは印をつけておかないとすぐ間違える。

A3292782

 基板のハンダ付けも終わった。今度こそピンアサインだ。雑誌記事の基板回路図を何度も確認しながら慎重にピン配置の.ucfファイルを作る。 Xilinxの開発環境ISEは操作性に問題があり余り評判が良くないが、このピン配置を決めるucfファイルの編集もやりにくい。

 一旦作ったucfファイルはUser Constraints(ユーザー制約定義)のGUI画面では編集できない。知らずに入力すると新たな制約定義を追記してしまう(ISE 10.1 )。編集はucfファイルをテキストエディターで開いて修正しないといけない(追記:GUIメニューにPre-SynthesisとPost-Synthesisの2つがあり、後者で修正が出来ることがわかった)。

 まあ、この開発環境はすべて無料なので(登録が必要だが)余り文句は言えない。しかし、これは統合環境というより、それぞれが別の仕事をするバッチジョブのランチャーに近い。ではバッチジョブとして独立しているかと思うと、沢山の中間ファイルが相互のジョブの途中経過を引き継いでいるので油断がならない。うまく行っているときは良いが、どこかのステップを失敗すると、とたんにご機嫌を損ねて先に進んでくれない。

 このときも二重に追記されたucfファイルを消したのだが、これに関係する内部ファイルが残っていて、新規にucfファイルを作っても同じエラーを吐いて動かない。結局、一旦そのプロジェクトをご破算にし、ソースコードだけ取り出して新しくプロジェクトを作り直したら先に進んだ(Clear Projectというのをやれば良いらしい)。

 ピンアサインが無事終わった。しかしFPGAを動かすまでの道は遠い。このあともまだたくさん手順がある。付録基板の号には詳しい手順が出ているのでもう一度おさらいし、慎重に進める。以前に1回通したというのが自信になっている(2008/8/8「FPGAでLEDピコピコ」記事)。幸い、Imprement Design(これがコンパイルにあたる)、Generate Programing File(実行可能ファイルを作る。リンカーか)は順調に終わった。

 残るは問題のiMPACTを使ったファーム書き込み(コンフィギュレーション)である。「鬼のように使いにくい」「難解きわまる」とWebで評判(?)のライターである。前回は、コンフィギュレーションROMをバイパスする手順を抜かして大はまりした。

Impact

 手順どおりiMPACTを始動させる。この始動方法にも色々な方法があるようで、おっかなびっくりである。ROMとFPGAのアイコンが出るConfigurationの画面が出た。手順どおりProgramの矢印アイコンをダブルクリックして書き込みを開始する。おお、順調のようだ。やった、やった、ブルーのSucceeded(出来たよ)のメッセージが出た。7セグLEDが点灯した。

 良かった。とりあえずはLEDが点いた。見ると、4つのLEDとも同じ形で激しく点滅しているだけだが、全くの無反応ではない。少なくとも暴走もせず(FPGAは暴走しないか)、何か動いている。タクトスイッチを押してみる。うん、何か変わった。数字にはなっていないが2番目の桁の形が変わったようだ。よーし、一歩前進したぞ。達成感で体が軽い。

A3272773

FPGAのデバッグは確かに大変だ(3/27/10)
 最初のFPGA自作プログラムは、無茶苦茶な点滅だけれど、とりあえず目的の7セグLEDを光らせることには成功した。次はデバッグである。全くの黙(だんま)りと違って何か動いている。手がかりは十分だ。

 激しく点滅するというのはダイナミック点灯の時間間隔が長すぎるからだろう。テレビのフレーム1/60秒に合わせて20msにしたが遅すぎるようだ。LEDが文字になっていないのはエレメントのアサインがおかしいからに違いない。

 LEDのピンアサインとソースコードをもういちど照合する。ああ、わかった。7セグ以外のエレメント、ドットポイントの位置を間違えている。ucfファイルでピンアサインを変える。ダイナミック点灯の間隔を1/10の2msに変更し、また最初の論理合成から始まる5つのステップを実行する。

 予想通り点滅は止まった。しかしまだ文字にならない。それにタクトスイッチを押して変わるLEDが一番右の桁にならない。ここも間違えているか。もういちどピンアサインを確認する。おやビットの定義がこれまでと違う。A[0] 、A[1]、A[2]はMSB(左から)からじゃないのか。そうなのだ。ここの配列はLSB(右)から数えるらしい。

 アサインを逆にして、やっと7セグLEDに数字が戻った。タクトスイッチを押すと数字が変わる。しかし、1づつ増えていかない。それに桁の位置がおかしい。うーむ、今日はこれくらいにしておこう。

同時に動くという仕組みを少し理解する(3/28/10)
 FPGAのデバッグは難しい。考えてみたらロジアナでは内部変数を外から見ることが出来ない。シミュレーターを入れれば良いのだろうが、そう簡単には入らない。それに7セグLEDくらいのアプリケーションで余りおおげさにしたくないという気持ちもある。まずは冷静になって最初からHDLコードを調べ直すことにする。

数字が大きく変わる不具合は新しく入れたスイッチのロジックが怪しい。スイッチがONになった直後、ただちにフラグをあげて、それ以上カウンターが動かないようにしているが、そのフラグが効いていないように見える。すると気になるところが見つかった。

if(A)       //begin endは文が単文のときは省略できるので
  if(B) begin     //この文は単文
    C;             //
    D;             //
  end             //
else begin  //このelseはif(A)のelseのつもり
  E;
  F;
end

Aが成立してBが成立すれば、CとD、そうでなければ何もしない。Aが成立しなければEとFを実行するという文だ。beginとendは複文の時だけ必要で単文のときはいらないので省略してあるが、よーく見ると、次のif(A)のつもりのelseは、字下げはしてあっても、if(B)の方のelseになってしまっている。

あーこれだ、これだ。昔々PASCALに凝っていた頃、さんざんやったミスだ。ここは面倒でも

if(A)  begin   //単文でもelseを取り違えないために必要   
   if(B) begin
     C;            
     D;          
   end
end                 //面倒だが、このbegin endがいる         
else begin  //これでやっとこのelseはif(A)のelseになる
  E;
  F;
end

としなければいけない。数字が大きく増える原因はここにちがいない。あせる気持ちでコンパイルしなおす。長い手順がもどかしい。コンフィギュレーションが出来たので動かしてみる。よーし、数字の増え方がひとつづつになった。

 おやあ、まだ時々1以上増える時があるぞ。うーむ、何故だ。スイッチの押し方で様子が変わる。チャタリング防止が機能していないようだ。どれくらい待っている? 300カウント待って同じならONとしている。待てよ、クロックは33Mhzだ。これでは少なすぎるのではないか。そうだ、10マイクロ秒しか待っていない。単位を間違えていた。千倍の300000(10ms)にしてチャタリングは完全に防止できた。

 表示される桁がおかしいのはもっと深い理由があった。ソフト屋はどうしてもステートメントが表記の順序で実行され、それが終わってから次のステートメントが実行されるという意識が頭から抜けない。always節の中は、順序回路だと言っても、実際の動作はわずかな遅延を伴って同時に処理される。

A3282780

 ダイナミック点灯のデータの指示をしたあと点灯する位置をシフトしているが、実際に点灯の処理がすんでからシフトが行われる保証はない。回路の具合で点灯する前に表示桁が移動するかもしれない。桁のずれがシフトの方向にずれているのが何よりの証拠だ。

 これは、シフトをブロッキング代入で最初にやり、点灯のデータの指示をノンブロッキング代入(always節の最後で一括変更)すれば解決するはずだ。ソースを組みなおす。これで遂に7セグLEDは正しい桁で数字が表示された。

 いやいや、良い勉強になった。参考書や雑誌に書いてあるブロッキング代入とノンブロッキング代入の違いをデバッグを通して肌で理解することができた。だいぶコーディングに自信がついてきた。次はUARTだ。

| | コメント (0) | トラックバック (0)

« 2010年3月21日 - 2010年3月27日 | トップページ | 2010年4月11日 - 2010年4月17日 »