Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions dfx_runtime.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
DFXRuntime Profile Report:
Total Application(DFX) Runtime : CPU : 0:2:18 WALL : 0:0:0 0.00 %
Comment on lines +1 to +2

Copilot AI Dec 4, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to be a Xilinx DFX (Dynamic Function eXchange) runtime report file, which is typically an auto-generated output file. Build artifacts and tool-generated reports should generally not be tracked in version control.

Consider adding dfx_runtime.txt or *.txt (if appropriate) to the .gitignore file to prevent committing build outputs.

Copilot uses AI. Check for mistakes.
188 changes: 126 additions & 62 deletions final-experiment-tetris.srcs/sources_1/new/block_renderer.sv
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ parameter DIGIT_SPACING = 4; // 数字间距
parameter SCORE_DISPLAY_X = 488; // 分数数字显示起始X坐标(居中对齐)
parameter SCORE_DISPLAY_Y = 130; // 分数数字显示起始Y坐标(在"得分"下方)

logic [3:0] cell_x;
logic [3:0] cell_x; // 当前像素对应的网格单元X坐标
assign cell_x = (curr_pix_x - GRID_START_X) / BLOCK_SIZE;
logic [4:0] cell_y;
logic [4:0] cell_y; // 当前像素对应的网格单元Y坐标
assign cell_y = curr_pix_y / BLOCK_SIZE;
logic [3:0] cell_color;

Expand Down Expand Up @@ -65,88 +65,133 @@ always_comb begin
// 默认值
pixel_color = 12'h6cf; // 背景色 #6cf 8(:D

// 游戏网格渲染
if (curr_pix_x >= GRID_START_X && curr_pix_x < GRID_START_X + GRID_WIDTH && curr_pix_y >= 0 && curr_pix_y < GRID_HEIGHT) begin // 在游戏网格内
cell_color = game_grid_array[cell_y][cell_x]; // 取出对应单元格
// 检查当前行是否正在消除
is_clearing_row = clearing_rows[cell_y];

// 基础颜色(未加特效)
case (cell_color)
4'h0: base_color = 12'hFFF;
4'h1: base_color = 12'hF00;
4'h2: base_color = 12'h0F0;
4'h3: base_color = 12'h00F;
4'h4: base_color = 12'hFF0;
4'h5: base_color = 12'h0FF;
4'h6: base_color = 12'hF0F;
4'h7: base_color = 12'h888;
default: base_color = 12'hFFF;
4'h0: base_color = 12'hFFF; // 空白为白色 #FFF
4'h1: base_color = 12'hF00; // 红色 #F00
4'h2: base_color = 12'h0F0; // 绿色 #0F0
4'h3: base_color = 12'h00F; // 蓝色 #00F
4'h4: base_color = 12'hFF0; // 黄色 #FF0
4'h5: base_color = 12'h0FF; // 青色 #0FF
4'h6: base_color = 12'hF0F; // 品红 #F0F
4'h7: base_color = 12'h888; // 灰色 #888
default: base_color = 12'hFFF; // 默认白色 #FFF
endcase

// 显示方块颜色(不管是否在消行)
pixel_color = base_color;

