« FPGAのSPIインターフェース遂に完成 | トップページ | フォトフレーム: デジタル液晶を動かす準備 »

2010年8月26日 (木)

エラーのないFPGAのUARTコードを自作する

 FPGAのフォトフレームプロジェクトは、いよいよ第一期完成フェーズにあたる第7ステップに進んだ。SDカードにあるビットマップ画像データを液晶モニターに表示する段階である。フォトフレームを作ると決めてブログで宣言したのが今年の2月末。ほぼ6ヶ月かけてここまできたことになる。

A8263052

 このあいだには、XilinxのFPGAチップと液晶モニターをプローブの誤接触で壊したり、HDL言語(VerilogHDL)の習得にてこずったり、今回も多くのドラマ(?)が生まれた。電子工作を始めて2年半、自分でも感心するほど多種多様な工作をしてきたが、ひとつのプロジェクトにこんなに長く(半年)かけたことはない。FPGAはやはりてごわい。

 これまで何度も書いているように、当研究所では電子工作には必ず何らかの実用的(こじつけでも良い)な目的を決めてから、工作を始めることにしている。定年後の楽しみに始めたアマチュアの電子工作である。何をやろうと、どこでやめようと誰もとがめる人はいないのだが、これだけ目的にこだわるのは理由がある。

 自分は飽きっぽい性格で、めげやすい。何の規制もせずにやりたいことだけやっていたら、いつまでたっても技術の向上は望めない。電子工作は幸い、初心者からプロまで、どんなレベルであろうと、それなりに夢中になり楽しめる世界だが、悔しいことに技術レベルが上がれば上がるほど楽しめる範囲が広くなっていくことは否定できない。

 何でも良いから具体的な目標(仕様)を設定しておくと、何とかそれを実現しようと、一生懸命に工夫、努力をする。飽きっぽい割には、一旦こだわると意地になる習性がある。しかし、これによって技術レベルは確実に上がる。遊びとは言いながら少しでも技術レベルを上げて今まで出来なかったことを可能にし、世界を広げたい。

 当研究所の所長は、この40年間、ほぼコンピューターのシステム開発を仕事にしていた(後半は企画と管理ばっかりだったが)。この世界ではコンピューターのハードウエアというものはいわば神が創ったものであり、これに注文をつけることはとんでもないことで、仕様どおり有難く使わせていただくものだった。

 PCやマイクロコンピューターでも似たようなものである。しかしFPGAはこういう今までの常識をくつがえすディバイスである。作ろうと思えば、自らが思いのままプロセッサーを自作できるのだ。何とかこのディバイスを自由自在に操れるようにしておきたい。

 フォトフレームプロジェクトは現物を作るだけでなくFPGAをマスターするということもプロジェクトの大きな目的にしている。そんなこともあってまた少し道草を食って、本来のフォトフレームとは関係ないFPGAのUARTにはまりこんでいた。

雑誌のサンプルUARTソースがうまく動いていない(8/16)
 SPIのバグ取りに散々苦労して、わかって見ればその原因はUARTの方だったという話は前回にした。このFPGAのUARTのVerilogHDLソースコードは、雑誌のデザインウエーブマガジン(その後季刊のデジタルデザインテクノロジーに改称)2008年10月号P86のサンプルソースをそっくり利用させてもらっている。

 雑誌のサンプルソースにバグはないはずなのだが、SRAMからのUART出力には明らかにエラーが出ている。エラーの頻度はそう高くないので、これまで気が付かなかった。これとは別のfpga4fun.comのUARTに較べれば、はるかに安定していると思っていた。

 しかし、詳しく調べると、1KBに数文字の字化け(エラー)があるようだ。特にリセット直後の数文字はたいていおかしい。フォトフレームプロジェクトではそろそろデジタル液晶の準備にかからなければいけない(ケース工作はほぼ完成)のだが、ここはそれ、しつこい性格である。気になると、他の事に手が付かなくなる。この際、徹底的にデバッグすることにした。

 折りしも、SPIのトラブルのとき勧められた、「HDLによる高性能デジタル回路設計」という本を入手し、FPGAを本格的に勉強しはじめたこともある。ロジアナを使って、雑誌サンプルソースの不具合の原因解明を始めた。

