FPGAを使ってShader芸で遊びます.
使用したFPGAボードは,Tang Mega 138K ProDock(Sipeed) です.画面は,ボードのHDMIコネクタからDVIで出力します.
まずは,フラグメント・シェーダで球(っぽいもの)を描画します.

●topモジュール
・PLL(Gowin IP)
DVI出力するために,ピクセルクロックとシリアルクロック(ピクセルクロック×5)を生成します.
・タイミング ジェネレータ -video_timing-
1,650×720でDVI出力するための同期信号を生成します.
・シェーダー -shader-
中央に,Sphere(っぽいもの)を書くシェーダーです.簡単のため今回は,R=G=B
・DVI_TX_Top(Gowin IP)
DVI出力用のモジュールです.
Verilog
module top (
input wire clk,
input wire rst_n,
// DVI TMDS output (HDMI connector)
output wire tmds_clk_p,
output wire tmds_clk_n,
output wire [2:0] tmds_data_p,
output wire [2:0] tmds_data_n,
output logic[5:0] state_led
);
assign state_led[0] = rst; // board LED
assign state_led[1] = rst_n; // board LED
assign state_led[2] = de; // board LED
// Clock Generation
wire clk_pixel;
wire clk_tmds;
wire pll_lock;
Gowin_PLL pll_inst(
.clkin(clk), //input clkin
.clkout0(clk_tmds), //output clkout0
.clkout1(clk_pixel), //output clkout1
.lock(pll_lock), //output lock
.reset(~rst_n) //input reset
);
wire rst = ~rst_n | ~pll_lock;
wire [10:0] hcount;
wire [10:0] vcount;
wire hsync, vsync, de, field_start;
//Timing Generator
video_timing u_timing (
.clk (clk_pixel),
.rst (rst),
.hcount (hcount),
.vcount (vcount),
.hsync (hsync),
.vsync (vsync),
.de (de),
.field_start(field_start)
);
// Shader Pixel Generator
wire [7:0] r_out, g_out, b_out;
wire shader_de;
shader u_shader (
.clk (clk_pixel),
.x (hcount),
.y (vcount),
.r (r_out),
.g (g_out),
.b (b_out)
);
// DVI output
DVI_TX_Top your_instance_name(
.I_rst_n(rst_n), //input I_rst_n
.I_serial_clk(clk_tmds), //input I_serial_clk
.I_rgb_clk(clk_pixel), //input I_rgb_clk
.I_rgb_vs(vsync), //input I_rgb_vs
.I_rgb_hs(hsync), //input I_rgb_hs
.I_rgb_de(de), //input I_rgb_de
.I_rgb_r(r_out), //input [7:0] I_rgb_r
.I_rgb_g(g_out), //input [7:0] I_rgb_g
.I_rgb_b(b_out), //input [7:0] I_rgb_b
.O_tmds_clk_p(tmds_clk_p), //output O_tmds_clk_p
.O_tmds_clk_n(tmds_clk_n), //output O_tmds_clk_n
.O_tmds_data_p(tmds_data_p), //output [2:0] O_tmds_data_p
.O_tmds_data_n(tmds_data_n) //output [2:0] O_tmds_data_n
);
endmoduleExpand
●シェーダー -Sphere-
入力するのは,X,Y座標のみです.
Verilog
module shader (
input wire clk,
input wire [11:0] x,
input wire [11:0] y,
output reg [7:0] r,
output reg [7:0] g,
output reg [7:0] b
);
localparam [11:0] CAMERA_x = 1650 / 2;
localparam [11:0] CAMERA_y = 720 / 2;
// =====================
// Sphire
// =====================
logic[7:0] pixel_value;
function [11:0] length;
input [15:0] val1;
input [15:0] val2;
length = ( (((val1>val2)?val1:val2) << 16'd4) + (((val1<val2)?val1:val2) << 16'd3)) >> 16'd4;
endfunction
logic[11:0] c_x, c_y; // current pixel position(x,y) from center
logic[11:0] sphere_length;
assign c_x = (x < CAMERA_x)?CAMERA_x - x: x - CAMERA_x;
assign c_y = (y < CAMERA_y)?CAMERA_y - y: y - CAMERA_y;
assign sphere_length = length(c_x,c_y);
always_ff @(posedge clk)begin
if (sphere_length[11:0] <100)begin
pixel_value <= 8'hff;
end else begin
pixel_value <= (8'hff < (sphere_length[11:0]*2-90))?8'b0:(8'hff - (sphere_length[7:0]*2-90));
end
r <= pixel_value;
g <= pixel_value;
b <= pixel_value;
end
endmodule