”FPGAで遊んでみる”では、セミナー用に作成したサンプル回路などを整理して少しづつ紹介していきます。


Papilioの本
FPGA版Arduino!!Papilioで作るディジタル・ガジェット CQ出版 発売中

セミナ開催します。
"実習・MAX10 NiosIIプロセッサ活用超入門”
2017年8月23日(水)、CQ出版社セミナルーム
DE10-Liteを使ってMAX10上でNisoIIを動かします.
詳しくはこちらへ


第2回 LEDをチカチカさせてみる

 今回は、簡単なサンプルということでFPGAからLEDをチカチカさせる回路を紹介します。
手っとり早くFPGAボードが生きているか確認したり、開発環境の立ち上げするのに重宝します。

LEDチカチカ回路その1のソース

module led_control ( clk, rstb, led_0);
  input clk;    //clk 40MHz
  input rstb;   //reset_b
  output led_0; //led control 0:on 1:off
  reg  led_0;
      reg  [25:0] led_cnt;

      parameter led_cnt_1s = 26'd39999999; //1S CLK=40MHz
      //parameter led_cnt_1s = 26'd3999; //sim用パラメータ

//カウンタ   
always @ (posedge clk or negedge rstb )
  if (rstb==1'b0)
    led_cnt <= 26'b0;
  else if (led_cnt == led_cnt_1s) //1秒経過 
    led_cnt <= 26'b0;             //カウント値を0
  else
    led_cnt <= led_cnt + 26'd1;   //カウントアップ
      
      //LED制御
always @ (posedge clk or negedge rstb )
  if (rstb==1'b0) 
    led_0 <= 1'b0;
  else if (led_cnt == led_cnt_1s ) 
    led_0 <= ~led_0;              //LED反転
  else
    led_0 <= led_0;

endmodule


仕様説明

ここから回路の動作と使い方を中心に解説します。Verilogの文法の解説は書籍などを、ISEの操作はxilinx社のサイトを参考にしてください。

 動作概要
  40MHzのクロックを入力して、カウンタで1秒を測定してLEDを点滅する。

 入力信号
  clk:クロック入力/今回使用してたボードにあわせて40MHz
  rstb:リセット入力/今回使用したボードのswがプッシュで0なので、0でリセット。

 出力信号
  led_0:LED制御信号/今回使用したボードでは0で点灯です。

 レジスタ
  led_0:LED制御用DFF/出力信号led_0の値
  led_cnt:時刻計測用カウンタ/取り合えす26bit

 パラメータ
  led_cnt_1s:1秒のカウンタ値を設定します。入力クロック周波数に合わせます。
        今回は40MHzなので39,999,999ににしてあります。
        別のクロック周波数を使うときは値を変更してください。
        コメントアウトしてある値は、シミュレーション実行時間を短くするための周期を短くした設定です。

 内部動作
 カウンタ:リセットで0になり、1CLKごとに+1します。カウント値がled_cnt_1sに達すると0に戻ります。

//カウンタ   
always @ (posedge clk or negedge rstb )
  if (rstb==1'b0)
    led_cnt <= 26'b0;
  else if (led_cnt == led_cnt_1s) //1秒経過 
    led_cnt <= 26'b0;             //カウント値を0
  else
    led_cnt <= led_cnt + 26'd1;   //カウントアップ

 回路図にすると下の図のようになります。



 LED制御:リセットで0になり、カウント値がled_cnt_1sに達すると値が反転します。これでLEDが点滅します。

      //LED制御
always @ (posedge clk or negedge rstb )
  if (rstb==1'b0) 
    led_0 <= 1'b0;
  else if (led_cnt == led_cnt_1s ) 
    led_0 <= ~led_0;              //LED反転
  else
    led_0 <= led_0;

回路図にすると以下のようになります。

カウンタと同じ比較器がありますが、論理合成の最適化で一つの比較器にまとめられます。


シミュレーションでの動作確認

ISEシミュレータでシミュレーションしています。テストベンチは,ISEの"NewSourceWizard"で作成したものに
clkとrstbの波形変化を追加しています。また、led_controlの記述でled_cnt_1sの値を3999にしてシミュレーション時間
を短縮しています。

テストベンチのソース

module led_control_tb;

// Inputs
reg clk;
reg rstb;
// Outputs
wire led_0;

// Instantiate the Unit Under Test (UUT)
led_control uut (
  .clk(clk), 
  .rstb(rstb), 
  .led_0(led_0)
);

initial begin
  // Initialize Inputs
  rstb = 1;

  // Wait 100 ns for global reset to finish
  #100;
  rstb = 0;
  #125;
  rstb = 1;
  // Add stimulus here
end

initial begin
  // Initialize Inputs
  clk = 0;
  repeat(50000) begin
    #12.5;
    clk = 1;
    #12.5;
    clk = 0;
  end
  $stop;
end
      
endmodule

シミュレーションの波形


カウンタが3999になると、カウンタ値が0になり、led_0が変転している部分の波形です。


FPGAボードでの動作確認

 ターゲットボードは前回も使用したマルツパーツのMFPGA-SPAR3Eです。
MFPGA-SPAR3Eは以前にDesignWave誌の付録付いていたspartn3e搭載ボードの復刻版で
マルツパーツの通販サイトから購入可能です。
MFPGA-SPAR3Eには、クロック発生器が実装されていませんので、一緒に購入してくだださい。
いずれ、SVGA表示回路のサンプルを紹介する予定います。この回路のドットclkが40MHzなので
SVGA表示回路を試したい人は、40MHzの発信器にしてください。
ダウンロードケーブルは第1回で紹介してFT2232Dモジュールを使っています。

FPGAの端子配置は回路に従って指定します。
clkはクロック発信器と接続しているPIN_88と接続、
rstbはプッシュSWと接続している PIN_89と接続、
led_0はLEDと接続しているPIN_38と接続しています。

UCFファイルの端子配置指定は以下になります。

NET "clk" LOC = P88;
NET "clk" IOSTANDARD = LVCMOS33;
NET "led_0" LOC = P98;
NET "led_0" IOSTANDARD = LVCMOS33;
NET "rstb" LOC = P89;
NET "rstb" IOSTANDARD = LVCMOS33;


動作の様子


ダウンロードケーブルは第1回で紹介してFT2232Dモジュールを使っています。


その1の回路では出力系の処理だけなので、リセットする以外の動作の変更がありません。
これではつまらないので、入力系の処理を追加してswスイッチを押した時間でチカチカの周期が変る回路を紹介します。

LEDチカチカ回路その2のソース

module led_control ( clk, rstb, led_0);
  input clk;    //clk 40MHz
  input rstb;   //reset 0:reset 
  output led_0; //led control 0:on 1:off
  reg  rstb_dff1;
  reg  rstb_dff2;
  reg  rstb_dff3;
  reg  rst_sig;
  reg  start_sig;
  reg  led_0;
  reg  [31:0] led_cnt;
  reg  [31:0] led_cnt_max;

//スイッチ入力のメタステーブル対策   
always @ (posedge clk  )begin
   rstb_dff1 <= rstb ;
   rstb_dff2 <= rstb_dff1 ;
   rstb_dff3 <= rstb_dff2 ;
end

//入力信号の立下り検出 カウンタ初期化信号
always @ (posedge clk  )begin
  if ((rstb_dff2 == 1'b0)&&(rstb_dff3==1'b1))
    rst_sig <= 1'b1;
  else
    rst_sig <= 1'b0;
end

//入力信号の立上がり検出 カウント開始信号
always @ (posedge clk  )begin
  if ((rstb_dff2 == 1'b1)&&(rstb_dff3==1'b0))
    start_sig <= 1'b1;
  else
    start_sig <= 1'b0;
end

//カウンタ最大値の記録   
always @ (posedge clk )
  if (rst_sig==1'b1) 
    led_cnt_max <= 32'hffffffff;
  else if (start_sig == 1'b1)
    led_cnt_max <= led_cnt;

//カウンタ
always @ (posedge clk )
  if (rst_sig==1'b1) 
    led_cnt <= 26'b0;
  //カウントスタートorカウンタ最大値
  else if ((start_sig == 1'b1)||(led_cnt == led_cnt_max))
    led_cnt <= 26'b0; //カウンタを0
  else
    led_cnt <= led_cnt + 26'd1; //カウントアップ

//LED制御
always @ (posedge clk)
  if (rst_sig==1'b1)
    led_0 <= 1'b0;
  else if (start_sig == 1'b1)
    led_0 <= 1'b1;
  else if (led_cnt == led_cnt_max) 
    led_0 <= ~led_0;
  else
    led_0 <= led_0;

endmodule


仕様説明

 
動作概要 
  プッシュSWを押した(0になる)時間のを記録して、その周期でLEDが点滅する

  入力信号
  clk:クロック入力/今回使用してたボードは40MHzでした。
  rstb:初期化および周期設定入力/
    本当rstbはリセット専用に使いたいところですが、ボードにswが1個しか実装されいないので
    初期化と周期設定の兼用にしています。

 出力信号
  led_0:LED制御信号/今回使用したボードでは0で点灯です。

 レジスタ
  rstb_dff1:rstb入力のりタイミング用DFF 1段目
  rstb_dff2:rstb入力のりタイミング用DFF 2段目
  rstb_dff3:rstb入力のりタイミング用DFF 3段目
  rst_sig:rstbが1->0の変化で1、カウンタの初期化および測定開始
  start_sig:rstbが0->1の変更で1、測定終了および点滅のためにカウンタカウンタ初期化
  led_0:LED制御用DFF/出力信号led_0の値
  led_cnt:時刻計測用DFF 1秒版より長い周期を扱うため32bitにしました
 l ed_cn_maxt:カウンタの最大値

 内部動作
 rstbの入力処理:入力信号をDFFで叩いています。メタステータタブル対策で2段DFFを入れてあります。 
 入力信号の立ち下がり検出:ratb_dff3=1でrstb_dff2=0の条件で立ち上がり検出
 入力信号の立ち上がり検出:ratb_dff3=0でrstb_dff2=1の条件で立ち上がり検出

//入力信号の立下がり検出 カウンタ初期化信号
always @ (posedge clk  )begin
  if ((rstb_dff2 == 1'b0)&&(rstb_dff3==1'b1))
    rst_sig <= 1'b1;
  else
    rst_sig <= 1'b0;
end

//入力信号の立上がり検出 カウント開始信号
always @ (posedge clk  )begin
  if ((rstb_dff2 == 1'b1)&&(rstb_dff3==1'b0))
    start_sig <= 1'b1;
  else
    start_sig <= 1'b0;
end

回路は以下のようになります。


 カウンタ最大値の記録処理:strat_sig=1の時にled_cnt_maxの値を記録します。

//カウンタ最大値の記録   
always @ (posedge clk )
  if (rst_sig==1'b1) 
    led_cnt_max <= 32'hffffffff;
  else if (start_sig == 1'b1)
    led_cnt_max <= led_cnt;

回路は以下のよううになります。



 カウンタ動作:rst_sig==1'b1またはstart_sig=1またはカウント値がled_cnt_maxに達すると0に戻ります。
          これ以外はカウントアップします。rst_sig=1ぁらstart_sig=1間は間隔の測定用に動作しています。
          基本的な動作はその1の回路と同じですが、非同期リセットから同期式の初期化に変更しています。
          また、start_sig=1でも初期化されます。

//カウンタ
always @ (posedge clk )
  if (rst_sig==1'b1) 
    led_cnt <= 26'b0;
  //カウントスタートorカウンタ最大値
  else if ((start_sig == 1'b1)||(led_cnt == led_cnt_max))
    led_cnt <= 26'b0; //カウンタを0
  else
    led_cnt <= led_cnt + 26'd1; //カウントアップ


 LED制御:動作は、その1の回路と同じですが、非同期リセットから同期式の初期化に変更しています。

//LED制御
always @ (posedge clk)
  if (rst_sig==1'b1)
    led_0 <= 1'b0;
  else if (start_sig == 1'b1)
    led_0 <= 1'b1;
  else if (led_cnt == led_cnt_max) 
    led_0 <= ~led_0;
  else
    led_0 <= led_0;


シミュレーションの波形


rstbが5clk間"0"になっているので、5clk周期でled_0の値が反転しています。

動作の様子


回路の再コンパイルなしで、スイッチを押した時間によってEDの点滅周期が変るので、
ダイレクトに回路を制御してる感じが、なんだか楽しいです。
この回路には、入力のチャタリング防止回路が入っていませんので、SWをカチャカチャと何回か押していると
チャタリングによって短い点滅周期に設定されて、LEDが点きっぱなしに見えることがあります。
チャタリングによる誤動作を体験してみることも良い経験になりますので、SWをカチャカチャしてみてください。
チャタリング防止回路はいずれ紹介いたします。

紹介した回路を試す場合は、自己責任でお願いします。
次回は、PCとシリアル通信するモジュールを紹介する予定です。

リンクフリーです。
リンクされた場合にご連絡をいただけると嬉しです。
メール


変更履歴
(2010/09/08) 初版
(2010/10/16) 項目の先頭にを追加,ソースコードのカラム表示の修正


TOPへ戻る