2012年06月13日

FPGA時計の設計

QFN32の FPGA』 を使ったなにかを設計してみたい。この際 AVR 使った方が有利で楽なものでも構わないが、実際に動かせるもの。ALU では、それを使ったものの敷居が高い。

で、時計はどうだろう? プロセッサなしで動かすには一体どうするのか? 実は意外にも簡単なことかも知れない。

構想1

    まず出力。今は 4桁の 7セグが入手できるからそれを使う。時計の : がないが、とりあえずは気にしない。気にする場合は、(配線が面倒になるのと引換に) 2桁の 7セグ x2 で間に LED を 2個入れられるようにして置けば良い。

    この FPGA だが ... DRIVE 能力の設定がある。LVCMOS33 だと デフォルト 8mA 、最大では 24mA の設定ができる。ただ、24 mA 流せてもコモンは 7 倍になるから 足りない。ドライバは必須。カソードコモンだと NPN トランジスタ が使えて楽そうな気もするのだが、7セグ側を H にして点灯させないといけない。そうなると白色 LED を使うのは厳しくなる。

    一応アノードコモンを使い 7セグ側を L で点灯ということにしておきたい。コモンのドライブも L 。こういったものは、出来た後見直すことにしよう。

    ピン数の合計 7 + 4 + 1(2) = 12(13)

      使える I/O は 21 しかない。ちゃんと入るものなのかどうか、常に気にするようにしよう。

    次にクロック。ダイナミック点灯や ボタンでのオペレーションを考えて 128 Hz を基本としておこう。 8MHz なら 128 で割り切れる。32768 Hz を原発信にしたい気もする。これは、あくまで最初の想定。出来上がってしまえば、対応できる範囲というのも分かってくるだろう。

    さて、これはどうやって入力するか -- 。取り敢えずは、外部のオシレータを想定すれば良い。ただ、水晶発振回路を内蔵したい。ピン数は、2 と見積もっておく。

    ピン数の合計 14(15)

    入力。時刻の設定は出来ないと困るだろう。最も簡単なのは、SEL と UP の 2 つ。DOWN も付けると、よくある時計のオペレーションになるはず。一応 3 つと見積もる。

    ピン数の合計 17(18) --- 残り 4(3)

    あと最大 4 つしか余らない。まぁ機能など付ける余裕もなさそう。JTAG はできれば空けときたいので、4 つ余らせることにしよう。

構想2 カウンタと表示

    ここからは、内部をどうするか ...

    まず時計というからにはカウンタが必要だ。それをどういう風に設計するか?

    前提としてダイナミック点灯するわけだ。ならば、出力は 4bitの BCD 1つで良い。これを 7seg エンコーダを通して 表示させるわけだ。

    考えたのだが、0〜59 や 0〜24 の 2 桁カウンタのモジュールをまず作ろうと思う。最大値は モジュールのパラメータで指定。あと 出力は 4bit で HI/LO を切り替える SEL 入力を持つことにする。

    入力は、128Hz のクロック と カウンタ動作の指定 UP(/DOWN)。あと上位に対する UP(/DOWN)。まずは、DOWN は後回し。回路が入るようなら検討。

    7seg エンコーダは、分散メモリを使ったテーブル ... にしたかったのだが Lattice の場合、妙な Warning が出るので assign と ? 演算子だけで何とかしようかと思う。(テーブルと同じ効果になるはず)。

構想3 カウンタと表示(続き)

    上のカウンタモジュールを 3 つ使い 時分秒に割り当てる。出力も 3 つ。とりあえず、時分 と 分秒 を表示する 2 つのモードを作ろう。

    セレクタでのこの 3 つの出力を切り替えられるようにして、モジュール内の HI/LO を切り替え を合わせてダイナミック点灯させる。

      ダイナミック点灯の 2bitカウンタがあるとする。下位は HI/LO に割り当てる。上位だけ考えれば良いのだ。

      0 1
      disp_mode 0 HH MM
      disp_mode 1 MM SS

      こうなるようにすれば良い。

    さて、これだけか? というと違う。時刻の設定である。時刻の設定ではセレクトされたものがブリンクする。設定モードについて考えておこう。

    モード自体の定義は、次のようにしよう。

      set_mode 0 : 通常表示 (HH:MM)
      set_mode 1 : 時設定 (HH:MM) で HH ブリンク
      set_mode 2 : 分設定 (HH:MM) で MM ブリンク
      set_mode 3 : 秒設定 (MM:SS) で SS ブリンク

      blink off blink on
      0 1 0 1
      set_mode 0 : HH MM HH MM
      set_mode 1 : HH MM -- MM
      set_mode 2 : HH MM HH --
      set_mode 3 : MM SS MM --

      ( -- : blank )

    だいぶややこしくなってきた。が、所詮これだけ。全部で 16 通りしかない。あと blink 信号は、クロックから 1Hz を取り出せば良い。

