1 項目背景 (至簡設計交流群:544453837) 1.1 FPGA存儲器 目前大多數FPGA都有內嵌的塊RAM(Block RAM),可以將其靈活地配置成單端口RAM(DPRAM,Single Port RAM)、雙端口RAM(DPRAM,Double Ports RAM)、偽雙端口RAM(PseudoDPRAM)、CAM(Content AddressableMemory)、FIFO等常用存儲結構。FPGA中其實并沒有專用的ROM硬件資源,實現ROM的思路是對RAM賦予初值,并保持該初值。 Altera 的器件內部提供了各種存儲器模塊(RAM、ROM 或雙口 RAM),可以在設計中使用 MegaWizardPlug-In Manager,執行【Tools】|【MegaWizard Plug-InManager】菜單命令來創建所需要 的存儲器模塊。也可以使用 Altera 提供的宏功能模塊 LPM_ROM 來創建存儲器模塊。每個 ROM 模塊有 CLOCK(時鐘)、address(地址)這兩個輸入信號和一個 q(值)輸出信號。 ROM 在每個時鐘上升沿取出由地址信號所指定的存儲單元中的值并輸出。ROM 內的值通過加載 MIF (Memory Initialization File,存儲器初始化文件)文件來實現。 當在設計中使用了器件內部的存儲器模塊時,需要對存儲器模塊進行初始化。在Quartus 中, 可以使用兩種格式的存儲器初始化文件:Intel Hex 格式(.hex)或Altera 存儲器初始化格式(.mif)的文件。 MIF 文件是 Altera 存儲器類器件初始化的專用文件格式,文件內容為地址與值的對應表,規定了 存儲器單元的初始值。 如果將要存儲于 ROM 中的內容比較少或者很有規律,可以執行【File】|【New…】菜單命令,創 建 MIF 文件并編輯其內容。 如果已經有 BMP 格式的圖片,則可以使用我們提供的 BmpToMif 這個軟件,從現有的 BMP 格式 圖片生成 MIF 文件。其使用非常簡單,注意要適當調整原圖片的大小,這可以通過各種圖形編輯軟件修改,如 Windows 自帶的畫圖程序、Photoshop 等。 BmpToMif 軟件的功能有: 將 bmp 圖片轉為 mif 文件:將黑白圖片轉換為單色 mif 文件;將彩色圖片轉換為三色 mif 文件。 將二進制文件轉為 mif 文件,如將中英文點陣字庫轉換為 mif 文件。 1.2 圖片變成MIF文件的方法 我們需要用到Img2Lcd軟件,此軟件可自行下載,無需安裝即可使用。 打開軟件后,點擊右上角“打開”按鈕,選擇需要轉化的圖片,在右側輸出數據類型下選擇“c語言數組”,掃描模式為“水平掃描”,輸出灰度“256色”,最大寬度和高度應大于圖片的大小,由于我們示例的圖片為120*60,最大寬度和高度我們選擇240*240。其他選項可以不用管。 之后點擊“保存”,文件名為“1.c”,在點擊下方保存。 用gvim打開生成的文件,箭頭處為數據個數,由于圖片是120*60的,輸出灰度為256色,所以就有7200個數據。 第一行是不需要的,先刪除掉,然后進入命令模式,輸入“/,”,然后回車,就可以選中所有的逗號。 將光標置于第一個字符,在命令模式下一次輸入“q、a”,調用gvim的錄制功能,然后輸入“n”跳轉到第一個逗號,進入編輯模式,將光標置于第一個逗號的右側,然后回車,接著摁4下退格鍵刪除多余的空格,在進入命令模式,輸入“q”結束錄制。 在命令模式下輸入“7198@a”,表示重復之前的錄制7198次,然后回車。 然后我們要刪除多余的間隔,返回到第一個字符,在命令模式下輸入“qa”進入錄制,然后向下移動16格,剛好到沒有數據的一行,輸入“dd”進行刪除,然后“q”退出錄制。在命令模式下輸入“954@a”,即可完成刪除。 刪除掉中間的OX 在命令模式下輸入“:%s/0X/: /”回車將“0X”替換為冒號,輸入“:%s/,/; /”回車將“,”替換為“;”。 打開一個空白gvim,輸入1,進入命令模式選中,輸入“yy7199p”將1復制7199份,將光標至于第一個1處,進入命令模式,輸入“:leti=1|g/1/s//\=i/|let i=i+1”讓其遞增。 選中1~7199,然后Ctrl+Q,進行列操作,然后Ctrl+c復制,然后回到1.c文件中,命令模式下,將光標置于第二行最前,然后Ctrl+v粘貼。 將第一個數據的地址標為0,然后在最上方和結尾添加如下圖所示內容。 13、然后將“1.c”文件保存為“1.mif”格式的。 2 設計目標 通過VGA連接線,將顯示器和教學板的VGA接口相連。連接示意圖如下。 然后FPGA產生640*480分辨率(使用上表中的第一種分辨率),讓顯示器產生顯示一幅圖像。提示:顯示器一般都會自適應功能,無須設置就能識別不同分辨率的圖像。 圖像的內容是:在屏幕的中央顯示一個明德揚的LOGO。明德揚LOGO的大小是120*60像素。除了圖片之外的顯示區域,則顯示白色。上板效果圖如下圖所示(顯示器不同,顯示效果也會有差別,請注意)。 rom1是一個寬度為16位,深度為8192的ROM,該ROM保存了明德揚的LOGO圖片,其排列方式如下。 該ROM按從左往右,由上往下的順序保存了LOGO圖像每個像素的值。圖中的(y,x)表示的是第y行第x列的像素值。例如,地址0保存的是第1行第1列的像素值,地址1保存的是第1行第2列的像素值,地址119保存的是第1行第120列的像素值。接下來,地址120保存的是第2行第1列的像素值,以此類推,地址6599保存的是第55行120列的像素值。而大于6599的地址,保存的值是0,沒有意義的數字。 ROM的每一個像素,按RGB565的方式保存,也就是[15:11]表示紅基色,[10:5]表示綠基色,[4:0]表示藍基色。 3 設計實現3.1 頂層接口 新建目錄:D:\mdy_book\picture_new_borad。在該目錄中,新建一個名為picture_new_borad.v的文件,并用GVIM打開,開始編寫代碼。 我們要實現的功能,概括起來就是FPGA產生VGA時序,即控制VGA_R4~R0、VGA_G5~G0、VGA_B4~B0、VGA_HSYNC和VGA_VSYNC,讓顯示器顯示紅色。其中,VGA_HSYNC和VGA_VSYNC,FPGA可根據時序產生高低電平。而顏色數據,由于是固定的紅色,FPGA也能自己產生,不需要外部輸入圖像的數據。那么我們的FPGA工程,可以定義輸出信號hys表示行同步,用輸出信號vys表示場同步,定義一個16位的信號lcd_rgb,其中lcd_rgb[15:11]表示VGA_R4~0,、lcd_rgb[10:5]表示VGA_G5~0,、lcd_rgb[4:0]表示VGA_B4~0。 我們還需要時鐘信號和復位信號來進行工程控制。 綜上所述,我們這個工程需要五個信號,時鐘clk,復位rst_n,場同步信號vys、行同步信號hys和RGB輸出信號lcd_rgb。 將module的名稱定義為picture_new_borad。并且我們已經知道該模塊有五個信號:clk、rst_n、lcd_hs、lcd_vs和lcd_rgb。為此,代碼如下: 其中clk、rst_n是輸入信號,lcd_hs、lcd_vs和lcd_rgb是輸出信號,其中clk、rst_n、lcd_hs、lcd_vs的值是0或者1,一根線即可,lcd_rgb為16位位寬的,根據這些信息,我們補充輸入輸出端口定義。代碼如下: 3.2 架構設計 需要注意的是,輸入進來的時鐘clk是50MHz,而從分辨率參數表可知道,行單位的基準時鐘是25MHz。為此我們需要根據50MHz來產生一個25 MHz的時鐘,然后再用于產生VGA時序。 為了得到這個25M時鐘,我們需要一個PLL。PLL可以認為是FPGA內的一個硬核,它的功能是根據輸入的時鐘,產生一個或多個倍頻和分頻后的輸出時鐘,同時可以調整這些輸出時鐘的相位、占空比等。 例如,輸入進來是50M時鐘,如果我需要一個100M時鐘,那么從邏輯上、代碼上是不可能產生的,我們就必須用到PLL來產生了。 整個工程的結構圖如下。 PLL的生成方式過程,請看本案例的綜合工程和上板一節的內容。 3.3VGA驅動模塊設計3.3.1接口信號 在目錄:D:\mdy_book\ picture_new_borad中,建立一個rectangle.v文件,并用GVIM打開,開始編寫代碼。 我們新建一個GVIM文件,并且將文件保存為vga_driver.v。 我們先分析功能。要控制顯示器,讓其產生紅色,也就是讓FPGA控制VGA_R0~4、VGA_G0~5、VGA_B0~4、VGA_VSYNC和VGA_HSYNC信號。那么VGA驅動模塊,可以定義輸出信號hys表示行同步,用輸出信號vys表示場同步,定義一個16位的信號lcd_rgb,其中lcd_rgb[15:11]表示VGA_R4~0,、lcd_rgb[10:5]表示VGA_G5~0,、lcd_rgb[4:0]表示VGA_B4~0。 同時該模塊的工作時鐘為25M,同時需要一個復位信號。 綜上所述,我們這個模塊需要五個信號,25M時鐘clk,復位rst_n,場同步信號vys、行同步信號hys和RGB輸出信號lcd_rgb。 將module的名稱定義為vga_driver。并且我們已經知道該模塊有五個信號:clk、rst_n、hys、vys和lcd_rgb。為此,代碼如下: 其中clk、rst_n是輸入信號,hys、vys和lcd_rgb是輸出信號,其中clk、rst_n、hys、vys的值是0或者1,一根線即可,lcd_rgb為16位位寬的,根據這些信息,我們補充輸入輸出端口定義。代碼如下: 3.3.2 信號設計 我們先設計場同步信號hys,VGA時序中的場同步信號,其時序圖如下: hys就是一個周期性地高低變化的脈沖。我們使用的是下表中的第一種分辨率,也就是同步脈沖a的時間是96個時鐘周期,而顯示后沿b是48個時鐘周期,顯示時序c是640個時鐘周期,顯示前沿是16個時鐘周期,一共是800個時鐘周期。 將時間信號填入圖中,更新后的時序圖如下: 很顯然,我們需要1個計數器來產生這個時序,我們將該計數器命名為h_cnt。由于hys是不停地產生的,那么h_cnt就是不停地計數,每個時鐘都要計數器,所以認為該計數器的加1條件為“1”,可寫成:assign add_h_cnt = 1。從上圖可知,該計數器的周期是800。綜上所述,該計數器的代碼如下: 有了計數器h_cnt,那么hys信號就有了對齊的對象。從時序圖可以發現, hys有兩個變化點,一個是h_cnt數到96個時,由0變1;另一個是當h_cnt數到800個時,由1變0。所以,場同步信號的代碼如下: 接下來設計vys信號。該信號的時序圖如下所示。 vys就是一個周期性地高低變化的脈沖。我們使用的是表中的第一種分辨率,查詢表可知,同步脈沖a的時間是2行的時間,而顯示后沿b是33行,顯示時序c是480行,顯示前沿是10行,一共是525行。其中,一“行”結束,也就是h_cnt數完了。 將時間信號填入圖中,更新后的時序圖如下: 很顯然,我們還需要1個計數器來產生這個時序,我們將該計數器命名為v_cnt。該計數器是用來數有多少行的,所以加1條件就是一行結束,即end_h_cnt,可寫成:assignadd_v_cnt = end_h_cnt。從上圖可知,該計數器的周期是525。綜上所述,該計數器的代碼如下: 有了計數器v_cnt,那么vys信號就有了對齊的對象。從時序圖可以發現, vys有兩個變化點,一個是v_cnt數到2個時,由0變1;另一個是當h_cnt數到525個時,由1變0。所以,場同步信號的代碼如下: 最后我們還有一個信號需要設計,那就是lcd_rgb信號。 我們在顯示器中一共要分成兩種方式顯示。如上圖中,以屏幕中點為中心,左右60列、上27個行、下28行顯示的是圖像數據;而在其他區域直接顯示白色,也就是lcd_rgb等于16’b11111_111111_11111。還要注意的是,在非顯示區域,lcd_rgb的值要為0,才能正確顯示。我們現在要仔細區分,在什么時候分別輸出上面的值。 |