Dwv_uart

 まず、犯行現場の現場検証が重要だ。字化けの事実を検証する。テスト用にボーレートカウンターの動きをFPGA出力ピンに出すコードをソースに追加して、これをロジアナで拾い、少しづつデータを送ってはロジアナでタイミングチャートを分析する。

 その結果、どこで字化けが起きるかは、ほぼ確定できた。UARTのデータの送出順序はLSBファーストである。字化けは必ずMSBで起きている。データの最後の方を集中的に調べていくと、添付画像にあるように、8ビット目の最後がボーレートの半分で終わり、そのままストップビットになるところがいくつも見つかった。

 UART受信はボーレート期間中の多数決論理でHかLを決めているので、ボーレートが少しずれると半分の幅しかないMSBで0のデータが1になる可能性がある。これが前回の記事で1や2が半角のアやイになる理由である。スタートビットはこのあと正しく出ているので、エラーはここだけに止まる。

 字化けが起きる原因はつきとめられた。しかし何故、このUARTの最後のビットがボーレート通りの時間にならないかがわからない。ソースコードにはおかしなところはない。きちんとボーレートの時間通りウェイトが入っている(はずである)。

「HDLによる高性能デジタル回路設計」で勉強する。(8/18)
 すんさんの掲示板で勧められ、絶版だと聞いていたが、秋葉原の書泉で探してみたら最後の一冊らしいカバーが少し汚れたものが見つかり早速手に入れた。

Hdl

 噂には聞いていたが、高度な内容である。非同期回路だけでなく、同期回路も安心ならないことを学ぶ。それ以外にも考慮しなければならないことが沢山ある。自分がいかに能天気にRTL設計をしていたかを思い知らされる。

 雑誌UARTのソースコード(動作環境 Altera QuartusⅡ)は、この本を読み進むうち、かなりな問題があることがわかってきた。まず、送信要求のビットはキーボード押下で発生する非同期パルスだが、その配慮がなされていないこと、状態遷移のレジスターが多段(5ビット)で、単なるバイナリーコードであるうえに、これをインクリメントするステップと、状態遷移をするロジックが同一クロック時に動いており、これが正しく評価されない可能性があることなどである。

 本に拠れば、非同期はもちろんであるが、同期回路といえども、ハザード(出力が不定になるところ)は、常に起きる可能性があり、その対処が必要としている。それから言えば、この雑誌のサンプルソースはあやしいところが沢山ある。

 それにこのソースには不可解なところがある。UARTは非同期通信なので、同期シリアル(USART)と違ってデータはクロックのエッジではなく、HighかLowでデータを採取する。それなのにサンプリング周波数はボーレートの2倍にとってある。少なくとも送信の場合はオーバーサンプリングしても無意味である。

 恐らく、受信側のモジュールとボーレートを共通にするためだと思うが、ソースコードを読めば読むほど、何かあやしくなってきた。具体的にどこでハザードが起き、それによってボーレートが最後だけ短くなるのかは、まだ経験が浅くてつきとめられない。しかし現実に字化けが起きていることは事実である。

 こうなったら、自前でUARTのソースコードを書いてみたくなってきた。参考書で学んだテクニックを応用する絶好の機会だ。

ここまで勉強したら自分で作ってみよう(8/21)
 擬似コーディングを念入りにやって、自前のUART送信コードを書き始めた。UARTは、クロックパルスを持たない非同期通信なので、スタートビットからの時間間隔(ボーレート)だけでデータをHかLに設定して送信する。雑誌の2倍サンプリングをやめて、ボーレートだけのステートマシンにする。

 自慢は、ステートレジスターのグレイコード化(1ビットづつ変化する遷移ビット)と、同時に多数のビットを変えないロジック、ボーレートカウンターのような多数のビットを変更するときは、ステートをあけて結果を利用するなどのテクニックだ。半日ばかりで出来た。おお、前よりステートメント数も少なくなっている。これで動けばいうことなしだ。

 期待に胸をふくらませて論理合成する。祈る気持ちでUART端末を立ち上げる。ARMからデータを送り、FPGA側のUARTのスペースキー(出力指示)を押した。やった、やったぞ、最初の文字から全く誤りなくテキスト文書が表示される。キーを何度も押す。すごい。全くエラーがない。このあいだのXbeeの電力ロガーのデータはテキストデータで定型なので、少しでもエラーがあると目立つのだが、どこまでも綺麗に出力される(前は、3回に一回はどこかしらデータの乱れがあった)。

Uart_ok

 いやあ満足、満足である。勢いに乗って消費リソースを調べてみた。今度のソースコード全体のスライスは366、前は382だった。差はわずかだが、このスライス量はすべてのルーチン(UART受信、SRAMアクセス、ビデオ信号、SPI、10進変換など)を含んでいる。送信UARTだけならスライス量は80程度と考えられ、それでこの差は大幅な削減だ。嬉しい。

 調子に乗って、またソースコードを公開してしまうことにした。送信の部分だけだが、Latticeの環境では、間違いなく雑誌のコードより信頼性の高いVerilogHDLソースである。なおコメントに日本語が入っているが、これは公開のために追加したもので、Latticeのエディターでは日本語は化けてしまうので注意されたい。