構想4 ボタン

    次は、どうやってボタンから入力をするかに飛ぼう。

    例えば、SELボタンを押したときは、set_mode をインクリメントするだけで良い。だが、どうやって押したと認識するのか? スイッチの入力をそのまま入れたのでは、128 Hz で set_mode が回転するだけだ。また、チャタリング対策というものも必要になる。

      (128 Hz で次の処理を行う)
      N 回 同じ入力が続いたら ボタンが押された、離されたと認識する。
      押された、離された という状態を作り、離された→押された と変化したとき ボタンON。
      使う側が、ボタンONを認識して リセット を送ってきたら (非同期に)ボタンOFF。

    こういう処理をするボタンモジュールを作ろうと考えている。

    N 回 同じ入力というのは、チャタリング対策。これを を認識するのに N -1 bit の レジスタが必要。N=4 にしたいなら、 128 Hzは、周波数が高すぎるかも知れない。これは、後で要調整。
 
実装編 カウンタ

    以上で重要な部分は説明した。後は実際のコードを示していこう。

    module clock_counter # (
    // parameter MAX = 60
    parameter MAX = 24
    ) (
    input CLK
    , input I_UP
    , input SEL // select output
    , output [3:0] O
    , output O_UP
    );

    reg [3:0] lower;
    reg [3:0] upper;
    reg r_ovr;

    assign O[3:0] = SEL ? lower[3:0] : upper[3:0];
    assign O_UP = r_ovr;

    always @(negedge CLK)
    begin
    if (I_UP)
    if ((upper >= (MAX-1)/10) & (lower >= (MAX-1)%10))
    begin
    r_ovr <= 1'b1;
    upper <= 0;
    lower <= 0;
    end
    else if (lower >= 9)
    begin
    r_ovr <= 1'b0;
    upper <= upper + 1;
    lower <= 0;
    end
    else
    begin
    r_ovr <= 1'b0;
    lower <= lower + 1;
    end
    else
    r_ovr <= 1'b0;
    end
    endmodule

    説明したとおりのもの。規模は1つ 10スライスのようだ。3つ使うから、これだけで 30 スライス。

実装編 ボタン

    module button
    (
    input CLK_128HZ
    , input I
    , output O
    , input R
    );

    reg [3:0] i_stat;
    reg prev_stat;
    reg r_out;

    always @(R, negedge CLK_128HZ)
    begin
    i_stat <= { i_stat[2:0] , I } ;
    if ( { i_stat[2:0] , I } == 4'b0000 )
    prev_stat <= 1'b0;
    else if ( { i_stat[2:0] , I } == 4'b1111 )
    prev_stat <= 1'b1;
    if (R) r_out <= 1'b0;
    else if (~prev_stat & ( { i_stat[2:0] , I } == 4'b1111 ))
    r_out <= 1'b1;
    end
    assign O = r_out;
    endmodule

    これもまた説明どおり。これは、4スライス。最低 2 つ使う。

一応完成

  • qfn32samples-03.zip

    Diamond プロジェクト込みのソースを置いておく。iVerilog シミュレータも一応通している。

    // XO2-256 modified ROM/RAM XO2-256
    // 27 B 1 24 VCC
    // 28 G 2 23 DIG1 25
    // 29 3 22 DIG2 23
    // 30 4 21 DIG3 21
    // 32 5 20 DIG4 20
    // 1 6 19 CLK_1HZ 17
    // 4 C 7 18 BTN_DN 16
    // 5 D 8 17 N.C.
    // 8 E 9 16 BTN_UP 14
    // 9 A 10 15 BTN_SEL 13
    // 10 F 11 14 CLK 12
    // GND 12 13 CLK_OUT 11

    ピン配置はとりあえずこうした。DIGは、LVCMOS33 の 24mA に設定。ACTIVE_HIGH に変更して、3mA 程度まで電流を減らせば直接ドライブできるかも。

    機能としては、
  • down ボタンを付けた
  • 7セグ制御の正論理・負論理を切り替えられるようにした。
  • クロックは、128HZ の倍数なら OK なようにした。

    一応説明で言及したものは、入れたことになる。-- これで規模は 86/128 スライス。
    機能追加はなかなか厳しそうだが、アラームクロックにするのは ... 可能かも。time_couner に比較用のレジスタを作って ... まぁ今後の課題にしておこう。

    追記: アラーム機能を付けてみた。

    .. といっても ポートを増やしたくなかったので、秒を示す LED を 1分間 高速点滅させるだけ。機能もちょっと見なおしている。


      set_mode 0 : 通常表示 (HH:MM)
      set_mode 1 : 時設定 (HH:MM) で HH ブリンク (アラーム)
      set_mode 2 : 分設定 (HH:MM) で MM ブリンク (アラーム)
      set_mode 1 : 時設定 (HH:MM) で HH ブリンク
      set_mode 2 : 分設定 (HH:MM) で MM ブリンク
      set_mode 3 : 秒表示 で SS ブリンク

      blink off blink on
      0 1 0 1
      set_mode 0 : HH MM HH MM
      set_mode 1 : HH MM -- MM
      set_mode 2 : HH MM HH --
      set_mode 3 : SS -- (秒表示のみ)

      ( -- : blank )

     ・ qfn32samples-04.zip (ソースコード)

関連記事
posted by すz at 20:03| Comment(0) | TrackBack(0) | CPLD
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/56454082
※ブログオーナーが承認したトラックバックのみ表示されます。

この記事へのトラックバック