if (is_clearing_row && cell_color != 4'h0) begin
// 动画阶段量化,约1秒
phase = clear_animation_counter[25:21]; // 0..31
crack_t = (phase >> 2) + 5'd1; // 裂纹厚度随时间加深
disp = phase[4:1]; // 碎片位移像素 0~15

// 碎片飞出
dir = {cell_x[0]^cell_y[1], cell_x[1]^cell_y[0]};
randbit = cell_x[2]^cell_y[2]^cell_x[0];

sx = block_x_offset;
sy = block_y_offset;
sx_s = sx;
sy_s = sy;

case (dir)
2'b00: sx_s = (sx > disp) ? (sx - disp) : 6'd63; // 右
2'b01: sx_s = sx + disp; // 左
2'b10: sy_s = sy + disp; // 上
default: sy_s = (sy > disp) ? (sy - disp) : 6'd63; // 下
endcase

in_bounds = (sx_s < BLOCK_SIZE) && (sy_s < BLOCK_SIZE);

// 碎片形状与稀疏门控(随时间更稀疏)
pattern = (sx_s[2] ^ sy_s[1] ^ randbit) | (sx_s[1] & ~sy_s[2]);
dist_bucket = (dir[0]) ? sx_s[4:3] : sy_s[4:3];
gate = (dist_bucket >= phase[4:3]);

if (in_bounds && pattern && gate) begin
pixel_color = base_color;
end else begin
pixel_color = 12'hFFF; // 飞出后留白
end

// 裂纹加深:十字 + 对角线,厚度随时间增长

// 在消除行上添加裂纹效果(非碎片部分显示背景色)
if (clear_line && is_clearing_row && cell_color != 4'h0) begin
// counter / 781250 (25_000_000 / 32 = 781250)
phase = (clear_animation_counter / 781250) & 5'h1F; // 确保在0-31范围
disp = (phase * 24) >> 5; // 碎片最大位移1个方块24像素,随phase线性增长
crack_t = (phase >> 2) + 5'd1; // 裂纹厚度随时间加深
mid5 = (BLOCK_SIZE/2);
d1 = (block_x_offset >= block_y_offset) ? (block_x_offset - block_y_offset) : (block_y_offset - block_x_offset);
d1 = (block_x_offset >= block_y_offset) ? (block_x_offset - block_y_offset) : (block_y_offset - block_x_offset);
sum_xy = block_x_offset + block_y_offset;
d2 = (sum_xy >= (BLOCK_SIZE-1)) ? (sum_xy - (BLOCK_SIZE-1)) : ((BLOCK_SIZE-1) - sum_xy);
ax = (block_x_offset >= mid5) ? (block_x_offset - mid5) : (mid5 - block_x_offset);
ay = (block_y_offset >= mid5) ? (block_y_offset - mid5) : (mid5 - block_y_offset);

d2 = (sum_xy >= (BLOCK_SIZE-1)) ? (sum_xy - (BLOCK_SIZE-1)) : ((BLOCK_SIZE-1) - sum_xy);
ax = (block_x_offset >= mid5) ? (block_x_offset - mid5) : (mid5 - block_x_offset);
ay = (block_y_offset >= mid5) ? (block_y_offset - mid5) : (mid5 - block_y_offset);
if ((d1 <= crack_t) || (d2 <= crack_t) || (ax <= (crack_t>>1)) || (ay <= (crack_t>>1))) begin
pixel_color = 12'hFFF; // 裂纹为亮白
pixel_color = 12'hFFF; // 裂纹为白
end
end
end

// 碎片飞溅动画 - 在所有区域检查(包括游戏区外)
if (clear_line) begin
logic [4:0] anim_phase; // 动画阶段 0-31
logic [5:0] anim_disp; // 碎片位移
logic fragment_drawn; // 是否已绘制碎片
Comment on lines +111 to +113

Copilot AI Dec 4, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These local variable declarations within the always_comb block, combined with the nested loops below, create highly complex combinational logic. This can lead to:

  1. Long synthesis times
  2. Potential timing closure issues
  3. Difficulty in static timing analysis

Consider moving these to module-level declarations (like the existing phase, crack_t, disp variables at lines 39-48) or refactoring the animation logic to use sequential (clocked) logic instead of pure combinational logic.

Copilot uses AI. Check for mistakes.

// 动画阶段量化(32步)
// 计数器范围: 0 ~ 25_000_000
// 归一化到 0~31: (counter * 32) / 25_000_000
// 简化: counter / 781250 (25_000_000 / 32 = 781250)
anim_phase = (clear_animation_counter / 781250) & 5'h1F; // 确保在0-31范围
anim_disp = (anim_phase * 48) >> 5; // 碎片最大位移2个方块48像素,随anim_phase线性增长

fragment_drawn = 0;

// 遍历所有可能的源方块位置(游戏网格内)
for (int src_y = 0; src_y < 20; src_y++) begin
for (int src_x = 0; src_x < 10; src_x++) begin
if (!fragment_drawn && clearing_rows[src_y]) begin
logic [3:0] src_color;
logic [1:0] src_dir;
logic src_randbit;
logic signed [10:0] pixel_offset_x, pixel_offset_y;
logic [5:0] local_x, local_y;
logic signed [10:0] src_screen_x, src_screen_y;
logic signed [10:0] fragment_screen_x, fragment_screen_y;
Comment on lines +124 to +134

Copilot AI Dec 4, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declaring multiple logic variables inside deeply nested loops within an always_comb block creates extremely complex combinational logic. Since these declarations are inside loops that run 200 iterations per pixel, the synthesis tool must handle thousands of temporary signals.

Consider:

  1. Moving these declarations to module-level scope
  2. Restructuring to avoid nested loops in combinational logic
  3. Using a clocked process to compute fragment positions once per frame rather than per pixel
Suggested change
// 遍历所有可能的源方块位置(游戏网格内)
for (int src_y = 0; src_y < 20; src_y++) begin
for (int src_x = 0; src_x < 10; src_x++) begin
if (!fragment_drawn && clearing_rows[src_y]) begin
logic [3:0] src_color;
logic [1:0] src_dir;
logic src_randbit;
logic signed [10:0] pixel_offset_x, pixel_offset_y;
logic [5:0] local_x, local_y;
logic signed [10:0] src_screen_x, src_screen_y;
logic signed [10:0] fragment_screen_x, fragment_screen_y;
// Declare temporary variables once at the top of the always_comb block to avoid excessive combinational logic
logic [3:0] src_color;
logic [1:0] src_dir;
logic src_randbit;
logic signed [10:0] pixel_offset_x, pixel_offset_y;
logic [5:0] local_x, local_y;
logic signed [10:0] src_screen_x, src_screen_y;
logic signed [10:0] fragment_screen_x, fragment_screen_y;
// 遍历所有可能的源方块位置(游戏网格内)
for (int src_y = 0; src_y < 20; src_y++) begin
for (int src_x = 0; src_x < 10; src_x++) begin
if (!fragment_drawn && clearing_rows[src_y]) begin

Copilot uses AI. Check for mistakes.

src_color = game_grid_array[src_y][src_x];

if (src_color != 4'h0) begin // 非空方块
// 计算源方块的飞散方向
src_dir = {src_x[0]^src_y[1], src_x[1]^src_y[0]};
src_randbit = src_x[2]^src_y[2]^src_x[0];

// 计算源方块在屏幕上的左上角位置
src_screen_x = GRID_START_X + src_x * BLOCK_SIZE;
src_screen_y = src_y * BLOCK_SIZE;

// 计算飞散后的位置
fragment_screen_x = src_screen_x;
fragment_screen_y = src_screen_y;

case (src_dir)
2'b00: fragment_screen_x = src_screen_x + anim_disp; // 向右飞
2'b01: fragment_screen_x = src_screen_x - anim_disp; // 向左飞
2'b10: fragment_screen_y = src_screen_y - anim_disp; // 向上飞
default: fragment_screen_y = src_screen_y + anim_disp; // 向下飞
endcase

// 计算当前像素相对于飞散后位置的偏移
pixel_offset_x = $signed(curr_pix_x) - fragment_screen_x;
pixel_offset_y = $signed(curr_pix_y) - fragment_screen_y;

// 检查是否在碎片范围内
if (pixel_offset_x >= 0 && pixel_offset_x < BLOCK_SIZE && pixel_offset_y >= 0 && pixel_offset_y < BLOCK_SIZE) begin
local_x = pixel_offset_x[5:0];
local_y = pixel_offset_y[5:0];

// 碎片形状与稀疏门控(随时间更稀疏)
pattern = (local_x[2] ^ local_y[1] ^ src_randbit) | (local_x[1] & ~local_y[2]);
dist_bucket = (src_dir[0]) ? local_x[4:3] : local_y[4:3];
gate = (dist_bucket >= anim_phase[4:3]);

if (pattern && gate) begin
pixel_color = ~pixel_color; // 碎片颜色取反,更加醒目
fragment_drawn = 1;
end
end
end
end
end
end
Comment on lines +125 to +180

Copilot AI Dec 4, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This nested loop structure iterates up to 200 times (20 rows × 10 columns) for every pixel being rendered, which is extremely inefficient. For a 640×480 display, this means up to 61.44 million loop iterations per frame. This will likely cause severe synthesis issues and timing violations.

Consider refactoring to:

  1. Pre-calculate fragment positions in a sequential clocked process
  2. Store active fragments in a small array (one entry per clearing row)
  3. Check only the relevant fragments for the current pixel instead of iterating through all grid positions

Copilot uses AI. Check for mistakes.
end

// "得分"文本渲染
if (curr_pix_x >= 508 && curr_pix_x < 572 && curr_pix_y >= 80 && curr_pix_y < 112) begin
if (score_font_model[curr_pix_y - 80][curr_pix_x - 508]) begin
pixel_color = 12'h000; // 黑色 #000
end
end

// "难度"文本渲染
if (curr_pix_x >= 508 && curr_pix_x < 572 && curr_pix_y >= 200 && curr_pix_y < 232) begin
if (difficult_level_font_model[curr_pix_y - 200][curr_pix_x - 508]) begin
pixel_color = 12'h000; // 黑色 #000
end
end

// 难度等级渲染(0-困难, 1-简单)
// 字模尺寸: 64x32 像素
if (curr_pix_y >= 250 && curr_pix_y < 282) begin
Expand All @@ -173,7 +218,6 @@ always_comb begin
end
end
end

// 分数数字渲染 (5位数字)
if (curr_pix_y >= SCORE_DISPLAY_Y && curr_pix_y < SCORE_DISPLAY_Y + DIGIT_HEIGHT) begin
logic [9:0] digit_offset_y;
Expand Down Expand Up @@ -236,9 +280,9 @@ always_comb begin
endcase
end
end

// 游戏结束文本渲染
// 游戏结束文本及遮罩渲染
if (game_over) begin
// 遮罩
// 分离颜色通道
logic [3:0] R_final, G_final, B_final;

Expand All @@ -250,20 +294,40 @@ always_comb begin
G_final = G_final >> 1; // 除以 2,亮度减半
B_final = B_final >> 1; // 除以 2,亮度减半

// 合并回 12 位颜色值
// 合并回颜色
pixel_color = {R_final, G_final, B_final};

// 确保数组访问不越界
if (curr_pix_y >= 210 && curr_pix_y < 270 && curr_pix_x >= 192 && curr_pix_x < 448) begin
// 有字的地方画黑色
// 有字的地方画白色
if (gameover_font_model[curr_pix_y - 210][curr_pix_x - 192]) begin
pixel_color = 12'hFFF; // 白色 #FFF
// 没字的地方追加半透明遮罩效果
end
end
end
end






















Comment on lines +318 to +330

Copilot AI Dec 4, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Consider removing these excessive blank lines (20+ empty lines). While whitespace can improve readability, this many consecutive blank lines doesn't add value and increases file size unnecessarily.

Suggested change

Copilot uses AI. Check for mistakes.
// Font Bitmap Data
// Text: 游戏结束
// Font: 花兮楷书 (非商业使用), Size: 72, DPI: 86
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ module lfsr_galois_3bit(

always @(posedge clk or posedge rst) begin
if (rst) begin
random_out <= 3'b111; // Non-zero seed
random_out <= 3'b111; // 非零种子
end else begin
// Galois LFSR feedback taps for 3-bit LFSR: x^3 + x + 1
// 3位伽罗瓦(Galois)型线性反馈移位寄存器(LFSR: x^3 + x + 1
random_out[2] <= random_out[1];
random_out[1] <= random_out[0] ^ random_out[2];
random_out[0] <= random_out[2];
Expand Down
9 changes: 4 additions & 5 deletions final-experiment-tetris.srcs/sources_1/new/tetris_logic.sv
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ function logic [4:0] get_column_distance(input logic signed [4:0] col_idx, input
return hit_y - start_y - 1;
endfunction

// 计算四列的最小下落距离
always_comb begin
logic [15:0] shape;
logic [4:0] d0, d1, d2, d3;
Expand Down Expand Up @@ -293,9 +294,7 @@ always_ff @(posedge gm_clk or posedge gm_rst) begin
automatic logic [1:0] next_rot = rotate;
automatic logic moved_by_player = 1'b0;



// --- 第一步:优先处理玩家输入 ---
// 第一步:优先处理玩家输入
if (left_edge) begin
next_x = active_x - 1;
moved_by_player = 1'b1;
Expand All @@ -307,13 +306,13 @@ always_ff @(posedge gm_clk or posedge gm_rst) begin
moved_by_player = 1'b1;
end

// --- 第二步:测试玩家移动是否有效 ---
// 第二步:测试玩家移动是否有效
if (moved_by_player && !check_collision(active_block, next_rot, next_x, active_y)) begin
active_x <= next_x;
rotate <= next_rot;
end

// --- 第三步:处理下落移动 ---
// 第三步:处理下落移动
if (grav_ce || down_edge) begin // 处理重力或硬降
if (down_edge) begin
next_y = active_y + drop_distance; // 硬降落
Expand Down