ベースにしたプロジェクトはこちら

 LEDモジュールを縦に連結しており,縦64 x 横16ドットのマトリクスLEDを表示領域として使える.

 当初は縦64ドットのテトリスにしようと思ったが,その場合,使用するLUTが25Kを超えてしまうので断念した.

 獲得点数は縦書きで,画面右下に表示している.次テトロミノは画面の右上に表示した.

●使ったもの

▲マトリクスLED

 秋月電子で販売されていたモジュール.“ドット抜け有りマトリクスLED”としてジャンク販売されていたものを入手.自作のベースボードを介して,FPGAボードに接続(Pmod)している.

▲FPGAボード

 Gowin FPGAを搭載したTang Primer 25K(Sipeed).こちらも秋月電子などで購入できる.

▲ゲームコントローラ🎮

 テトリスのプレイ用に,プレステ用として販売されていたコントローラを使う.FPGAボードにはPmod(SPI)を介して接続する.このためのPmodモジュールとして,Sipeed製のものを利用する.

●codexに生成させたRTLコード

 生成には,GPT-5.4を使った.

 ゲームコントローラ部分は,OSS(nestan)から流用して自前で実装した.

Verilog
module matrix_led_top (
    input  wire clk,
    input  wire USER_KEY,
    input  wire USER_KEY2,
    output reg  SIN1,
    output reg  SIN2,
    output reg  SIN3,
    output reg  LATCH,
    output reg  LED_CLK,
    output reg  STROBE_,
    output wire joystick_cs2,
    output wire joystick_mosi2,
    output wire joystick_clk2,
    output wire joystick_cs,
    output wire joystick_mosi,
    input wire joystick_miso,
    input wire joystick_miso2,
    output wire joystick_clk,
    output wire[7:0] led
);

//assign KEY_LEFT = USER_KEY2;
assign KEY_LEFT = ~joy_rx[0][7];
assign KEY_RIGHT = ~joy_rx[0][5];
assign KEY_DOWN = ~joy_rx[0][6];
assign BTN_A = ~joy_rx[1][4];
assign BTN_B = ~joy_rx[1][7];
assign reset_key = ~joy_rx[0][3];
//STA 0,3
//〇 1,5
//△ 1,4
//X 1,6
//□ 1,7
//→ 0,5
//← 0,7
//↓ 0,7 - 0,6
//sel 0,1
//R2 1,1
//R1 1,3
//L1 1,2
//l2 0,0
assign led[0] = ~joy_rx[0][5];
assign led[1] = ~joy_rx[0][7];

    wire KEY_LEFT;
    wire KEY_RIGHT;
    wire KEY_DOWN;
    wire BTN_A;
    wire BTN_B;

    wire reset_key;

reg sclk;                   // controller main clock at 250Khz
localparam  SCLK_DELAY = 200;
reg [$clog2(SCLK_DELAY)-1:0] sclk_cnt;         

// Generate sclk
always @(posedge clk) begin
    sclk_cnt <= sclk_cnt + 1;
    if (sclk_cnt == SCLK_DELAY-1) begin
        sclk = ~sclk;
        sclk_cnt <= 0;
    end
end

