で、時計はどうだろう? プロセッサなしで動かすには一体どうするのか? 実は意外にも簡単なことかも知れない。
構想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 (ソースコード)
関連記事
・ 『QFN32の FPGA』
・ 『TTL ALU 74281』
・ 『FPGA時計の設計』
・ 『MCPU -- A Minimal 8 Bit CPU』