HDL書けないのでChatGPT使ってパターン表示回路を作る
XilinxのFPGAでパターン表示回路を作る
はじめに
FPGAを勉強するにあたり「FPGAプログラミング大全 第2版」がとてもオススメらしく、
購入したのでやってみる
HDL経験:Verilog入門書を一日読んだだけ 基本の文法のみ
本投稿では本書2-3,「PC用ディスプレイにパターンを表示する」を実施したメモを書きます
やりたいこと
パターン表示回路をFPGAで合成し、HDMIラインから画像を表示させる
環境
FPGA : Xilinx Zybo Z7-20
OS : WSL2 Ubuntu20.04
開発環境 : Vivado ML edition 2022.1 Linux版
仕様
-
FPGAボードのHDMI端子から出力
-
白・黒を含めた8色を表示
-
VGA(640*480)
-
動作タイミング
Pix CLK | H CLK | V VLK |
---|---|---|
25.175MHz | 31.469kHz | 59.94kHz |
Item | Dot |
---|---|
H Period | 800 |
H Front Poach | 16 |
H Width | 96 |
H Back Poach | 48 |
V Period | 525 |
V Front Poach | 10 |
V Width | 2 |
V Back Poach | 33 |
パターン表示回路
下記ファイルで構成される
コードについては著作権が怪しいので載せません 下記URLに置いてあります
- vga_param.vh
- syncgen.v
- pattern.v
- pckgen.v
- pattern_hdmi.v
上記のようなプロジェクト構成にしたらそのままGenerate Bitstream
実行結果
FPGAによって出力されたHDMIの画面をキャプチャするのは面倒なのでRun Simulation -> Behavioral Simulationを実施
Simulation時に表示画像をfwriteで書き込むようなテストベンチをVerilogで書いてSimulation Sourcesに追加しておく(本書3章)
一部抜粋
initial begin
RST = 0;
fd = $fopen("imagedata.raw", "wb");
#(STEP*600) RST = 1;
#(STEP*20) RST = 0;
#(STEP*CLKNUM);
$fclose(fd);
$stop;
end
always @(posedge PCK) begin
if ( VGA_DE ) begin
$fwrite(fd, "%c", VGA_R);
$fwrite(fd, "%c", VGA_G);
$fwrite(fd, "%c", VGA_B);
end
end
SIMULATION -> Run ALLを実行して波形を表示
Data EnableがHになってからR/G/Bの値が変化する(Analog表示のためやや分かりづらい笑)
画像だと見切れているが、1V = 1/59.94 s = 16ms程度になっている
書き込まれたRGBデータはrawファイルとしてプロジェクト直下のsimに生成される
意図通りの表示がされる事を確認!
左上から
(R,G,B)= White(1,1,1) -> Yellow(1,1,0) -> Purple(1,0,1) ->...Black(0,0,0)となっている
パターン表示回路の拡張/階調を付けてグラデーション表示する
本記事で一番やりたかった部分。上記までのコードを参考に、下記要件を満たす画像が出力されるようにpattern.vを変更する。
要件
- 階調を16段階で変化させたパターンを表示する回路
- 各色4bitの階調表現とする
- 左右方向は10分割して64dotのブロックを形成し、縦方向は4分割して上から白、赤、緑、青とする
- 各ブロックの横方向は4dot事に変化させる
表示させたい画像
始めにゴールを提示する。最終的にこれを表示させるようなpattern.vを作成する
という訳で、ChatGPTと相談して作成(HDL書けない)
module pattern(
input CLK,
input RST,
output reg [7:0] VGA_R, VGA_G, VGA_B,
output VGA_HS, VGA_VS,
output reg VGA_DE,
output PCK
);
`include "vga_param.vh"
localparam HSIZE = 10'd80;
localparam VSIZE = 10'd120;
localparam HBLK_SIZE = 10'd64;
localparam VBLK_SIZE = 10'd480 / 4;
wire [9:0] HCNT, VCNT;
syncgen syncgen(
.CLK (CLK),
.RST (RST),
.PCK (PCK),
.VGA_HS (VGA_HS),
.VGA_VS (VGA_VS),
.HCNT (HCNT),
.VCNT (VCNT)
);
wire [9:0] HBLANK = HFRONT + HWIDTH + HBACK;
wire [9:0] VBLANK = VFRONT + VWIDTH + VBACK;
wire disp_enable = (VBLANK <= VCNT)
&& (HBLANK-10'd1 <= HCNT) && (HCNT < HPERIOD-10'd1);
wire [3:0] gradient_index = ((HCNT - HBLANK) % HBLK_SIZE) / 10'd4;
wire [1:0] block_index = (HCNT - HBLANK) / HBLK_SIZE;
reg [2:0] color;
always @( posedge PCK ) begin
case (VCNT / VBLK_SIZE)
2'd0: color <= 3'b111; // 白
2'd1: color <= 3'b100; // 赤
2'd2: color <= 3'b010; // 緑
2'd3: color <= 3'b001; // 青
default: color <= 3'b000;
endcase
end
reg [3:0] color_level;
always @( posedge PCK ) begin
if (block_index[0] == 1'b0) begin
color_level <= gradient_index;
end else begin
color_level <= 4'd15 - gradient_index;
end
end
wire [7:0] VGA_R_temp = color[2] ? color_level * 8'h11 : 8'h00;
wire [7:0] VGA_G_temp = color[1] ? color_level * 8'h11 : 8'h00;
wire [7:0] VGA_B_temp = color[0] ? color_level * 8'h11 : 8'h00;
always @( posedge PCK ) begin
if ( RST )
{VGA_R, VGA_G, VGA_B} <= 24'h0;
else if ( disp_enable )
{VGA_R, VGA_G, VGA_B} <= {VGA_R_temp, VGA_G_temp, VGA_B_temp};
else
{VGA_R, VGA_G, VGA_B} <= 24'h0;
end
always @( posedge PCK ) begin
if ( RST )
VGA_DE <= 1'b0;
else
VGA_DE <= disp_enable;
end
endmodule
pattern.vを変更してGenerate Bitstream -> Behavioral Simulationでrawdataを書き込み、先ほど同様に内容を確認
波形確認
VGA_DE(DataEnable) H後のRGBデータの波形が変化している
表示画像
表示したい画像と微妙に違っている。(ほとんどChatGPTに任せきりだったので仕方なし)
問題点は下記
- グラデーションの方向が64dotで反転してしまっている
- 画面下10%程度が真っ暗。恐らくDisplayのV Blank期間を考慮できていない
再びChatGPTと相談
修正したコードを記述
module pattern(
input CLK,
input RST,
output reg [7:0] VGA_R, VGA_G, VGA_B,
output VGA_HS, VGA_VS,
output reg VGA_DE,
output PCK
);
`include "vga_param.vh"
localparam HSIZE = 10'd80;
localparam VSIZE = 10'd120;
localparam HBLK_SIZE = 10'd64;
localparam VBLK_SIZE = (10'd480 - 10'd45 )/ 4;
wire [9:0] HCNT, VCNT;
syncgen syncgen(
.CLK (CLK),
.RST (RST),
.PCK (PCK),
.VGA_HS (VGA_HS),
.VGA_VS (VGA_VS),
.HCNT (HCNT),
.VCNT (VCNT)
);
wire [9:0] HBLANK = HFRONT + HWIDTH + HBACK;
wire [9:0] VBLANK = VFRONT + VWIDTH + VBACK;
wire disp_enable = (VBLANK <= VCNT)
&& (HBLANK-10'd1 <= HCNT) && (HCNT < HPERIOD-10'd1);
wire [3:0] gradient_index = ((HCNT - HBLANK) % HBLK_SIZE) / 10'd4;
wire [1:0] block_index = (HCNT - HBLANK) / HBLK_SIZE;
reg [2:0] color;
always @( posedge PCK ) begin
case ((VCNT - VBLANK + 10'd1) / VSIZE)
2'd0: color <= 3'b111; // 白
2'd1: color <= 3'b100; // 赤
2'd2: color <= 3'b010; // 緑
2'd3: color <= 3'b001; // 青
default: color <= 3'b000;
endcase
end
reg [3:0] color_level;
always @( posedge PCK ) begin
color_level <= gradient_index;
end
wire [7:0] VGA_R_temp = color[2] ? color_level * 8'h11 : 8'h00;
wire [7:0] VGA_G_temp = color[1] ? color_level * 8'h11 : 8'h00;
wire [7:0] VGA_B_temp = color[0] ? color_level * 8'h11 : 8'h00;
always @( posedge PCK ) begin
if ( RST )
{VGA_R, VGA_G, VGA_B} <= 24'h0;
else if ( disp_enable )
{VGA_R, VGA_G, VGA_B} <= {VGA_R_temp, VGA_G_temp, VGA_B_temp};
else
{VGA_R, VGA_G, VGA_B} <= 24'h0;
end
always @( posedge PCK ) begin
if ( RST )
VGA_DE <= 1'b0;
else
VGA_DE <= disp_enable;
end
endmodule
表示画像
だいぶ完璧に近いが、一番左のグラデーションが(R,G,B)=(0,0,0)ではなく(255,255,255)になっていそう。恐らくグラデーションの演算がズレていると思われる
ここまで来ればグラデーションの開始位置のズレを修正するだけ!
+4でオフセットをかけて画面一番左の余剰演算結果が1増加するよう変更
wire [3:0] gradient_index = ((HCNT - HBLANK +10'd4) % HBLK_SIZE) / 10'd4;
完成!!!!!
おわりに
- HDL書けなくても要件定義をキチンとすればChatGPTとの組み合わせで何とかなる
- 最後に4ライン分のグラデーションがズレたのはパターン表示回路の最終段でフリップフロップをかませるためと予想。従って水平方向は1CLK前に値を用意する必要があった。
Discussion