dualshock_controller controller (
    .I_CLK250K(sclk), .I_RSTn(1'b1),
    .O_psCLK(joystick_clk), .O_psSEL(joystick_cs), .O_psTXD(joystick_mosi),
    .I_psRXD(joystick_miso),
    .O_RXD_1(joy_rx[0]), .O_RXD_2(joy_rx[1]), .O_RXD_3(),
    .O_RXD_4(), .O_RXD_5(), .O_RXD_6(),
    // config=1, mode=1(analog), mode_en=1
    .I_CONF_SW(1'b0), .I_MODE_SW(1'b1), .I_MODE_EN(1'b0),
    .I_VIB_SW(2'b00), .I_VIB_DAT(8'hff)     // no vibration
);

  wire [7:0] joy_rx[0:1], joy_rx2[0:1];     // 6 RX bytes for all button/axis state

    localparam integer CLK_HZ            = 50_000_000;
    localparam integer DISPLAY_X         = 16;
    localparam integer DISPLAY_Y         = 64;
    localparam integer FIELD_W           = 8;
    localparam integer FIELD_H           = 32;
    localparam integer CELL_H            = 1;
    localparam integer PREVIEW_X         = 10;
    localparam integer PREVIEW_Y         = 4;
    localparam integer PREVIEW_W         = 4;
    localparam integer PREVIEW_H         = 4;
    localparam integer SCORE_X           = 10;
    localparam integer SCORE_Y           = 40;
    localparam integer SCORE_W           = 5;
    localparam integer SCORE_DIGITS      = 5;
    localparam integer DIGIT_H           = 3;
    localparam integer DIGIT_STRIDE      = 4;
    localparam integer CONTROL_TICK_CYC  = CLK_HZ / 50;
    localparam integer GRAVITY_TICK_CYC  = CLK_HZ / 2;
    localparam integer BASE_ON_TICKS     = 5_000;
    localparam integer SPAWN_X           = 2;
    localparam integer SPAWN_Y           = 0;

    localparam integer STATE_LOAD        = 0;
    localparam integer STATE_SHIFT_SETUP = 1;
    localparam integer STATE_SHIFT_HIGH  = 2;
    localparam integer STATE_SHIFT_LOW   = 3;
    localparam integer STATE_LATCH_ON    = 4;
    localparam integer STATE_LATCH_OFF   = 5;
    localparam integer STATE_HOLD        = 6;

    localparam integer ROW0_IS_LEFT      = 0;
    localparam integer COL0_IS_TOP       = 0;
    localparam integer SHIFT_MSB_FIRST   = 0;

    reg [2:0]  state;
    reg        plane_sel;
    reg [3:0]  scan_row;
    reg [5:0]  bit_index;
    reg [31:0] row_shift_word;
    reg [31:0] col_shift_word_sin2;
    reg [31:0] col_shift_word_sin3;
    reg [13:0] hold_count;

    reg [19:0] control_count;
    reg [24:0] gravity_count;

    reg        key_left_ff0;
    reg        key_left_ff1;
    reg        key_right_ff0;
    reg        key_right_ff1;
    reg        key_down_ff0;
    reg        key_down_ff1;
    reg        btn_a_ff0;
    reg        btn_a_ff1;
    reg        btn_b_ff0;
    reg        btn_b_ff1;

    reg        key_left_prev;
    reg        key_right_prev;
    reg        btn_a_prev;
    reg        btn_b_prev;

    reg [255:0] board_bits;
    reg [255:0] temp_board_bits;
    reg [255:0] compact_board_bits;
    reg [2:0]   cur_piece;
    reg [2:0]   next_piece;
    reg [1:0]   cur_rot;
    reg signed [4:0] cur_x;
    reg signed [5:0] cur_y;
    reg         game_over;
    reg         line_clear_pending;
    reg [16:0]  score_value;
    reg [3:0]   score_d4;
    reg [3:0]   score_d3;
    reg [3:0]   score_d2;
    reg [3:0]   score_d1;
    reg [3:0]   score_d0;
    reg [15:0]  lfsr;

    integer work_x;
    integer work_y;
    integer work_rot;
    integer cell_x;
    integer cell_y;
    integer read_row;
    integer write_row;
    integer clear_count;
    integer new_score;
    integer locked_now;
    integer row_full;

    wire control_pulse;
    wire gravity_pulse;
    wire left_press;
    wire right_press;
    wire rot_r_press;
    wire rot_l_press;
    wire soft_drop_step;
    wire drop_step;

    assign control_pulse = (control_count == CONTROL_TICK_CYC - 1);
    assign gravity_pulse = (gravity_count == GRAVITY_TICK_CYC - 1);

    assign left_press     = control_pulse && key_left_ff1  && !key_left_prev;
    assign right_press    = control_pulse && key_right_ff1 && !key_right_prev;
    assign rot_r_press    = control_pulse && btn_a_ff1     && !btn_a_prev;
    assign rot_l_press    = control_pulse && btn_b_ff1     && !btn_b_prev;
    assign soft_drop_step = control_pulse && key_down_ff1;
    assign drop_step      = gravity_pulse || soft_drop_step;

    function [31:0] row_word_for;
        input [3:0] x_idx;
        integer panel_row;
        reg [15:0] one_hot_row;
        begin
            one_hot_row = 16'd0;
            panel_row = ROW0_IS_LEFT ? x_idx : (DISPLAY_X - 1 - x_idx);
            one_hot_row[panel_row] = 1'b1;
            row_word_for = {one_hot_row, one_hot_row};
        end
    endfunction

    function serial_pick32;
        input [31:0] word;
        input [5:0] idx;
        begin
            if (SHIFT_MSB_FIRST != 0) begin
                serial_pick32 = word[idx];
            end else begin
                serial_pick32 = word[31 - idx];
            end
        end
    endfunction

    function [15:0] piece_shape;
        input [2:0] piece_id;
        input [1:0] rot;
        begin
            piece_shape = 16'h0000;
            case (piece_id)
                3'd0: begin
                    case (rot)
                        2'd0: piece_shape = 16'b0000_1111_0000_0000;
                        2'd1: piece_shape = 16'b0010_0010_0010_0010;
                        2'd2: piece_shape = 16'b0000_1111_0000_0000;
                        default: piece_shape = 16'b0010_0010_0010_0010;
                    endcase
                end
                3'd1: begin
                    piece_shape = 16'b0000_0110_0110_0000;
                end
                3'd2: begin
                    case (rot)
                        2'd0: piece_shape = 16'b0000_1110_0100_0000;
                        2'd1: piece_shape = 16'b0100_0110_0100_0000;
                        2'd2: piece_shape = 16'b0100_1110_0000_0000;
                        default: piece_shape = 16'b0100_1100_0100_0000;
                    endcase
                end
                3'd3: begin
                    case (rot)
                        2'd0: piece_shape = 16'b0000_0110_1100_0000;
                        2'd1: piece_shape = 16'b0100_0110_0010_0000;
                        2'd2: piece_shape = 16'b0000_0110_1100_0000;
                        default: piece_shape = 16'b0100_0110_0010_0000;
                    endcase
                end
                3'd4: begin
                    case (rot)
                        2'd0: piece_shape = 16'b0000_1100_0110_0000;
                        2'd1: piece_shape = 16'b0010_0110_0100_0000;
                        2'd2: piece_shape = 16'b0000_1100_0110_0000;
                        default: piece_shape = 16'b0010_0110_0100_0000;
                    endcase
                end
                3'd5: begin
                    case (rot)
                        2'd0: piece_shape = 16'b1000_1110_0000_0000;
                        2'd1: piece_shape = 16'b0110_0100_0100_0000;
                        2'd2: piece_shape = 16'b0000_1110_0010_0000;
                        default: piece_shape = 16'b0100_0100_1100_0000;
                    endcase
                end
                default: begin
                    case (rot)
                        2'd0: piece_shape = 16'b0010_1110_0000_0000;
                        2'd1: piece_shape = 16'b0100_0100_0110_0000;
                        2'd2: piece_shape = 16'b0000_1110_1000_0000;
                        default: piece_shape = 16'b1100_0100_0100_0000;
                    endcase
                end
            endcase
        end
    endfunction

    function piece_cell;
        input [2:0] piece_id;
        input [1:0] rot;
        input integer local_x;
        input integer local_y;
        reg [15:0] shape;
        integer bit_index_local;
        begin
            piece_cell = 1'b0;
            if ((local_x >= 0) && (local_x < 4) && (local_y >= 0) && (local_y < 4)) begin
                shape = piece_shape(piece_id, rot);
                bit_index_local = 15 - ((local_y * 4) + local_x);
                piece_cell = shape[bit_index_local];
            end
        end
    endfunction

    function position_valid_on_board;
        input [255:0] board_state;
        input [2:0] piece_id;
        input [1:0] rot;
        input integer base_x;
        input integer base_y;
        integer local_x;
        integer local_y;
        integer board_x;
        integer board_y;
        begin
            position_valid_on_board = 1'b1;
            for (local_y = 0; local_y < 4; local_y = local_y + 1) begin
                for (local_x = 0; local_x < 4; local_x = local_x + 1) begin
                    if (piece_cell(piece_id, rot, local_x, local_y)) begin
                        board_x = base_x + local_x;
                        board_y = base_y + local_y;
                        if ((board_x < 0) || (board_x >= FIELD_W) ||
                            (board_y < 0) || (board_y >= FIELD_H)) begin
                            position_valid_on_board = 1'b0;
                        end else if (board_state[(board_y * FIELD_W) + board_x]) begin
                            position_valid_on_board = 1'b0;
                        end
                    end
                end
            end
        end
    endfunction

    function piece_should_lock;
        input [255:0] board_state;
        input [2:0] piece_id;
        input [1:0] rot;
        input integer base_x;
        input integer base_y;
        integer local_x;
        integer local_y;
        integer board_x;
        integer board_y;
        begin
            piece_should_lock = 1'b0;
            for (local_y = 0; local_y < 4; local_y = local_y + 1) begin
                for (local_x = 0; local_x < 4; local_x = local_x + 1) begin
                    if (piece_cell(piece_id, rot, local_x, local_y) &&
                        !piece_cell(piece_id, rot, local_x, local_y + 1)) begin
                        board_x = base_x + local_x;
                        board_y = base_y + local_y;
                        if (board_y >= FIELD_H - 1) begin
                            piece_should_lock = 1'b1;
                        end else if ((board_x >= 0) && (board_x < FIELD_W) &&
                                     (board_y >= 0) &&
                                     board_state[((board_y + 1) * FIELD_W) + board_x]) begin
                            piece_should_lock = 1'b1;
                        end
                    end
                end
            end
        end
    endfunction

    function current_piece_cell;
        input integer board_x;
        input integer board_y;
        integer local_x;
        integer local_y;
        begin
            current_piece_cell = 1'b0;
            for (local_y = 0; local_y < 4; local_y = local_y + 1) begin
                for (local_x = 0; local_x < 4; local_x = local_x + 1) begin
                    if (piece_cell(cur_piece, cur_rot, local_x, local_y) &&
                        (cur_x + local_x == board_x) &&
                        (cur_y + local_y == board_y)) begin
                        current_piece_cell = 1'b1;
                    end
                end
            end
        end
    endfunction

    function [2:0] random_piece;
        input [15:0] seed;
        begin
            case (seed[2:0])
                3'd0: random_piece = 3'd0;
                3'd1: random_piece = 3'd1;
                3'd2: random_piece = 3'd2;
                3'd3: random_piece = 3'd3;
                3'd4: random_piece = 3'd4;
                3'd5: random_piece = 3'd5;
                default: random_piece = 3'd6;
            endcase
        end
    endfunction

    function integer score_increment;
        input integer lines;
        begin
            case (lines)
                1: score_increment = 1;
                2: score_increment = 4;
                3: score_increment = 16;
                4: score_increment = 256;
                default: score_increment = 0;
            endcase
        end
    endfunction

    function [2:0] digit_row_bits;
        input [3:0] digit;
        input integer row;
        begin
            digit_row_bits = 3'b000;
            case (digit)
                4'd0: begin
                    case (row)
                        0: digit_row_bits = 3'b111;
                        1: digit_row_bits = 3'b101;
                        2: digit_row_bits = 3'b101;
                        3: digit_row_bits = 3'b101;
                        4: digit_row_bits = 3'b111;
                    endcase
                end
                4'd1: begin
                    case (row)
                        0: digit_row_bits = 3'b010;
                        1: digit_row_bits = 3'b110;
                        2: digit_row_bits = 3'b010;
                        3: digit_row_bits = 3'b010;
                        4: digit_row_bits = 3'b111;
                    endcase
                end
                4'd2: begin
                    case (row)
                        0: digit_row_bits = 3'b111;
                        1: digit_row_bits = 3'b001;
                        2: digit_row_bits = 3'b111;
                        3: digit_row_bits = 3'b100;
                        4: digit_row_bits = 3'b111;
                    endcase
                end
                4'd3: begin
                    case (row)
                        0: digit_row_bits = 3'b111;
                        1: digit_row_bits = 3'b001;
                        2: digit_row_bits = 3'b111;
                        3: digit_row_bits = 3'b001;
                        4: digit_row_bits = 3'b111;
                    endcase
                end
                4'd4: begin
                    case (row)
                        0: digit_row_bits = 3'b101;
                        1: digit_row_bits = 3'b101;
                        2: digit_row_bits = 3'b111;
                        3: digit_row_bits = 3'b001;
                        4: digit_row_bits = 3'b001;
                    endcase
                end
                4'd5: begin
                    case (row)
                        0: digit_row_bits = 3'b111;
                        1: digit_row_bits = 3'b100;
                        2: digit_row_bits = 3'b111;
                        3: digit_row_bits = 3'b001;
                        4: digit_row_bits = 3'b111;
                    endcase
                end
                4'd6: begin
                    case (row)
                        0: digit_row_bits = 3'b111;
                        1: digit_row_bits = 3'b100;
                        2: digit_row_bits = 3'b111;
                        3: digit_row_bits = 3'b101;
                        4: digit_row_bits = 3'b111;
                    endcase
                end
                4'd7: begin
                    case (row)
                        0: digit_row_bits = 3'b111;
                        1: digit_row_bits = 3'b001;
                        2: digit_row_bits = 3'b001;
                        3: digit_row_bits = 3'b001;
                        4: digit_row_bits = 3'b001;
                    endcase
                end
                4'd8: begin
                    case (row)
                        0: digit_row_bits = 3'b111;
                        1: digit_row_bits = 3'b101;
                        2: digit_row_bits = 3'b111;
                        3: digit_row_bits = 3'b101;
                        4: digit_row_bits = 3'b111;
                    endcase
                end
                default: begin
                    case (row)
                        0: digit_row_bits = 3'b111;
                        1: digit_row_bits = 3'b101;
                        2: digit_row_bits = 3'b111;
                        3: digit_row_bits = 3'b001;
                        4: digit_row_bits = 3'b111;
                    endcase
                end
            endcase
        end
    endfunction

    function digit_pixel_rotated;
        input [3:0] digit;
        input integer local_x;
        input integer local_y;
        reg [2:0] src_row;
        begin
            digit_pixel_rotated = 1'b0;
            if ((local_x >= 0) && (local_x < SCORE_W) &&
                (local_y >= 0) && (local_y < DIGIT_H)) begin
                src_row = digit_row_bits(digit, 4 - local_x);
                digit_pixel_rotated = src_row[2 - local_y];
            end
        end
    endfunction

    function [3:0] score_digit_at;
        input integer digit_index;
        begin
            case (digit_index)
                0: score_digit_at = score_d4;
                1: score_digit_at = score_d3;
                2: score_digit_at = score_d2;
                3: score_digit_at = score_d1;
                default: score_digit_at = score_d0;
            endcase
        end
    endfunction

    function [1:0] logical_pixel_gray;
        input integer screen_y;
        input [3:0] screen_x;
        integer board_y;
        integer preview_y;
        integer digit_slot;
        integer local_x;
        integer local_y;
        begin
            logical_pixel_gray = 2'd0;

            if ((screen_x < FIELD_W) && (screen_y < (FIELD_H * CELL_H))) begin
                board_y = screen_y / CELL_H;
                if (board_bits[(board_y * FIELD_W) + screen_x]) begin
                    logical_pixel_gray = 2'd2;
                end
                if (current_piece_cell(screen_x, board_y) && !game_over) begin
                    logical_pixel_gray = 2'd3;
                end
            end else if ((screen_x >= PREVIEW_X) && (screen_x < PREVIEW_X + PREVIEW_W) &&
                         (screen_y >= PREVIEW_Y) && (screen_y < PREVIEW_Y + PREVIEW_H)) begin
                local_x = screen_x - PREVIEW_X;
                preview_y = (screen_y - PREVIEW_Y) / CELL_H;
                if (piece_cell(next_piece, 2'd0, local_x, preview_y)) begin
                    logical_pixel_gray = 2'd3;
                end
            end else if ((screen_x >= SCORE_X) && (screen_x < SCORE_X + SCORE_W) &&
                         (screen_y >= SCORE_Y) &&
                         (screen_y < SCORE_Y + (SCORE_DIGITS * DIGIT_STRIDE))) begin
                digit_slot = (screen_y - SCORE_Y) / DIGIT_STRIDE;
                local_y = (screen_y - SCORE_Y) % DIGIT_STRIDE;
                local_x = screen_x - SCORE_X;
                if ((digit_slot < SCORE_DIGITS) && (local_y < DIGIT_H) &&
                    digit_pixel_rotated(score_digit_at(digit_slot), local_x, local_y)) begin
                    logical_pixel_gray = 2'd3;
                end
            end

            if (game_over && (screen_x >= 10) && (screen_x < 15) &&
                (screen_y >= 28) && (screen_y < 36) &&
                ((screen_x + screen_y) & 1)) begin
                logical_pixel_gray = 2'd3;
            end
        end
    endfunction

    function [31:0] column_word_sin2_for;
        input [3:0] x_idx;
        input plane;
        integer y;
        integer panel_y;
        integer lane_bit;
        reg [1:0] gray;
        begin
            column_word_sin2_for = 32'd0;
            for (y = 0; y < DISPLAY_Y; y = y + 1) begin
                panel_y = COL0_IS_TOP ? y : (DISPLAY_Y - 1 - y);
                gray = logical_pixel_gray(y, x_idx);
                if (gray[plane]) begin
                    if ((panel_y >= 16) && (panel_y < 32)) begin
                        lane_bit = panel_y - 16;
                        column_word_sin2_for[lane_bit] = 1'b1;
                    end else if (panel_y >= 48) begin
                        lane_bit = panel_y - 32;
                        column_word_sin2_for[lane_bit] = 1'b1;
                    end
                end
            end
        end
    endfunction

    function [31:0] column_word_sin3_for;
        input [3:0] x_idx;
        input plane;
        integer y;
        integer panel_y;
        integer lane_bit;
        reg [1:0] gray;
        begin
            column_word_sin3_for = 32'd0;
            for (y = 0; y < DISPLAY_Y; y = y + 1) begin
                panel_y = COL0_IS_TOP ? y : (DISPLAY_Y - 1 - y);
                gray = logical_pixel_gray(y, x_idx);
                if (gray[plane]) begin
                    if (panel_y < 16) begin
                        lane_bit = panel_y;
                        column_word_sin3_for[lane_bit] = 1'b1;
                    end else if ((panel_y >= 32) && (panel_y < 48)) begin
                        lane_bit = panel_y - 16;
                        column_word_sin3_for[lane_bit] = 1'b1;
                    end
                end
            end
        end
    endfunction

    always @(posedge clk) begin
        if (reset_key) begin
            state             <= STATE_LOAD;
            plane_sel         <= 1'b0;
            scan_row          <= 4'd0;
            bit_index         <= 6'd31;
            row_shift_word    <= 32'd0;
            col_shift_word_sin2 <= 32'd0;
            col_shift_word_sin3 <= 32'd0;
            hold_count        <= 14'd0;
            control_count     <= 20'd0;
            gravity_count     <= 25'd0;
            key_left_ff0      <= 1'b0;
            key_left_ff1      <= 1'b0;
            key_right_ff0     <= 1'b0;
            key_right_ff1     <= 1'b0;
            key_down_ff0      <= 1'b0;
            key_down_ff1      <= 1'b0;
            btn_a_ff0         <= 1'b0;
            btn_a_ff1         <= 1'b0;
            btn_b_ff0         <= 1'b0;
            btn_b_ff1         <= 1'b0;
            key_left_prev     <= 1'b0;
            key_right_prev    <= 1'b0;
            btn_a_prev        <= 1'b0;
            btn_b_prev        <= 1'b0;
            board_bits        <= 256'd0;
            temp_board_bits   <= 256'd0;
            compact_board_bits <= 256'd0;
            cur_piece         <= 3'd0;
            next_piece        <= 3'd1;
            cur_rot           <= 2'd0;
            cur_x             <= SPAWN_X;
            cur_y             <= SPAWN_Y;
            game_over         <= 1'b0;
            line_clear_pending <= 1'b0;
            score_value       <= 17'd0;
            score_d4          <= 4'd0;
            score_d3          <= 4'd0;
            score_d2          <= 4'd0;
            score_d1          <= 4'd0;
            score_d0          <= 4'd0;
            lfsr              <= 16'h1ACE;
            SIN1              <= 1'b0;
            SIN2              <= 1'b0;
            SIN3              <= 1'b0;
            LATCH             <= 1'b0;
            LED_CLK           <= 1'b0;
            STROBE_           <= 1'b1;
        end else begin
            key_left_ff0  <= KEY_LEFT;
            key_left_ff1  <= key_left_ff0;
            key_right_ff0 <= KEY_RIGHT;
            key_right_ff1 <= key_right_ff0;
            key_down_ff0  <= KEY_DOWN;
            key_down_ff1  <= key_down_ff0;
            btn_a_ff0     <= BTN_A;
            btn_a_ff1     <= btn_a_ff0;
            btn_b_ff0     <= BTN_B;
            btn_b_ff1     <= btn_b_ff0;

            lfsr <= {lfsr[14:0], lfsr[15] ^ lfsr[13] ^ lfsr[12] ^ lfsr[10]};

            if (control_pulse) begin
                control_count  <= 20'd0;
                key_left_prev  <= key_left_ff1;
                key_right_prev <= key_right_ff1;
                btn_a_prev     <= btn_a_ff1;
                btn_b_prev     <= btn_b_ff1;
            end else begin
                control_count <= control_count + 20'd1;
            end

            if (gravity_pulse) begin
                gravity_count <= 25'd0;
            end else begin
                gravity_count <= gravity_count + 25'd1;
            end

            case (state)
                STATE_LOAD: begin
                    STROBE_           <= 1'b1;
                    LATCH             <= 1'b0;
                    LED_CLK           <= 1'b0;
                    SIN1              <= 1'b0;
                    SIN2              <= 1'b0;
                    SIN3              <= 1'b0;
                    row_shift_word    <= row_word_for(scan_row);
                    col_shift_word_sin2 <= column_word_sin2_for(scan_row, plane_sel);
                    col_shift_word_sin3 <= column_word_sin3_for(scan_row, plane_sel);
                    bit_index         <= 6'd31;
                    state             <= STATE_SHIFT_SETUP;
                end

                STATE_SHIFT_SETUP: begin
                    LED_CLK <= 1'b0;
                    SIN1    <= serial_pick32(row_shift_word, bit_index);
                    SIN2    <= serial_pick32(col_shift_word_sin2, bit_index);
                    SIN3    <= serial_pick32(col_shift_word_sin3, bit_index);
                    state   <= STATE_SHIFT_HIGH;
                end

                STATE_SHIFT_HIGH: begin
                    LED_CLK <= 1'b1;
                    state   <= STATE_SHIFT_LOW;
                end

                STATE_SHIFT_LOW: begin
                    LED_CLK <= 1'b0;
                    if (bit_index == 0) begin
                        state <= STATE_LATCH_ON;
                    end else begin
                        bit_index <= bit_index - 6'd1;
                        state     <= STATE_SHIFT_SETUP;
                    end
                end

                STATE_LATCH_ON: begin
                    LATCH <= 1'b1;
                    state <= STATE_LATCH_OFF;
                end

                STATE_LATCH_OFF: begin
                    LATCH      <= 1'b0;
                    STROBE_    <= 1'b0;
                    hold_count <= plane_sel ? ((BASE_ON_TICKS << 1) - 1) : (BASE_ON_TICKS - 1);
                    state      <= STATE_HOLD;
                end

                STATE_HOLD: begin
                    if (hold_count == 0) begin
                        STROBE_ <= 1'b1;
                        if (scan_row == DISPLAY_X - 1) begin
                            scan_row  <= 4'd0;
                            plane_sel <= ~plane_sel;
                        end else begin
                            scan_row <= scan_row + 4'd1;
                        end
                        state <= STATE_LOAD;
                    end else begin
                        hold_count <= hold_count - 14'd1;
                    end
                end

                default: begin
                    state <= STATE_LOAD;
                end
            endcase

            if ((control_pulse || gravity_pulse) && !game_over) begin
                if (line_clear_pending) begin
                    // Clear completed rows and drop all rows above them.
                    compact_board_bits = 256'd0;
                    clear_count = 0;
                    write_row = FIELD_H - 1;
                    for (read_row = FIELD_H - 1; read_row >= 0; read_row = read_row - 1) begin
                        row_full = 1;
                        for (cell_x = 0; cell_x < FIELD_W; cell_x = cell_x + 1) begin
                            if (!board_bits[(read_row * FIELD_W) + cell_x]) begin
                                row_full = 0;
                            end
                        end
                        if (row_full != 0) begin
                            clear_count = clear_count + 1;
                        end else begin
                            for (cell_x = 0; cell_x < FIELD_W; cell_x = cell_x + 1) begin
                                compact_board_bits[(write_row * FIELD_W) + cell_x] =
                                    board_bits[(read_row * FIELD_W) + cell_x];
                            end
                            write_row = write_row - 1;
                        end
                    end

                    board_bits <= compact_board_bits;

                    new_score = score_value + score_increment(clear_count);
                    if (new_score > 99999) begin
                        new_score = 99999;
                    end
                    score_value <= new_score[16:0];
                    score_d4    <= (new_score / 10000) % 10;
                    score_d3    <= (new_score / 1000) % 10;
                    score_d2    <= (new_score / 100) % 10;
                    score_d1    <= (new_score / 10) % 10;
                    score_d0    <= new_score % 10;

                    cur_piece <= next_piece;
                    next_piece <= random_piece(lfsr);
                    cur_rot <= 2'd0;
                    cur_x <= SPAWN_X;
                    cur_y <= SPAWN_Y;
                    line_clear_pending <= 1'b0;
                    if (!position_valid_on_board(compact_board_bits, next_piece, 2'd0, SPAWN_X, SPAWN_Y)) begin
                        game_over <= 1'b1;
                    end
                end else begin
                work_x = cur_x;
                work_y = cur_y;
                work_rot = cur_rot;
                locked_now = 0;

                if (piece_should_lock(board_bits, cur_piece, cur_rot, cur_x, cur_y)) begin
                    locked_now = 1;
                end else begin
                    if (rot_r_press) begin
                        if (position_valid_on_board(board_bits, cur_piece, (work_rot + 1) & 2'b11, work_x, work_y)) begin
                            work_rot = (work_rot + 1) & 2'b11;
                        end
                    end
                    if (rot_l_press) begin
                        if (position_valid_on_board(board_bits, cur_piece, (work_rot + 3) & 2'b11, work_x, work_y)) begin
                            work_rot = (work_rot + 3) & 2'b11;
                        end
                    end
                    if (left_press) begin
                        if (position_valid_on_board(board_bits, cur_piece, work_rot, work_x - 1, work_y)) begin
                            work_x = work_x - 1;
                        end
                    end
                    if (right_press) begin
                        if (position_valid_on_board(board_bits, cur_piece, work_rot, work_x + 1, work_y)) begin
                            work_x = work_x + 1;
                        end
                    end

                    if (piece_should_lock(board_bits, cur_piece, work_rot[1:0], work_x, work_y)) begin
                        locked_now = 1;
                    end else if (drop_step) begin
                        if (position_valid_on_board(board_bits, cur_piece, work_rot[1:0], work_x, work_y + 1)) begin
                            work_y = work_y + 1;
                        end
                    end
                end

                if (locked_now != 0) begin
                        // Lock the tetromino at the first supported position.
                        temp_board_bits = board_bits;
                        for (cell_y = 0; cell_y < 4; cell_y = cell_y + 1) begin
                            for (cell_x = 0; cell_x < 4; cell_x = cell_x + 1) begin
                                if (piece_cell(cur_piece, work_rot[1:0], cell_x, cell_y)) begin
                                    temp_board_bits[((work_y + cell_y) * FIELD_W) + (work_x + cell_x)] = 1'b1;
                                end
                            end
                        end

                        clear_count = 0;
                        for (read_row = FIELD_H - 1; read_row >= 0; read_row = read_row - 1) begin
                            row_full = 1;
                            for (cell_x = 0; cell_x < FIELD_W; cell_x = cell_x + 1) begin
                                if (!temp_board_bits[(read_row * FIELD_W) + cell_x]) begin
                                    row_full = 0;
                                end
                            end
                            if (row_full != 0) begin
                                clear_count = clear_count + 1;
                            end
                        end

                        board_bits <= temp_board_bits;
                        if (clear_count != 0) begin
                            line_clear_pending <= 1'b1;
                        end else begin
                            cur_piece <= next_piece;
                            next_piece <= random_piece(lfsr);
                            cur_rot <= 2'd0;
                            cur_x <= SPAWN_X;
                            cur_y <= SPAWN_Y;
                            if (!position_valid_on_board(temp_board_bits, next_piece, 2'd0, SPAWN_X, SPAWN_Y)) begin
                                game_over <= 1'b1;
                            end
                        end
                        locked_now = 1;
                end

                if (locked_now == 0) begin
                    cur_x   <= work_x;
                    cur_y   <= work_y;
                    cur_rot <= work_rot[1:0];
                end
                end
            end
        end
    end

endmodule
Expand
無事死亡

1件のコメントがあります

コメントを残す