在VHDL中使用FPGA的Snake游戏

时间:2014-12-30 06:08:44

标签: vhdl fpga

我使用 Altera 中的 DE2-115 FPGA 在VHDL中编写蛇形游戏。我已经使用VGA协议将FPGA与显示器连接起来以显示游戏。

我有一个问题是显示蛇并在显示器上移动所有推底(FPGA有4个,上/下/左/右

在代码中,使用 SQ_X1 SQ_Y1 来每次推送底部处于低位状态时进行移动。

还有其他方法可以进行动作吗?没有按任何底部(带有循环或类似的东西)和改变方向,按下底部以模拟蛇的运动?

ARCHITECTURE behavior OF hw_image_generator IS
    SIGNAL SQ_X1,SQ_Y1: INTEGER RANGE 0 TO 1688:=400; --initial position X      
    SIGNAL SQ_X2,SQ_Y2: INTEGER RANGE 0 TO 1688:=600; --initial position Y
    SIGNAL DRAW: STD_LOGIC;                                         

BEGIN
    SQ(column,row,SQ_X1,SQ_Y1,DRAW);    -- function that print a square (head of snake with SQ variables)   
    PROCESS(disp_ena, row, column, down, up, left, right)
    BEGIN
        IF(disp_ena = '1') THEN     --display time  
            IF (column=19 or column=1901 or row=19 or row=1181) THEN  -- print the border
                red <= (OTHERS => '1'); 
                green   <= (OTHERS => '0');
                blue <= (OTHERS => '0');
            ELSE
                red <= (OTHERS => '0');
                green   <= (OTHERS => '0');
                blue <= (OTHERS => '0');
            END IF;

            IF (DRAW = '1') THEN   -- it comes from a package that print a square (head of snake)
                red <= (OTHERS => '1');
                green   <= (OTHERS => '1');
                blue <= (OTHERS => '1');
            END IF;

            --up, down right and left are input of FPGA (push bottom)
            --SQs are the current postions of X and Y and the add is to make the movement
            IF(up='0' AND down='1' AND right='1' AND left='1' )THEN
                IF (SQ_X1-40 > 20) THEN 
                    SQ_X1<=SQ_X1-40;
                END IF;        
            END IF;

            IF(up='1' AND down='0' AND right='1' AND left='1')THEN
                IF (SQ_X1+40 < 1180) THEN 
                    SQ_X1<=SQ_X1+40;
                END IF; 
            END IF;
            IF(tup='1' AND down='1' AND right='0' AND left='1')THEN
                IF (SQ_Y1+40 < 1900) THEN 
                    SQ_Y1<=SQ_Y1+40;
                END IF; 
            END IF;
            IF(up='1' AND down='1' AND right='1' AND left='0')THEN
                IF (SQ_Y1-40 > 20) THEN 
                    SQ_Y1<=SQ_Y1-40;
                END IF;
            END IF;
        END IF;

    END PROCESS;
END BEHAVIOR;

1 个答案:

答案 0 :(得分:1)

首先,我建议您将显示和位置更新功能拆分为单独的流程,以使事情更加清晰。

你似乎已经拥有了显示过程所需的一切,除了像Paebbels在评论中提到的那样,这应该是一个同步(即时钟)过程。我假设你的时钟名为clk

PROCESS(clk)
BEGIN
    IF rising_edge(clk) THEN
        --display time 
        IF(disp_ena = '1') THEN 
            -- print the border                
            IF (column=19 or column=1901 or row=19 or row=1181) THEN
                red <= (OTHERS => '1'); 
                green   <= (OTHERS => '0');
                blue <= (OTHERS => '0');
            ELSE
                red <= (OTHERS => '0');
                green   <= (OTHERS => '0');
                blue <= (OTHERS => '0');
            END IF;

            IF (DRAW = '1') THEN   
                red <= (OTHERS => '1');
                green   <= (OTHERS => '1');
                blue <= (OTHERS => '1');
            END IF;
        END IF;
    END IF;
END PROCESS;

对于职位更新,您需要两件事:

  1. 一种状态,代表蛇的当前方向,可以通过按钮对其进行操作。
  2. 某种形式的时基来更新位置。
  3. 第一点很容易实现。首先,声明方向状态信号。

    type direction_t is (DIR_UP, DIR_DOWN, DIR_LEFT, DIR_RIGHT);
    signal direction : direction_t;
    

    然后,创建方向状态过程。

    process(clk) begin
        if rising_edge(clk) then
            if rst = '1' then
                direction <= DIR_UP;
            else
                if up = '0' then
                    direction <= DIR_UP;
                elsif down = '0' then
                    direction <= DIR_DOWN;
                elsif left = '0' then
                    direction <= DIR_LEFT;
                elsif right = '0' then
                    direction <= DIR_RIGHT;
                end if;
            end if;
        end if;
    end process;
    

    我在此假设你有一个名为rst的复位信号,并且该复位是同步高电平有效。适应您的口味。

    此外,请注意,虽然这个特定示例并非严格必要,但作为一般最佳做法:

    • updownleftright如果来自其他时钟域,则应正确重新同步,或完全异步(如按钮)。
    • updownleftright如果直接来自按钮,或其他&#34;弹性&#34;机械设备。

    最后,您需要创建一个定时脉冲的时钟使能信号,并作为位置更新的时基。创建这种信号的一种简单方法通常是使用计数器来分频时钟,但如果时钟太快,如果它是视频时钟可能就是你的情况,你最终会得到一个太大的计数器,这是负面的影响时间。对此有各种解决方案,但在您的情况下,您可以使用rowcolumn信号在每次到达视频帧结束时生成脉冲,如下所示:

    signal update_ce : std_logic;
    
    -- (...)
    
    process(clk) begin
        if rising_edge(clk) then
            -- adapt the value to your taste
            if disp_ena = '1' and column = 1901 and row = 1181 then
                update_ce <= '1';
            else
                update_ce <= '0';
            end if;
        end if;
    end process;
    

    如果更新速度过快,您可能需要再次使用计数器对此信号的频率进行分频,但至少您的计数器不必数以百万计。鉴于此信号,您​​现在可以更新您的位置:

    process(clk)
    begin
        if rising_edge(clk) then
            if rst = '1' then
                -- adapt starting position to your taste
                SQ_X1<= 100;
                SQ_Y1<= 100;
            elsif update_ce = '1' then
                case direction is
                when DIR_UP =>
                    if SQ_X1 - 1 > 20 then 
                        SQ_X1 <= SQ_X1 - 1;
                    end if;
    
                when DIR_DOWN =>
                    if SQ_X1 + 1 < 1180 then 
                        SQ_X1 <= SQ_X1 + 1;
                    end if; 
    
                when DIR_LEFT =>
                    if SQ_Y1 - 1 > 20 then 
                        SQ_Y1 <= SQ_Y1 - 1;
                    end if;
    
                when others =>
                -- DIR_RIGHT
                    if SQ_Y1 - 1 > 20 then 
                        SQ_Y1 <= SQ_Y1 - 1;
                    end if;                    
                end case;
            end if;
        end if;
    end process;