FPGAを使ってShader芸で遊びます.

 使用したFPGAボードは,Tang Mega 138K ProDock(Sipeed) です.画面は,ボードのHDMIコネクタからDVIで出力します.

 まずは,フラグメント・シェーダで球(っぽいもの)を描画します.

ルート演算を嫌って,近似値で妥協したので8角形のスフィアに…(モニタ画面をカメラで撮影)

●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
);

endmodule
Expand

●シェーダー -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

コメントを残す