ハードウェアとして,Tang Primer 138K Pro Dockを使います.合成系はGowin IDEです.
目標はコレ
●生成されたRTLコード(シェーダー部分のみ)
▲初期実装
// ============================================================================
// shader.sv
// 座標 (x, y) と時間 (t) から 24bit ピクセル色 (R,G,B 各8bit) を生成する回路
//
// 【設計方針】
// ・三角関数 (sin/cos) は使用しない
// ・浮動小数点は使用しない
// ・整数の加算・定数乗算・右シフト・ビット反転のみで実装
//
// 【描画内容】
// 空 : 上ほど濃い青、地平線に近づくほど明るい青のグラデーション
// 奥の山 : 霞んだ青灰色の山並み
// 手前の山: 濃い緑の山並み (奥の山を隠す)
// 雪 : 高い稜線の頂部に白い雪
//
// 山の稜線は「三角波の重ね合わせ」で生成する。
// 三角波は位相の MSB で下位ビットを反転するだけで作れるため、
// sin/cos も除算も使わずにギザギザした自然な輪郭が得られる。
//
// 時間変化:
// ・山並みがゆっくり横に流れる (手前と奥で速度を変えて視差を付ける)
// ・フレームカウンタから作った三角波で明るさ(色)が周期的に変化する
// ============================================================================
module shader #(
parameter int H_ACTIVE = 1280, // 水平アクティブ画素数
parameter int V_ACTIVE = 720 // 垂直アクティブ画素数
) (
input logic clk, // ピクセルクロック
input logic rst, // 同期リセット (アクティブHigh)
input logic de, // データイネーブル (アクティブ期間で1)
input logic [11:0] hcount, // X座標 (0 .. H_ACTIVE-1)
input logic [11:0] vcount, // Y座標 (0 .. V_ACTIVE-1)
output logic [7:0] r, // 赤 (8bit)
output logic [7:0] g, // 緑 (8bit)
output logic [7:0] b // 青 (8bit)
);
// ------------------------------------------------------------------------
// 1. 時間カウンタ (フレーム単位)
// 各フレームの先頭 (hcount=0, vcount=0, de=1) で +1 する
// ------------------------------------------------------------------------
logic [31:0] frame_cnt;
always_ff @(posedge clk) begin
if (rst)
frame_cnt <= 32'd0;
else if (de && (hcount == 12'd0) && (vcount == 12'd0))
frame_cnt <= frame_cnt + 32'd1;
end
// 時間値 (8bit) : フレームカウンタの下位8bit
logic [7:0] t_val;
assign t_val = frame_cnt[7:0];
// 時間三角波 (0 .. 254) : 明るさ(色)を周期的に変化させる
// のこぎり波の MSB が 1 のとき下位ビットを反転すると三角波になる
logic [7:0] saw_t;
logic [7:0] wave_t;
assign saw_t = {t_val[6:0], 1'b0}; // t_val << 1 (0..254)
assign wave_t = t_val[7] ? ~saw_t : saw_t; // 三角波 0..254
// ------------------------------------------------------------------------
// 2. 画面下からの高さ y_up (0..255)
// vcount=0 が画面上端 → y_up=255、vcount=V_ACTIVE-1 が下端 → y_up=0
// 除算を避けるため「定数乗算 + 右シフト」でスケーリングする
// y_up = 255 - (vcount * 91 >> 8) (0..719 → 255..0)
// ------------------------------------------------------------------------
logic [15:0] y_prod;
logic [7:0] y_up;
assign y_prod = {4'b0000, vcount} * 16'd91; // 最大 719*91 = 65429
assign y_up = 8'd255 - y_prod[15:8]; // 0(下端) .. 255(上端)
// ------------------------------------------------------------------------
// 3. 山の稜線 (周期の異なる三角波の重ね合わせ)
// 三角波 : 位相の MSB が 1 なら下位ビットを反転 (0..255)
// 周期の違う波を足すことで、単調なギザギザではなく
// 大小の峰が混ざった自然な山並みになる
// 山の高さは「画面下からの高さ」で表す (大きいほど高い)
// ------------------------------------------------------------------------
logic [11:0] x_far, x_near; // 横スクロールを加えた X 座標
// 手前の山は奥の山より速く流れる (視差効果)
assign x_far = hcount + {4'b0000, t_val}; // ゆっくり
assign x_near = hcount + {3'b000, t_val, 1'b0}; // 2倍速
// 三角波を生成する関数 (位相の MSB で折り返す)
// 周期 512 の大きな波 (峰と谷がゆったり)
function automatic logic [7:0] tri_slow(input logic [11:0] phase);
tri_slow = phase[8] ? ~phase[7:0] : phase[7:0];
endfunction
// 周期 128 の細かい波 (岩肌のギザギザ)
function automatic logic [7:0] tri_fast(input logic [11:0] phase);
tri_fast = phase[6] ? ~{1'b0, phase[6:0]} : {1'b0, phase[6:0]};
endfunction
// 各山の波をいったん信号に受ける
logic [7:0] far_slow, far_fast, near_slow, near_fast;
assign far_slow = tri_slow(x_far);
assign far_fast = tri_fast(x_far);
assign near_slow = tri_slow(x_near);
assign near_fast = tri_fast(x_near);
// 稜線の高さ (0..255) : 大きな波 + 細かい波 を合成
// 奥の山 : 高くそびえる (70..164)
// 手前の山: 低く手前に広がる (30..125)
logic [7:0] h_far, h_near;
assign h_far = 8'd70 + {1'b0, far_slow[7:2]} + {2'b00, far_fast[6:2]};
assign h_near = 8'd30 + {1'b0, near_slow[7:2]} + {2'b00, near_fast[6:2]};
// ------------------------------------------------------------------------
// 4. 空のグラデーション
// 上ほど濃い青、地平線に近づくほど明るい水色
// ------------------------------------------------------------------------
logic [7:0] sky_r, sky_g, sky_b;
assign sky_r = 8'd40 + {2'b00, vcount[9:4]}; // 40..103
assign sky_g = 8'd90 + {1'b0, vcount[9:3]}; // 90..217
assign sky_b = 8'd200 + {3'b000, vcount[9:5]}; // 200..231
// ------------------------------------------------------------------------
// 5. 山の色 (奥は霞んだ青灰色、手前は濃い緑)
// 時間三角波で明るさを変化させる
// ------------------------------------------------------------------------
logic [7:0] far_r, far_g, far_b;
logic [7:0] near_r, near_g, near_b;
// 奥の山 : 青灰色 + 時間による明暗
assign far_r = 8'd70 + {1'b0, wave_t[7:2]}; // 70..133
assign far_g = 8'd85 + {1'b0, wave_t[7:2]}; // 85..148
assign far_b = 8'd110 + {1'b0, wave_t[7:2]}; // 110..173
// 手前の山 : 濃い緑 + 時間による明暗
assign near_r = 8'd20 + {2'b00, wave_t[7:3]}; // 20..51
assign near_g = 8'd70 + {1'b0, wave_t[7:2]}; // 70..133
assign near_b = 8'd30 + {2'b00, wave_t[7:3]}; // 30..61
// ------------------------------------------------------------------------
// 6. 雪 : 奥の山の高い稜線 (頂上付近) を白くする
// ------------------------------------------------------------------------
logic snow_far;
assign snow_far = (h_far > 8'd140) && (y_up > (h_far - 8'd18));
// ------------------------------------------------------------------------
// 7. 太陽 : 円の内部判定 (dx^2 + dy^2 < r^2) を整数乗算だけで行う
// ------------------------------------------------------------------------
logic [11:0] sun_dx, sun_dy;
logic [23:0] sun_dist;
logic sun_on;
assign sun_dx = (hcount > 12'd950) ? (hcount - 12'd950) : (12'd950 - hcount);
assign sun_dy = (y_up > 8'd200) ? ({4'b0000, y_up} - 12'd200)
: (12'd200 - {4'b0000, y_up});
// 乗算結果が切り捨てられないよう、オペランドを 24bit に拡張する
assign sun_dist = ({12'b0, sun_dx} * {12'b0, sun_dx})
+ ({12'b0, sun_dy} * {12'b0, sun_dy});
assign sun_on = (sun_dist < 24'd1600); // 半径 40 の円
// ------------------------------------------------------------------------
// 8. ピクセル合成 (手前の山が奥の山を隠す)
// ------------------------------------------------------------------------
always_comb begin
if (!de) begin
// ブランキング期間は黒
r = 8'h00;
g = 8'h00;
b = 8'h00;
end else if (y_up < h_near) begin
// 手前の山より下 → 手前の山 (緑)
r = near_r; g = near_g; b = near_b;
end else if (y_up < h_far) begin
// 奥の山より下 → 奥の山 (青灰色) / 頂上は雪
if (snow_far) begin
r = 8'hE8; g = 8'hEE; b = 8'hFF;
end else begin
r = far_r; g = far_g; b = far_b;
end
end else begin
// それより上 → 空 (太陽があれば太陽)
if (sun_on) begin
r = 8'hFF; g = 8'hF4; b = 8'hC0;
end else begin
r = sky_r; g = sky_g; b = sky_b;
end
end
end
endmodule広げる