コーディングエージェントのcodexを使って,SystemVerilogのコードをVerylにポーティングします.

 ターゲットアプリケーションは,マトリクスLEDを使ったテトリスです.ベースとして使ったプロジェクトはこちらです.今回は,LEDマトリクスの制御部分と,テトリスのゲームロジックをポーティングします.

 生成したVerylコードは次の通りです.特に指示をせずコード生成させたら,何でもかんでもi32にされました.合成できないので,致し方なく手動で修正し,SVにコンパイルして実機(Tang Primer 25K+LEDマトリクス)で動作を確認できました.

Verilog
// ---------------------------------------------------------------------------
// matrix_led_top.veryl
//
// Tetris game on 16x64 LED matrix with DualShock controller input.
// Ported from matrix_led_top.v to Veryl.
// Target: Gowin EDA synthesis.
// ---------------------------------------------------------------------------

module matrix_led_top (
    clk         : input  '_ clock,
    user_key    : input  logic,
    user_key2   : input  logic,
    sin1        : output logic,
    sin2        : output logic,
    sin3        : output logic,
    latch       : output logic,
    led_clk     : output '_ clock,
    strobe_n    : output logic,
    joystick_cs2  : output logic,
    joystick_mosi2: output logic,
    joystick_clk2 : output '_ clock,
    joystick_cs   : output logic,
    joystick_mosi : output logic,
    joystick_miso : input  logic,
    joystick_miso2: input  logic,
    joystick_clk  : output '_ clock,
    led           : output logic<8>,
) {
    // -----------------------------------------------------------------------
    // Parameters
    // -----------------------------------------------------------------------
    const CLK_HZ          : u32 = 50_000_000;
    const DISPLAY_X       : u32 = 16;
    const DISPLAY_Y       : u32 = 64;
    const FIELD_W         : u32 = 8;
    const FIELD_H         : u32 = 32;
    const CELL_H          : u32 = 1;
    const PREVIEW_X       : u32 = 10;
    const PREVIEW_Y       : u32 = 4;
    const PREVIEW_W       : u32 = 4;
    const PREVIEW_H       : u32 = 4;
    const SCORE_X         : i32 = 10;
    const SCORE_Y         : u32 = 40;
    const SCORE_W         : u32 = 5;
    const SCORE_DIGITS    : u32 = 5;
    const DIGIT_H         : u32 = 3;
    const DIGIT_STRIDE    : u32 = 4;
    const CONTROL_TICK_CYC: u32 = CLK_HZ / 50;
    const GRAVITY_TICK_CYC: u32 = CLK_HZ / 2;
    const BASE_ON_TICKS   : u32 = 5_000;
    const SPAWN_X         : u32 = 2;
    const SPAWN_Y         : u32 = 0;
    const SCLK_DELAY      : u32 = 200;

    // Display state machine states
    const STATE_LOAD        : logic<3> = 3'd0;
    const STATE_SHIFT_SETUP : logic<3> = 3'd1;
    const STATE_SHIFT_HIGH  : logic<3> = 3'd2;
    const STATE_SHIFT_LOW   : logic<3> = 3'd3;
    const STATE_LATCH_ON    : logic<3> = 3'd4;
    const STATE_LATCH_OFF   : logic<3> = 3'd5;
    const STATE_HOLD        : logic<3> = 3'd6;

    // Display orientation
    const ROW0_IS_LEFT   : u32 = 0;
    const COL0_IS_TOP    : u32 = 0;
    const SHIFT_MSB_FIRST: u32 = 0;

    // -----------------------------------------------------------------------
    // DualShock clock generation (250 kHz from 50 MHz)
    // -----------------------------------------------------------------------
    var sclk    : '_ clock;
    var sclk_cnt: logic<8>;   // ceil(log2(200)) = 8 bits

    always_ff (clk) {
        if sclk_cnt == (SCLK_DELAY - 1) {
            sclk     = ~sclk;
            sclk_cnt = 8'h00;
        } else {
            sclk_cnt = sclk_cnt + 8'h01;
        }
    }

    // -----------------------------------------------------------------------
    // DualShock controller instance
    // -----------------------------------------------------------------------
    var joy_rx : logic<8>[2];
    var joy_rx2: logic<8>[2];   // second controller (unused in game logic)

    // Unused outputs for second controller tied off
    assign joystick_cs2   = 1'b1;
    assign joystick_mosi2 = 1'b1;
    assign joystick_clk2  = 1'b1;

    inst controller: dualshock_controller (
        i_clk250k  : sclk,
        i_rstn     : 1'b1,
        o_ps_clk   : joystick_clk,
        o_ps_sel   : joystick_cs,
        o_ps_txd   : joystick_mosi,
        i_ps_rxd   : 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    : _,
        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,
    );

    // -----------------------------------------------------------------------
    // Button / joystick decoding
    // -----------------------------------------------------------------------
    // DualShock byte0 bit mapping (active-low):
    //   bit7=left  bit5=right  bit6=down  bit3=start
    // byte1: bit4=triangle  bit7=square  bit6=cross  bit5=circle
    var key_left : logic;
    var key_right: logic;
    var key_down : logic;
    var btn_a    : logic;
    var btn_b    : logic;
    var reset_key: logic;

    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];

    assign led[0] = ~joy_rx[0][5];
    assign led[1] = ~joy_rx[0][7];
    assign led[7:2] = 6'h00;

    // -----------------------------------------------------------------------
    // Registers
    // -----------------------------------------------------------------------
    var state              : logic<3>;
    var plane_sel          : logic;
    var scan_row           : logic<4>;
    var bit_index          : logic<6>;
    var row_shift_word     : logic<32>;
    var col_shift_word_sin2: logic<32>;
    var col_shift_word_sin3: logic<32>;
    var hold_count         : logic<14>;

    var control_count: logic<20>;
    var gravity_count: logic<25>;

    var key_left_ff0 : logic;
    var key_left_ff1 : logic;
    var key_right_ff0: logic;
    var key_right_ff1: logic;
    var key_down_ff0 : logic;
    var key_down_ff1 : logic;
    var btn_a_ff0    : logic;
    var btn_a_ff1    : logic;
    var btn_b_ff0    : logic;
    var btn_b_ff1    : logic;

    var key_left_prev : logic;
    var key_right_prev: logic;
    var btn_a_prev    : logic;
    var btn_b_prev    : logic;

    var board_bits        : logic<256>;
    var temp_board_bits   : logic<256>;
    var compact_board_bits: logic<256>;
    var cur_piece         : logic<3>;
    var next_piece        : logic<3>;
    var cur_rot           : logic<2>;
    var cur_x             : logic<5>;   // signed, but stored as 2's complement
    var cur_y             : logic<6>;
    var game_over         : logic;
    var line_clear_pending: logic;
    var score_value       : logic<17>;
    var score_d4          : logic<4>;
    var score_d3          : logic<4>;
    var score_d2          : logic<4>;
    var score_d1          : logic<4>;
    var score_d0          : logic<4>;
    var lfsr              : logic<16>;

    // -----------------------------------------------------------------------
    // Derived pulse / press signals
    // -----------------------------------------------------------------------
    var control_pulse: logic;
    var gravity_pulse: logic;
    var left_press   : logic;
    var right_press  : logic;
    var rot_r_press  : logic;
    var rot_l_press  : logic;
    var soft_drop_step: logic;
    var drop_step    : logic;

    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;

    // -----------------------------------------------------------------------
    // Functions
    // -----------------------------------------------------------------------

    // Build row one-hot word for a given column index
    function row_word_for (
        x_idx: input logic<4>,
    ) -> logic<32> {
        var one_hot_row: logic<16>;
        var panel_row  : u32;
        one_hot_row = 16'h0000;
        if ROW0_IS_LEFT != 0 {
            panel_row = x_idx as u32;
        } else {
            panel_row = (DISPLAY_X - 1) - x_idx as u32;
        }
        one_hot_row[panel_row] = 1'b1;
        return {one_hot_row, one_hot_row};
    }

    // Pick one bit from a 32-bit word (MSB or LSB first)
    function serial_pick32 (
        word: input logic<32>,
        idx : input logic<6>,
    ) -> logic {
        if SHIFT_MSB_FIRST != 0 {
            return word[idx];
        } else {
            return word[31 - idx as u32];
        }
    }

    // Tetromino shape look-up (4x4 bitmap packed into 16 bits)
    function piece_shape (
        piece_id: input logic<3>,
        rot     : input logic<2>,
    ) -> logic<16> {
        var result: logic<16>;
        result = 16'h0000;
        case piece_id {
            3'd0: {   // I
                case rot {
                    2'd0, 2'd2: result = 16'b0000_1111_0000_0000;
                    default   : result = 16'b0010_0010_0010_0010;
                }
            }
            3'd1: {   // O
                result = 16'b0000_0110_0110_0000;
            }
            3'd2: {   // T
                case rot {
                    2'd0: result = 16'b0000_1110_0100_0000;
                    2'd1: result = 16'b0100_0110_0100_0000;
                    2'd2: result = 16'b0100_1110_0000_0000;
                    default: result = 16'b0100_1100_0100_0000;
                }
            }
            3'd3: {   // S
                case rot {
                    2'd0, 2'd2: result = 16'b0000_0110_1100_0000;
                    default   : result = 16'b0100_0110_0010_0000;
                }
            }
            3'd4: {   // Z
                case rot {
                    2'd0, 2'd2: result = 16'b0000_1100_0110_0000;
                    default   : result = 16'b0010_0110_0100_0000;
                }
            }
            3'd5: {   // J
                case rot {
                    2'd0: result = 16'b1000_1110_0000_0000;
                    2'd1: result = 16'b0110_0100_0100_0000;
                    2'd2: result = 16'b0000_1110_0010_0000;
                    default: result = 16'b0100_0100_1100_0000;
                }
            }
            default: {   // L
                case rot {
                    2'd0: result = 16'b0010_1110_0000_0000;
                    2'd1: result = 16'b0100_0100_0110_0000;
                    2'd2: result = 16'b0000_1110_1000_0000;
                    default: result = 16'b1100_0100_0100_0000;
                }
            }
        }
        return result;
    }

    // Test if a local cell of a piece is set
    function piece_cell (
        piece_id: input logic<3>,
        rot     : input logic<2>,
        local_x : input i32,
        local_y : input i32,
    ) -> logic {
        var shape    : logic<16>;
        var bit_pos  : i32;
        if local_x >= 0 && local_x <: 4 && local_y >= 0 && local_y <: 4 {
            shape   = piece_shape(piece_id, rot);
            bit_pos = 15 - (local_y * 4 + local_x);
            return shape[bit_pos as u32];
        } else {
            return 1'b0;
        }
    }

    // Check whether a piece position is valid on the board
    function position_valid (
        board_state: input logic<256>,
        piece_id   : input logic<3>,
        rot        : input logic<2>,
        base_x     : input i32,
        base_y     : input i32,
    ) -> logic {
        var valid  : logic;
        var bx : i32;
        var by : i32;
        valid = 1'b1;
        for ly in 0..4 {
            for lx in 0..4 {
                if piece_cell(piece_id, rot, lx, ly) {
                    bx = base_x + lx;
                    by = base_y + ly;
                    if bx <: 0 || bx >= FIELD_W as i32 ||
                       by <: 0 || by >= FIELD_H as i32 {
                        valid = 1'b0;
                    } else if board_state[(by * FIELD_W as i32 + bx) as u32] {
                        valid = 1'b0;
                    }
                }
            }
        }
        return valid;
    }

    // Check whether the piece should lock (nothing below it)
    function piece_should_lock (
        board_state: input logic<256>,
        piece_id   : input logic<3>,
        rot        : input logic<2>,
        base_x     : input i32,
        base_y     : input i32,
    ) -> logic {
        var should_lock: logic;
        var bx     : i32;
        var by     : i32;
        should_lock = 1'b0;
        for ly in 0..4 {
            for lx in 0..4 {
                if piece_cell(piece_id, rot, lx, ly) &&
                   !piece_cell(piece_id, rot, lx, ly + 1) {
                    bx = base_x + lx;
                    by = base_y + ly;
                    if by >= FIELD_H as i32 - 1 {
                        should_lock = 1'b1;
                    } else if bx >= 0 && bx <: FIELD_W as i32 && by >= 0 &&
                              board_state[((by + 1) * FIELD_W as i32 + bx) as u32] {
                        should_lock = 1'b1;
                    }
                }
            }
        }
        return should_lock;
    }

    // Check if the current (active) piece occupies a given board cell
    function current_piece_cell (
        bx: input i32,
        by: input i32,
    ) -> logic {
        var found: logic;
        found = 1'b0;
        for ly in 0..4 {
            for lx in 0..4 {
                if piece_cell(cur_piece, cur_rot, lx, ly) &&
                   cur_x + lx == bx &&
                   cur_y + ly == by {
                    found = 1'b1;
                }
            }
        }
        return found;
    }

    // LFSR-based random piece selector
    function random_piece (
        seed: input logic<16>,
    ) -> logic<3> {
        case seed[2:0] {
            3'd0   : return 3'd0;
            3'd1   : return 3'd1;
            3'd2   : return 3'd2;
            3'd3   : return 3'd3;
            3'd4   : return 3'd4;
            3'd5   : return 3'd5;
            default: return 3'd6;
        }
    }

    // Score increment per number of cleared lines
    function score_increment (
        lines: input i32,
    ) -> i32 {
        case lines {
            1      : return 1;
            2      : return 4;
            3      : return 16;
            4      : return 256;
            default: return 0;
        }
    }

    // 3-bit row bitmap for a 7-segment-style digit
    function digit_row_bits (
        digit: input logic<4>,
        row  : input i32,
    ) -> logic<3> {
        var r: logic<3>;
        r = 3'b000;
        case digit {
            4'd0: { case row { 0: r=3'b111; 1: r=3'b101; 2: r=3'b101; 3: r=3'b101; 4: r=3'b111; default:{} } }
            4'd1: { case row { 0: r=3'b010; 1: r=3'b110; 2: r=3'b010; 3: r=3'b010; 4: r=3'b111; default:{} } }
            4'd2: { case row { 0: r=3'b111; 1: r=3'b001; 2: r=3'b111; 3: r=3'b100; 4: r=3'b111; default:{} } }
            4'd3: { case row { 0: r=3'b111; 1: r=3'b001; 2: r=3'b111; 3: r=3'b001; 4: r=3'b111; default:{} } }
            4'd4: { case row { 0: r=3'b101; 1: r=3'b101; 2: r=3'b111; 3: r=3'b001; 4: r=3'b001; default:{} } }
            4'd5: { case row { 0: r=3'b111; 1: r=3'b100; 2: r=3'b111; 3: r=3'b001; 4: r=3'b111; default:{} } }
            4'd6: { case row { 0: r=3'b111; 1: r=3'b100; 2: r=3'b111; 3: r=3'b101; 4: r=3'b111; default:{} } }
            4'd7: { case row { 0: r=3'b111; 1: r=3'b001; 2: r=3'b001; 3: r=3'b001; 4: r=3'b001; default:{} } }
            4'd8: { case row { 0: r=3'b111; 1: r=3'b101; 2: r=3'b111; 3: r=3'b101; 4: r=3'b111; default:{} } }
            default: { case row { 0: r=3'b111; 1: r=3'b101; 2: r=3'b111; 3: r=3'b001; 4: r=3'b111; default:{} } }
        }
        return r;
    }

    // Read rotated digit pixel
    function digit_pixel_rotated (
        digit  : input logic<4>,
        local_x: input i32,
        local_y: input i32,
    ) -> logic {
        var src_row: logic<3>;
        if local_x >= 0 && local_x <: SCORE_W as i32 &&
           local_y >= 0 && local_y <: DIGIT_H as i32 {
            src_row = digit_row_bits(digit, 4 - local_x);
            return src_row[2 - local_y as u32];
        } else {
            return 1'b0;
        }
    }

    // Get score digit by position index
    function score_digit_at (
        digit_index: input i32,
    ) -> logic<4> {
        case digit_index {
            0      : return score_d4;
            1      : return score_d3;
            2      : return score_d2;
            3      : return score_d1;
            default: return score_d0;
        }
    }

    // Compute 2-bit grey level for a logical screen pixel
    function logical_pixel_gray (
        screen_y: input i32,
        screen_x: input logic<4>,
    ) -> logic<2> {
        var gray      : logic<2>;
        var board_y   : i32;
        var preview_y : i32;
        var digit_slot: i32;
        var local_x   : i32;
        var local_y   : i32;

        gray = 2'd0;

        if screen_x <: FIELD_W && screen_y <: FIELD_H as i32 * CELL_H as i32 {
            board_y = screen_y / CELL_H as i32;
            if board_bits[(board_y * FIELD_W as i32 + screen_x as i32) as u32] {
                gray = 2'd2;
            }
            if current_piece_cell(screen_x as i32, board_y) && !game_over {
                gray = 2'd3;
            }
        } else if screen_x >= PREVIEW_X as i32 &&
                  screen_x <: PREVIEW_X as i32 + PREVIEW_W as i32 &&
                  screen_y          >= PREVIEW_Y as i32 &&
                  screen_y          <: PREVIEW_Y as i32 + PREVIEW_H as i32 {
            local_x  = screen_x as i32 - PREVIEW_X as i32;
            preview_y = (screen_y - PREVIEW_Y as i32) / CELL_H as i32;
            if piece_cell(next_piece, 2'd0, local_x, preview_y) {
                gray = 2'd3;
            }
        } else if screen_x >= SCORE_X &&
                  screen_x <: SCORE_X + SCORE_W &&
                  screen_y          >= SCORE_Y &&
                  screen_y          <: SCORE_Y + SCORE_DIGITS * DIGIT_STRIDE {
            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) {
                gray = 2'd3;
            }
        }

        // Game-over checkerboard overlay
        if game_over &&
           screen_x >= 10 && screen_x <: 15 &&
           screen_y >= 28 && screen_y <: 36 &&
           (screen_x + screen_y) & 1 != 0 {
            gray = 2'd3;
        }

        return gray;
    }

    // Build SIN2 column word for a given x column and plane
    function column_word_sin2_for (
        x_idx: input logic<4>,
        plane: input logic,
    ) -> logic<32> {
        var result  : logic<32>;
        var panel_y : i32;
        var lane_bit: i32;
        var gray    : logic<2>;
        result = 32'd0;
        for y in 0..DISPLAY_Y as i32 {
            if COL0_IS_TOP != 0 {
                panel_y = y;
            } else {
                panel_y = DISPLAY_Y as i32 - 1 - y;
            }
            gray = logical_pixel_gray(y, x_idx);
            if gray[plane as u32] {
                if panel_y >= 16 && panel_y <: 32 {
                    lane_bit = panel_y - 16;
                    result[lane_bit as u32] = 1'b1;
                } else if panel_y >= 48 {
                    lane_bit = panel_y - 32;
                    result[lane_bit as u32] = 1'b1;
                }
            }
        }
        return result;
    }

    // Build SIN3 column word for a given x column and plane
    function column_word_sin3_for (
        x_idx: input logic<4>,
        plane: input logic,
    ) -> logic<32> {
        var result  : logic<32>;
        var panel_y : i32;
        var lane_bit: i32;
        var gray    : logic<2>;
        result = 32'd0;
        for y in 0..DISPLAY_Y as i32 {
            if COL0_IS_TOP != 0 {
                panel_y = y;
            } else {
                panel_y = DISPLAY_Y as i32 - 1 - y;
            }
            gray = logical_pixel_gray(y, x_idx);
            if gray[plane as u32] {
                if panel_y <: 16 {
                    lane_bit = panel_y;
                    result[lane_bit as u32] = 1'b1;
                } else if panel_y >= 32 && panel_y <: 48 {
                    lane_bit = panel_y - 16;
                    result[lane_bit as u32] = 1'b1;
                }
            }
        }
        return result;
    }

    // -----------------------------------------------------------------------
    // Main always_ff block
    // -----------------------------------------------------------------------
    always_ff (clk) {
        if reset_key {
            // --- Synchronous reset (from DualShock Start button) ---
            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_n           = 1'b1;
        } else {
            // --- Input double-flip-flop synchronisers ---
            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 advance (Fibonacci: taps 16,14,13,11 → bits 15,13,12,10)
            lfsr = {lfsr[14:0], lfsr[15] ^ lfsr[13] ^ lfsr[12] ^ lfsr[10]};

            // Control timer
            if control_pulse {
                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;
            } else {
                control_count = control_count + 20'd1;
            }

            // Gravity timer
            if gravity_pulse {
                gravity_count = 25'd0;
            } else {
                gravity_count = gravity_count + 25'd1;
            }

            // ---------------------------------------------------------------
            // LED display state machine
            // ---------------------------------------------------------------
            case state {
                STATE_LOAD: {
                    strobe_n            = 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;
                }
                STATE_SHIFT_SETUP: {
                    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;
                }
                STATE_SHIFT_HIGH: {
                    led_clk = 1'b1;
                    state   = STATE_SHIFT_LOW;
                }
                STATE_SHIFT_LOW: {
                    led_clk = 1'b0;
                    if bit_index == 6'd0 {
                        state = STATE_LATCH_ON;
                    } else {
                        bit_index = bit_index - 6'd1;
                        state     = STATE_SHIFT_SETUP;
                    }
                }
                STATE_LATCH_ON: {
                    latch = 1'b1;
                    state = STATE_LATCH_OFF;
                }
                STATE_LATCH_OFF: {
                    latch      = 1'b0;
                    strobe_n   = 1'b0;
                    hold_count = if plane_sel==1 ? ((BASE_ON_TICKS << 1) - 1) : (BASE_ON_TICKS - 1);
                    state = STATE_HOLD;
                }
                STATE_HOLD: {
                    if hold_count == 14'd0 {
                        strobe_n = 1'b1;
                        if scan_row == (DISPLAY_X - 1) {
                            scan_row  = 4'd0;
                            plane_sel = ~plane_sel;
                        } else {
                            scan_row = scan_row + 4'd1;
                        }
                        state = STATE_LOAD;
                    } else {
                        hold_count = hold_count - 14'd1;
                    }
                }
                default: {
                    state = STATE_LOAD;
                }
            }

            // ---------------------------------------------------------------
            // Tetris game logic (runs on control_pulse or gravity_pulse)
            // ---------------------------------------------------------------
            if (control_pulse | gravity_pulse) & ~game_over {

                if line_clear_pending {
                    // ---- Compact board: remove full rows ----
                    var compact: logic<256>;
                    var clr_cnt: i32;
                    var wr_row : i32;
                    var rd_row : i32;
                    var full   : logic;
                    var ns     : i32;

                    compact = 256'd0;
                    clr_cnt = 0;
                    wr_row  = FIELD_H as i32 - 1;

                    for r in 0..FIELD_H as i32 {
                        rd_row = FIELD_H as i32 - 1 - r;
                        full   = 1'b1;
                        for cx in 0..FIELD_W as i32 {
                            if !board_bits[(rd_row * FIELD_W as i32 + cx) as u32] {
                                full = 1'b0;
                            }
                        }
                        if full {
                            clr_cnt = clr_cnt + 1;
                        } else {
                            for cx in 0..FIELD_W as i32 {
                                compact[(wr_row * FIELD_W as i32 + cx) as u32] =
                                    board_bits[(rd_row * FIELD_W as i32 + cx) as u32];
                            }
                            wr_row = wr_row - 1;
                        }
                    }

                    board_bits = compact;

                    ns = score_value as i32 + score_increment(clr_cnt);
                    if ns >: 99999 { ns = 99999; }
                    score_value = ns;
                    score_d4    = (ns / 10000 % 10) ;
                    score_d3    = (ns / 1000  % 10) ;
                    score_d2    = (ns / 100   % 10) ;
                    score_d1    = (ns / 10    % 10) ;
                    score_d0    = (ns         % 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(compact, next_piece, 2'd0,
                                       SPAWN_X as i32, SPAWN_Y as i32) {
                        game_over = 1'b1;
                    }

                } else {
                    // ---- Normal game tick ----
                    var wx  : u8;
                    var wy  : u8;
                    var wrot    : i32;
                    var locked  : logic;

                    wx     = cur_x;
                    wy     = cur_y;
                    wrot   = cur_rot as i32;
                    locked = 1'b0;

                    if piece_should_lock(board_bits, cur_piece, cur_rot,
                                        cur_x , cur_y ) {
                        locked = 1'b1;
                    } else {
                        if rot_r_press {
                            if position_valid(board_bits, cur_piece,
                                              ((wrot + 1) & 3), wx, wy) {
                                wrot = (wrot + 1) & 3;
                            }
                        }
                        if rot_l_press {
                            if position_valid(board_bits, cur_piece,
                                              ((wrot + 3) & 3), wx, wy) {
                                wrot = (wrot + 3) & 3;
                            }
                        }
                        if left_press {
                            if position_valid(board_bits, cur_piece,
                                              wrot, wx - 1, wy) {
                                wx = wx - 1;
                            }
                        }
                        if right_press {
                            if position_valid(board_bits, cur_piece,
                                              wrot , wx + 1, wy) {
                                wx = wx + 1;
                            }
                        }

                        if piece_should_lock(board_bits, cur_piece,
                                             wrot , wx, wy) {
                            locked = 1'b1;
                        } else if drop_step {
                            if position_valid(board_bits, cur_piece,
                                             wrot , wx, wy + 1) {
                                wy = wy + 1;
                            }
                        }
                    }

                    if locked {
                        // ---- Lock piece into board ----
                        var tmp    : logic<256>;
                        var clr_cnt: i32;
                        var full   : logic;

                        tmp = board_bits;
                        for ly in 0..4 {
                            for lx in 0..4 {
                                if piece_cell(cur_piece, wrot , lx, ly) {
                                    tmp[((wy + ly) * FIELD_W as i32 + (wx + lx)) as u32] = 1'b1;
                                }
                            }
                        }

                        clr_cnt = 0;
                        for r in 0..FIELD_H as i32 {
                            var rr: i32;
                            rr = FIELD_H - 1 - r;
                            full = 1'b1;
                            for cx in 0..FIELD_W as i32 {
                                if !tmp[(rr * FIELD_W as i32 + cx) as u32] {
                                    full = 1'b0;
                                }
                            }
                            if full { clr_cnt = clr_cnt + 1; }
                        }

                        board_bits = tmp;

                        if clr_cnt != 0 {
                            line_clear_pending = 1'b1;
                        } else {
                            cur_piece  = next_piece;
                            next_piece = random_piece(lfsr);
                            cur_rot    = 2'd0;
                            cur_x      = SPAWN_X;
                            cur_y      = SPAWN_Y;
                            if !position_valid(tmp, next_piece, 2'd0,
                                               SPAWN_X as i32, SPAWN_Y as i32) {
                                game_over = 1'b1;
                            }
                        }
                    } else {
                        cur_x   = wx ;
                        cur_y   = wy ;
                        cur_rot = wrot;
                    }
                }
            }
        }
    }
}
Expand

コメントを残す