せっかくPmodが1ポート付いているので,7セグLEDでも駆動しながら,ハマったポイントを紹介する.
Liteという名前のわりに,扱いはライトじゃありません.ヤッパリFPGAダッタヨ…
まずは,シミュレータが欲しいので次の通り準備する.筆者の環境は Windows である.
シミュレータの準備
Icarus Verilog と GTKWave の組み合わせでGo Configure Hubから直接呼び出せる.
準備1:Icarus Verilogインストール
Icarus Verilogをダウンロード してインストールする.ダブルクリックするだけ.筆者は v12 をインストールした.自動的にGTKWaveもインストールされる.
準備2:Go Configure Hubに設定
筆者はIcarus Verilogインストール中にPATHの設定チェックマークを外したので,Go Configure Hubのメニューから「Options」-「Settings」で開くダイアログのTools をクリックし,それぞれの実行ファイルがあるフォルダパスを設定した.

テストベンチを作る
tb1:新規テストベンチ
Go Configure Hub 左側のエクスプローラで右クリックして新規テストベンチを作る.
出来たファイル(テンプレートが生成される)に,Verilogでテストを書く.テストベンチの中で,テスト対象のモジュールをインスタンシエートすると,なぜがそのモジュールが見えないと言ってくるが,無視して進める(テスト対象のモジュール名とそのファイル名を合わせるとワーニングが消える).
生成されるテストベンチ
// Custom testbench
`timescale 1ns / 1ps
module top_tb;
initial begin
$dumpfile ("top_tb.vcd");
$dumpvars (0, top_tb);
$finish;
end
endmodule
tb2:シミュレータ起動
合成したら,Go Configure Hubのメニューにある 「Run RTL Simulation」 ボタンをクリックする.
ダイアログが開き,作ったテストベンチがリストアップされる.所望のものを選んで「Simulate」ボタンをクリックするとシミュレーションが走り,しばらくするとGTKWaveが開く.
7セグ4桁駆動
ハードウェア
仕様など
8桁あるPmodモジュールを使ったが,大きなレジスタを使うとP&RがFitしなかったため,4桁で我慢することにした.Utilizedはせいぜい10%程度なので,うまく組めば何とかはなりそう.
リソース少なめなのは致し方なし
クロックリソースがあまり無いようなので,なるべくシステムクロック以外を使わないように組む(always の処理の中にカウンタを入れた).ドキュメントにはクロックドメインは2つまでと書いてあるので,クロック2つは使えるのかもしれない.
Go Configure Hub内の設定で,P&Rツールのバージョンを選べる.新しいものは,物理制約ファイルに対応しているようだが,試していない.
記述できるVerilog文法は貧弱かも
Verilog 2005に対応しているらしい.
多次元のRegisterは宣言できないので,1次元で確保して,{目的行 × 1行の桁数 + 目的桁}でアクセスすることになる. 次の演算でハマった.
そして,
はまりポイント
segment_select は4ビットのレジスタであるが,どうやら segment_select * 8 も4ビットになるようだ.掛ける数を明示的に 7'd8 とすることで,実機でも所望の波形となった.
困ったことに,元の記述の場合,シミュレータでは狙った波形になっており,実機で別な挙動になるので,当初は????となり,気づくのに時間がかかった.
// bad data <= segment_map[segment_select * 8 + counter_16bit[2:0]];
data <= segment_map[segment_select * 7'd8 + counter_16bit[2:0]]; // good
コード全体
(* top *) module digit8_7seg(
(* iopad_external_pin, clkbuf_inhibit *) input clk,
(* iopad_external_pin *) output LED,
(* iopad_external_pin *) output LED_en,
(* iopad_external_pin *) output clk_en,
(* iopad_external_pin *) output sclk,
(* iopad_external_pin *) output sclk_en,
(* iopad_external_pin *) output rclk,
(* iopad_external_pin *) output rclk_en,
(* iopad_external_pin *) output srclr,
(* iopad_external_pin *) output srclr_en,
(* iopad_external_pin *) output serial_data,
(* iopad_external_pin *) output serial_data_en
);
// only single dimension. multi dimensional resistor is prohibited
wire [127:0] segment_map = {8'b1000_1110, //F
8'b1111_0010,
8'b0111_1010,
8'b1001_1100, //C
8'b0011_1110,
8'b1110_1110, //A
8'b1111_0110, //9
8'b1111_1110,
8'b1110_0000,
8'b1011_1110,
8'b1011_0110, //5
8'b0110_0110,
8'b1111_0010, //3
8'b1101_1010,
8'b0110_0000,
8'b1111_1100}; //0
reg [3:0] counter_16bit=0;
reg [1:0] digit_select=0; // for 4digit
reg [3:0] segment_select=0; // for 8segment LED(this is index of segment_map)
reg [15:0] target_number = 16'h0000;
reg [9:0] number=0;
reg data=0;
reg sclk_reg=0;
reg rclk_reg=0;
assign LED_en = 1'b1; assign clk_en = 1'b1;
assign sclk_en = 1'b1; assign rclk_en = 1'b1;
assign serial_data_en = 1'b1;
assign srclr_en = 1'b1;
assign LED = 1;
assign serial_data = data;
assign rclk = rclk_reg;
assign srclr = 1;
assign sclk = sclk_reg;
reg [15:0] counter=0;
always @ (posedge clk)begin
if (counter == 6_250) begin // 4000Hz
counter <= 16'b0;
sclk_reg <= ~sclk_reg;
number <= number + 10'b1;
if(number == 10'd1023)begin
target_number <= target_number + 16'b1;
end
if(sclk_reg)begin
if(counter_16bit == 4'd0)begin
rclk_reg <= 1;
end else begin
rclk_reg <= 0;
end
counter_16bit <= counter_16bit + 4'b1;
if(counter_16bit == 4'd0)begin // submitting of every digit data uses 16 clocks
digit_select <= digit_select + 2'b1; // for next digit
end
if(counter_16bit[3])begin
segment_select = target_number >>(digit_select*4);
// data <= segment_map[segment_select * 8 + counter_16bit[2:0]];
data <= segment_map[segment_select * 7'd8 + counter_16bit[2:0]];
end else begin
if(counter_16bit[2] && (counter_16bit[1:0] == digit_select))begin
data <= 1'b0;
end else begin
data <= 1'b1;
end
end
end else begin
end
end else if(counter == 6000)begin
rclk_reg <= 0;
counter <= counter + 1'b1;
end else begin
counter <= counter + 1'b1;
end
end
endmodule
テストベンチとか
// Custom testbench
`timescale 1ns / 1ps
module tb_tb;
reg clk=1;
digit8_7seg digit8_7seg_inst(
.clk(clk)
);
initial begin
$dumpfile ("tb_tb.vcd");
$dumpvars (0, tb_tb);
#10000000; $finish;
end
always begin
#1; clk <= ~clk;
end
endmodule
Pmodモジュールの電気図
メモ:PLL
今回はPLLは使っていないので,単なるメモ.
クロックソースは3つある.PLLとオシレータ(OSC)とLaC(Logic as Clock).
PLLとオシレータは内部クロック生成用に使える.PLLはクロックソースとしてオシレータまたはGPIO2を通して信号をを受け取る.
内蔵オシレータの周波数は50MHzである.
OSCとPLLとLaCsはBRAMとFPGAコアを駆動できる.
GPIOへ外部からLaC信号を入力できる.
LaCは,コアからクロックツリー入力へ論理信号を通すために使える.
同時に使えるクロックドメインは2つまでである.
その他
このボードの入力電源はUSBの5Vであり,ボード上の2種のレギュレータで,
- I/O用の3.3V
- RP2040とForgeFPGA用の1.1V
を作っている.
1.1V系はコンデンサの定数設定に問題があるらしく,RP2040の電源が入ると揺れが大きい.これに伴い,FPGAのコンフィグレーションに問題が発生することがあるらしい.RP2040の動作周波数を125MHz程度まで落とすと比較的安定するらしい.
対処する場合は,C8とC7コンデンサをそれぞれ変更することで1.1V電源が安定する. - C8 100nF → 2.2uF
- C7 1uF → 100nF
コンデンサのサイズは1005,ハンドソルダー用のパッドではないので,交換はそれなりに難しい.