//------------------------------------------------------------
// UART送信モジュール   8/25/2010 (C) LABO Gataro
//
//  CLKはマスタークロック、BAUDは受信モジュールと共通のパラメーター
// (受信モジュールは、DWM2008年10月月号P86を利用している)
//
//   メインで以下のステートメントが必要(38.4kbpsのとき)
//
//   parameter CLK  = 33000000;        // FPGA master clock 
//   parameter BAUD =  CLK/(38400*2);  // 38.4kbps 8bit 1start/stop
//   assign div = BAUD;
//
module UART_TX( so, pi, xmit, rdy, div, pCLK, pRST );
output     so ;      // transmitted out シリアル出力
input[7:0] pi;       // data to transmit 送信データ(8ビット)
input xmit;          //start transmit flag   (req)  送信要求
output rdy;          // rdy=0 : in send proc (busy) ビジーフラッグ
input[10:0] div;     // baud rate count from main module ボーレート
input pCLK;          // master clock
input pRST;          // master reset(XP2 is positive reset)

reg [7:0] ps8;
reg       so ;
reg [1:0] state;
reg [10:0] dc ;
reg       rdy ;

reg [4:0] bitctr;    // including start/stop bit
wire [10:0] div2;
assign div2 = {div[9:0],1'b0};  // multiply by 2 for TX

parameter IDLE = 2'b00;  //state reg in gray code
parameter DSET = 2'b01;
parameter WAIT = 2'b11;

always @(posedge pCLK or posedge pRST ) begin
  if(pRST) begin          // initial process if reset
   ps8 <= 8'h00;          // clear output reg
   state <= IDLE;         // initial state
   dc  <= div2;           // load baud rate couner
   rdy <= 1'b1;           // inform TX ready
   so  <= 1'b1;           // UART is negative logic(active=0)
   bitctr <= 4'h0;        // confirm initial status
  end
  else begin
   case(state)
    IDLE: if(xmit == 1'b1) begin    // if tx request
            ps8 <= pi;              // set 8bit tx data
            state <= DSET;          // move to next state
            bitctr <= 4'h0;         // initialize counter
            rdy <= 1'b0;            // set busy bit
          end

    DSET: begin
           if(bitctr == 4'h0)      so <= 1'b0; // start bit
           else if(bitctr == 4'h9) so <= 1'b1; // stop bit
                else begin
                  so <= ps8[0];            // put data on serial line
                  ps8[6:0] <= ps8[7:1];    // shift data reg
                end
           bitctr <= bitctr + 4'h1;       // shift 1 bit
           state <= WAIT;                 // go to wait state
          end

    WAIT: begin
           dc <= dc - 11'h1;            // decrement baud rate counter
           if(dc == 11'h0) begin        // refill baud rate value
             dc <= div2;
             if(bitctr > 4'h9) begin  // goto IDLE after stop bit sent
               state <= IDLE;
               rdy <= 1'b1;           // set ready bit
             end else
               state <= DSET;  //repeat 10 times(start + 8bits + stop)
           end
          end
    endcase               
  end  // end of else after Init
end   // end of always clause
endmodule  // UART_TX

|

« FPGAのSPIインターフェース遂に完成 | トップページ | フォトフレーム: デジタル液晶を動かす準備 »

FPGA」カテゴリの記事

コメント

がた老です。早速のコメントありがとうございました。

>かなり腕が上がった証拠ですね。
いやいや、褒められるとは思わなかったので嬉しいですね。ありがとうございます。

>何かの状態(stateとか)をデコードして生成した信号だと
>ハザードの危険が出てきます。
あの雑誌ソースは、ステートレジを足し算で作っていますので、これが問題かと思っています。ステート15から16は、01111から10000になり5ビットが一気に変わります。これがMSBのボーレートが短くなる原因かと思いましたが、MSBのステートは17と18。追求はこれ以上あきらめました。

投稿: がた老 | 2010年8月27日 (金) 13時55分

雑誌に載っているソースの問題点が見えてくるのは
かなり腕が上がった証拠ですね。

同期回路内で発生するハザードは問題ありません。
ハザードがあっても、その信号を参照するのはするのは
次のクロックのタイミングなので、そのときに信号が安定
していればいい訳です。
PINに出力する信号にハザードがあっては問題になります。
出力PINに接続される信号がDFFの出力そのものなら
原理的にハザードはありません。
何かの状態(stateとか)をデコードして生成した信号だと
ハザードの危険が出てきます。
always @(posedge pCLK)内で出力信号を直接生成すると
その信号はDFFの出力そのものになるので、ハザードの
心配はありません。

投稿: kuga | 2010年8月27日 (金) 05時19分

コメントを書く



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


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



トラックバック


この記事へのトラックバック一覧です: エラーのないFPGAのUARTコードを自作する:

« FPGAのSPIインターフェース遂に完成 | トップページ | フォトフレーム: デジタル液晶を動かす準備 »