這是一個使用FPGA制作的遊戲,能實作Flappy Bird遊戲的基本功能。其中參考了許多大神的部落格,代碼,思路與一些特别的設計。完成大一的數電作業。(整個項目在我的GitHub,項目位址在文末)
按鍵:UP-上升 RST-重新開始 BEGIN-開始遊戲
效果圖如下:
接下來我将陸續記錄錯誤彙總以及一些代碼實作。
ERROR彙總
ERROR1:
Error: Selected device has 46 RAM location(s) of type M9K. However, the current design needs more than 46 to successfully fit
Error: Can't place all RAM cells in design
這個問題是FPGA内部ROM資源不足,也就是你存儲的東西超過它的記憶體了。
1解決方法:
壓縮圖檔>優化代碼>外置sd卡
ERROR2:
Error: Can't find Memory Initialization File or Hexadecimal (Intel-Format) File ../../../bird.mif for ROM instance
解決方法:這是一個很皮的錯誤,明明有MIF生成對應HEX檔案可是工程就找不到...百度無果後我重建立立了一個ROM就解決了,應該是之前生成的HEX與後面修改來源檔案後的HEX沖突了
ERROR3:
Error: Can't resolve multiple constant drivers for net“xx”
解決方法:這種問題屬于文法錯誤,兩個或多個always中對同一個變量指派就會産生這種錯誤,自行完善下就行。
ERROR4:
變量指派錯誤
這種錯誤可能編譯器是找不出來的,初學者很容易犯。注意寄存器的位數設定與所需位數是否吻合。
原理圖
其中具體的代碼實作後面作業寫完再更
6月2日更新(有點忙)
加入了一些新的功能(死亡,通關,計分),完善了随機子產品
以下為代碼實作思路:
核心控制代碼 > 讀取ROM > VGA顯示
VGA
筆者認為,VGA其實很簡單,就和加強版點陣差不多,就多了點時序控制。
以下為VGA信号時序
如圖,在VGA的時序裡,并不是所有的時間都是在輸出顯示信号(隻在Active vidio time輸出顯示信号),還有一部分時間
是用來同步的。
是以我們要把握好以下幾個量:
valid (顯示信号有效段)
hsync(同步信号) = (hcount > hsync_end);
vsync (同步信号) = (vcount > vsync_end); 與上圖一直即可(注意頻率不同低電平時間不同)
計數器轉成對應分辨率,友善開發
assign hc = hcount - hdat_begin;
assign vc = vcount - vdat_begin;
驅動好了以後你就可以在螢幕上顯示一些東西了!
最簡單的啟動代碼(600*800分辨率)
module vga(clk,rst,hsync,vsync,x_cnt,y_cnt,valid);
input clk;
input rst;
output hsync;
output vsync;
output [10:0]x_cnt;
output [9:0]y_cnt;
output valid;
reg[10:0]x_cnt; //行坐标
reg[9:0]y_cnt; //列坐标
[email protected](posedge clk or negedge rst)
if(!rst) x_cnt <= 11'd0;
else if (x_cnt == 11'd1039) x_cnt <= 11'd0;
else x_cnt <= x_cnt + 1'b1;
[email protected](posedge clk or negedge rst)
if(!rst) y_cnt <= 10'd0;
else if (y_cnt == 10'd665) y_cnt <= 10'd0;
else if (x_cnt == 11'd1039)y_cnt <= y_cnt + 1'b1;
wire valid ; //有效顯示區标志
assign valid = (x_cnt >= 11'd176)&&(x_cnt <=11'd976)
&&(y_cnt >= 10'd43)&&(y_cnt < 10'd643);
wire[9:0]xpos,ypos; //有效顯示區坐标
assign xpos = x_cnt - 11'd176;
assign ypos = y_cnt - 10'd43;
reg hsync_r,vsync_r;
[email protected](posedge clk or negedge rst)
if(!rst) hsync_r <= 1'b1;
else if (x_cnt == 11'd0) hsync_r <= 1'b0; //産生hsync信号
else if (x_cnt == 11'd120) hsync_r <= 1'b1;
[email protected](posedge clk or negedge rst_n)
if(!rst) vsync_r <= 1'b1;
else if (y_cnt == 10'd0) vsync_r <= 1'b0; //産生vsync信号
else if (y_cnt == 10'd6) vsync_r <= 1'b1;
assign hsync = hsync_r;
assign vsync = vsync_r;
endmodule
ROM
有很多朋友在問我如何将自己的圖檔顯示到螢幕上,我來說一下我的方法
首先得有一張BMP格式的圖檔(不是的轉化一下就好),然後對它取模生成MIF檔案(軟體為bmptomif),然後再将MIF存入ROM中即可
MIF如圖 可以看到每個位址對應了3位的RGB資料(紅綠藍三色) 并在開頭說明了位址有多少和每一個位址存了多少資料(這些資料與後面設定ROM有關系)
CONTROL
控制部分要對資料進行分類(什麼時候該出現在螢幕哪裡)并加上一點控制來實作遊戲效果
我們要對幾個功能進行分析及解決實作
開始界面
遊戲進行界面
死亡界面
勝利界面
其中主要設計遊戲運作界面
如何把遊戲在螢幕上動态顯示呢?我們需要讓靜态的圖檔動起來(這裡不需要太完美像動畫一樣真正動起來,隻是圖檔簡單移動,有能力的可以試試動畫播放)
把小鳥看作像素塊的堆疊,小鳥的移動就是對應像素塊坐标的移動,那麼綠色水管的像素快和小鳥像素塊坐标有重疊的時候判斷小鳥死亡。是不是很簡單(當然其中水管的生成要随機)。
關于計分子產品...我在寫的時候犯了之前解決過的錯誤:忘了分頻。不過這一次我用了另一種方法解決:用邏輯判斷來避免重複計數。
always @ (posedge vga_clk )
begin
if((push <= move_x||push1 <= move_x||push2 <= move_x||push3 <= move_x)&&!ad)
begin
score <= score + 1'b1;
ad <= 1;
end
else if(push > move_x&&push1 > move_x&&push2 > move_x&&push3 > move_x)
ad <= 0;
if(score == 30)
begin
win <= 1;
end
if(!reset)
begin
win <= 0;
score <= 0;
end
end
嗯~ o(* ̄▽ ̄*)o每想出一種新方法,就會有一種新感覺
總結:從零開始FPGA,學習Verilog文法和VGA時序,碼核心代碼,調完善。後又花一上午改進完善并記錄這篇部落格。過程中最難受的是一開始,以C的思路去想卻不知道怎麼寫,更是無從下手,自己百度或者想辦法一步一步試,後來慢慢有一點點并行(或許和多線程有異曲同工之妙?(後續補充:FPGA是由硬體實作并行,而線程多是軟體并發,除非CPU多核多線程也能并行))的感覺(FPGA真奇妙)和完成了大作業。
換一種代碼換一種思路,挺舒服的~歡迎留言指出不足,項目GitHub位址:https://github.com/BlusLiu/Flappy-Bird