Altera SOPC FrameBuffer系統設計教程


 

Altera SOPC FrameBuffer系統設計教程

小梅哥編寫,未經授權,嚴禁轉載或用於任何商業用途

在嵌入式系統中,LCD屏作為最友好的人機交互方式,被大量的應用到了各個系統中。在基於ARM處理器的系統中,應用更是非常廣泛。FPGA作為廣義嵌入式系統的一員,自然也有很多時候需要來驅動顯示屏顯示一些內容,例如經常有需求要用FPGA來做液晶測試架,做顯示器驅動測試卡。很多學習了FPGA的朋友都知道,FPGA驅動VGA顯示器是比較輕松的,幾乎每個板卡商提供的資料中都提供了有諸如顯示彩條,顯示圖案,甚至顯示簡單的文字。但是,當希望顯示更加復雜的內容的時候,往往就很難辦到了。因為FPGA設計的是電路,是硬件,特點是頭腦簡單,四肢發達。而顯示復雜的內容,恰恰需要一個頭腦發達的控制器來執行這項任務。

在FPGA這些年的發展中,先后經歷了簡單邏輯擴展、復雜邏輯設計、SOPC系統和SOC系統,邏輯設計部分就不多說了,對於SOPC系統設計,Xilinx家有MicroBlaze軟核、Altera家有大名鼎鼎的NIOS II軟核處理器。而到了SOC時代,Xilinx家和Altera(現已被因特爾收購,成為Intel的可編程事業部)都推出了內嵌雙核ARM Cortex-A9的SOC芯片,Xilinx家著名的zynq7000系列,以及Altera花了大力氣在高校推廣的Cyclone V SOC。

雖然隨着技術的發展以及時間的推移,SOC的應用會越來越廣泛,但是作為我們一般學習和使用來說,SOPC技術也還是有其使用的價值的,畢竟內嵌硬核的SOC芯片,動輒幾百塊,而使用軟核的方案,只要40元不到的BOM成本就能完成一個系統設計,優勢還是很明顯的。本例,我們就帶領大家使用Altera Cyclone IV系列最低端的FPGA芯片EP4CE6/EP4CE10設計的能夠驅動640*480分辨率顯示屏動態顯示復雜內容的系統。例如顯示文字,顯示圖片

在之前我們講解過RGB顯示屏的驅動,RGB顯示屏驅動時序和VGA驅動時序幾乎完全一樣,只是不同的分辨率之間,其時序參數不同。說到這個驅動480*272分辨率顯示屏動態顯示復雜內容,相信很多朋友並不陌生,是的,在我們之前的SOPC公開課時候,就已經給大家講解過實現這種系統的一種方式,即使用了一片獨立的SRAM來作為顯示屏的顯存,顯示驅動模塊直接讀取SRAM中的數據並實時刷新顯示,而NIOS II CPU則只在需要刷新顯示內容的時候,才執行對SRAM的寫入操作。此種方式,需要一片獨立的SRAM作為顯存,以及一片SDRAM作為NIOS II的運行內存。除了SRAM的使用會增加系統的BOM成本外,SRAM也會占用掉FPGA芯片較多的管腳,導致必須得使用256腳BGA方案的芯片才能夠實現。因此,此種方案僅適合在對成本不太敏感高的場合使用。本節要介紹的方案,是對該系統改進優化得到的。

本節所描述的系統,僅需用到以下硬件資源

1. EP4CE6/EP4CE10型FPGA芯片一片(30元)

2. 16Mbit 或以上SDRAM芯片一片(3元,如華邦w9816g6)

3. 50M有源晶振一片(3元)

4. 4Mbit SPI FLASH芯片(1.5元,如W25Q80)

5. 3.3V、2.5V、1.2V LDO穩壓電源(AMS117系列,合計不到1元)

本節希望達到的目的:

在顯示屏上顯示一幀圖像,並在指定位置顯示字符內容

 

圖片1

 

上述框架是我們經過分析,要實現本系統方案所需的最小硬件系統,本節我們將基於芯航線的AC620學習套件來完成FPGA的系統搭建和NIOS II軟件設計。芯航線的AC620 FPGA開發板整體硬件配置高於上述分析,有助於我們進行原型驗證。

FPGA系統搭建

本系統主要在Qsys系統中完成,包含了以下IP核:

  • clk_0:輸入時鍾管理單元,系統默認添加
  • altpll_0:PLL鎖相環,對輸入時鍾進行分頻和倍頻,以得到兩路頻率為100MHz,相位相差180度的時鍾信號,分別提供給系統中邏輯電路工作(包含NIOS II軟核處理器)和SDRAM芯片使用。以及1路24M的時鍾信號,供640*480分辨率的VGA驅動電路使用。
  • NIOS II CPU:實現系統的控制以及顯示內容的處理。
  • SDRAM:NIOS II CPU運行內存和TFT顯示圖像幀緩存。
  • onchip_memory:片上存儲器,指定SGDMA要執行的數據傳輸,主要用作SGDMA的描述符存儲器。
  • lcd_sgdma:SGDMA IP,主要實現大量數據的高效搬運,支持流模式,效率比Avalon MM接口的DMA核高。
  • timing_adapter:時序匹配IP核,主要用於流數據在兩個不同模塊間進行傳遞時,對部分信號加上一定延遲,使得數據和標志信號能夠完全同步(說的簡單點兒,就是用寄存器打拍的方式對一些信號進行延遲,使數據和控制信號對齊)。
  • fifo:雙時鍾fifo,主要完成數據流的傳輸速度轉換。SGDMA輸出的數據流,是按照和存儲器相同的時鍾速度(本例中為100M)進行的,而在數據使用端,即VGA顯示部分,是按照9M的時鍾取用數據的,因此使用雙時鍾fifo,解決跨時鍾域的問題。
  • VGA_SINK:該IP為第三方開源IP,由友晶科技提供,主要完成Avalon ST流數據轉換成VGA行場時序並驅動VGA屏進行顯示。
  • EPCS:EPCS FLASH存儲器控制IP,使用該IP,使得EPCS芯片能夠存儲FPGA固件和NIOS II的程序。
  • jtag_uart0:調試串口,主要用來打印一些調試信息,在設計的前期調試中很有用

 

圖片2

 

接下來,將詳細講解本系統的搭建過程。

1、 altpll_0

為了保證整個系統能夠具有較高的性能,因此讓其運行在100MHz,所以需要使用PLL對外部晶振產生的50MHz輸入的時鍾信號進行倍頻,得到兩路頻率為100M,相位相差90度的時鍾信號。另外,還需要提供一路9MHz時鍾信號供480*272分辨率的TFT屏驅動使用。關於PLL的詳細添加和參數修改方式這里不再多說,僅提供設置結果:

inclk0:50M

不需要areset信號和locked信號

c0:9MHz

c1:100MHz,相位為0

c2:100MHz,相位為-180度

2、 nios ii cpu

所有設置使用默認值,等后續添加完sdram和EPCS和,將復位向量(Reset Vector)設置為epcs,將異常向量(Exception Vector)設置為sdram。

3、 sdram

數據位寬(Data Width)為16bit。

結構(Architecture)為一個片選,4個bank。

地址寬度(Address Width)Row地址為12、Column地址為9。

其他默認即可。

 

圖片3

 

4、 onchip_memory

片上緩存onchip_memory作為SGDMA的描述符存儲器,存儲SGDMA的數據傳輸描述內容。

類型為RAM;

數據位寬選擇32位;

存儲器總量選擇16384Bytes(用戶可以根據自己芯片的RAM容量適當增加或減小該值);

 

圖片4

 

5、 lcd_sgdma

選擇傳輸模式為存儲器到數據流模式(Memory To Streram);

數據寬度(Data width)為16bit,該位寬與存儲器(SDRAM)的Avalon MM總線寬度一致。

其他按照默認即可,如下圖所示。

 

圖片5

 

6、 timing_adapter

時序匹配IP核,主要用於流數據在兩個不同模塊間進行傳遞時,對部分信號加上一定延遲,使得數據和標志信號能夠完全同步(說的簡單點兒,就是用寄存器打拍的方式對一些信號進行延遲,使數據和控制信號對齊)。

如下圖所示設置,所有勾選項都勾選上,輸入Ready Latency設置為0,輸出Ready Latency設置為1,每個數據總共有兩個symbols,每個symbol含8位數據。(根據RGB數據流的實際意義,這里應該是每個數據有3個symbols,但是本設計使用的是16位色RGB565模式,即RGB總共才16位,所以在搭建系統的時候,假設只有2個symbols,只是在最終數據輸出到VGA的時候,將16bit數據拆分成RGB565,分別送給VGA的R、G、B色通道)

 

圖片6

 

以上各個模塊,都是工作在100MHz的時鍾頻率下。主要完成將需要顯示的數據從SDRAM中讀取出來。而讀取出來的數據最終是需要送到TFT顯示屏上去顯示的,TFT顯示部分,對於本系統,數據傳輸速率為9M,因此就需要實現數據從100M時鍾域到9M時鍾域的跨時鍾域傳輸。而實現數據流跨時鍾域傳輸,較好的方式是使用雙時鍾fifo。因此,接下來就需要添加一個雙時鍾fifo,先將100M時鍾域的數據緩存起來,然后再等待9M時鍾域的讀取邏輯來讀取。

7、 fifo

設置fifo的深度為512字節,每個數據含2個symbols,每個symbols由8位數據組成,即整個數據寬度為16位。時鍾設置為雙時鍾模式(Dual Clock Mode),輸入和輸出都使用Avalon ST接口,使能數據包(Enable Packet Data)。這里順帶說一下,Cyclone IV E器件中的嵌入式塊RAM為M9K存儲器,每個器件里都有若干個M9K存儲器。所謂M9K存儲器,就是一個塊存儲器中有9Kb個存儲位,每個M9K存儲器可以被配置為以下模式:

  • 8192 × 1
  • 4096 × 2
  • 2048 × 4
  • 1024 × 8
  • 1024 × 9
  • 512 × 16
  • 512 × 18
  • 256 × 32
  • 256 × 36

由於本設計中,數據是16位的,因此,如果要想僅使用一個M9K存儲器就實現本Fifo,存儲深度可在1到512之間任選。假如我們選擇存儲深度為128,那么就只占用1/4個M9K的存儲容量,但是,剩下的3/4的存儲容量也依舊無法單獨使用,在該設計中會永遠的被浪費掉,因此,不如將存儲深度直接設置為512,還能保證緩存足夠大,避免意外丟失數據。

 

圖片7

8、 VGA_SINK

VGA_SINK模塊是從友晶提供的設計中修改得到的,該IP原本僅支持24位色模式,而本系統,由於SDRAM為16位寬度,如果強行使用24位色模式,則必須得從SDRAM中讀取兩個數據才能拼接成一個像素,導致SDRAM的負載過大,從而使得系統帶寬遇到瓶頸,無法正常工作,因此對該IP進行了修改,即降低為16bit的RGB565模式。

(根據RGB數據流的實際意義,這里應該是每個數據有3個symbols,但是本設計使用的是16位色RGB565模式,即RGB總共才16位,所以在搭建系統的時候,假設只有2個symbols,只是在VGA_SINK模塊內部,在最終數據輸出到VGA的時候,進行了修改,將16bit數據拆分成RGB565,分別送給VGA的R、G、B色通道)。這里參數分別如下設置:

H_DISP(行顯示有效像素):640

H_FPORCH:16時鍾周期

H_SYNC:96時鍾周期

H_BPROCH:48時鍾周期

V_DISP(行顯示有效像素):480

V_FPORCH:1時鍾周期

V_SYNC:2時鍾周期

V_BPROCH:33時鍾周期

 

圖片8

 

這些參數都是從VGA標准中查到的。

9、EPCS

EPCS的添加沒有什么需要特別注意的,一切按照默認設置即可。

至此,我們已經完成了整個顯示系統的組件添加工作,接下來就該進行組件間的總線連接了。

在總線連接之前,必須要先給大家講解一下本系統的數據流向,因為本系統和我們之前在SOPC公開課時候講過的系統架構還不太一樣,本系統主要引入了Avalon ST總線。在之前公開課時候講過的各種例子,都是基於Avalon MM總線的,所有外設IP都通過Avalon MM總線連接到NIOS II CPU上,因此總線連接是非常簡單的。但是Avalon MM總線的數據搬運能力是比較弱的,無法支持高速大量的數據傳輸。而在基於Avalon ST總線的數據流中,NIOS II CPU實質相當於被旁路了,只起到了狀態管理的作用,所有的數據流轉都是模塊間通過Avalon ST總線進行交互的,不需要NIOS II去執行搬運工作,因此數據的搬運效率大大提高。

本節暫不對Avalon ST總線進行過多的講解,總之,我們可以把Avalon ST總線想象成一個水管,水從水管的一頭可以很容易且快速的流到另一頭,不需要第三方用水瓢一瓢一瓢的去舀過去。

Framebuffer系統數據流

在整個FrameBuffer系統中,SGDMA模塊是最核心的部分,他實現了對SDRAM存儲器的直接讀取操作,並將讀到的數據最終以數據流的形式輸出。SGDMA的使用相當的廣泛,不僅是在本系統中,在Altera很多官方設計中也都使用了SGDMA IP核,比如PCIE 示例、千兆以太網示例、VIP示例等,關於SGDMA的詳細介紹,可以參看SGDMA的用戶手冊,我們也希望能盡快推出SGDMA的專題講解。這里僅對SGDMA的端口和功能進行下簡述。

本系統中SGDMA被配置為存儲映射模式到數據流模式,即數據的源頭采用地址映射的方式組織,而數據的接收方則使用數據流的方式進行接收。在此種模式下,SGDMA總共有5個端口,分別為Avalon MM Slave接口的控制總線,Avalon MM Master接口源數據讀取總線、Avalon MM Master接口的描述符寫入總線和Avalon MM Master接口的描述符讀取總線,以及最終的Avalon ST Source數據輸出總線。

 

圖片9

 

csr:Avalon MM Slave接口的控制總線,與NIOS II CPU進行連接,NIOS II CPU通過該接口讀寫SGDMA內部的控制和狀態寄存器,以實現DMA的傳輸控制。這也是整個數據流傳輸唯一需要NIOS II參與的地方。NIOS II實際就扮演了個老板的角色,安排SGDMA去做什么,SGDMA接到任務后就下去干搬運的苦力活兒了,而NIOS II這個老板,則可以坐在辦公室喝喝咖啡,看看報紙,等着SGDMA把苦力干完后回來報告(中斷)或者隔一段時間打個電話問問工作狀態和進度(查詢)。

m_read:Avalon MM Master接口的源數據讀取總線,該接口實現對Avalon MM總線上掛載的存儲需要傳輸的數據源存儲器的讀取,例如SDRAM、DDR2 SDRAM。從這里,大家也可以看到,在Qsys搭建的系統中,不是只有NIOS II能夠去讀取SDRAM、DDR2等存儲器,實際上,只要是Avalon MM Master接口的IP,都可以去讀取這些存儲器,我們甚至可以自己編寫一個Avalon MM Master接口的邏輯,來取代NIOS II CPU完成各種IP核的讀寫操作。

descriptor_write:描述符寫端口,SGDMA實現數據傳輸需要有一個描述符,SGDMA的所有傳輸都是受描述符控制的,通過描述符存儲SGDMA的實時傳輸狀態。這里專門使用一個片上存儲器來存儲SGDMA的描述符,能夠節省FPGA資源。否則,如果使用FPGA資源來實現描述符,將會帶來非常大的資源消耗。

descriptor_read:描述符讀端口,SGDMA使用此端口讀取描述符,以獲得傳輸信息。

out:數據流輸出端口,從SDRAM或DDR2中讀取到的數據會經由此端口流出,以提供給數據的使用端使用。

對於我們分析數據流來說,可以暫時忽略csr、descriptor_write和descriptor_read端口,因為這些端口並不處於數據通路上,真正處於數據通路上的,是m_read(數據流入)和out(數據流出)端口。下圖為本系統的數據流圖。

 

圖片10

 

NIOS II CPU使用SDRAM作為運行存儲器,存儲程序和數據,同時,SGDMA從SDRAM中實時讀取需要顯示到VGA屏上的數據。NIOS II 和 SGDMA之間對SDRAM的使用仲裁由Avalon MM總線的仲裁機制自動處理。

通過以上介紹,了解了系統的數據流向,就可以首先完成數據流總線的連接了。每個st 流接口的模塊都有2個流端口,一個是流輸入端口(in: Avalon Streaming Sink),另一個是流輸出端口(out: Avalon Streaming Source)。連接時,只需將上一級模塊的流out連接到下一級模塊的流in端口,即可實現數據流的自動銜接。在本例中每個模塊端口連接如下表所示:

 

圖片11

 

整體連接完成后,結果如下圖所示:

 

圖片12

 

當然,連接完成總線后,不要忘了連接復位網絡、導出需要引出到系統頂層的信號以及中斷網絡。

  • 修改NIOS II的復位向量為EPCS、異常向量為SDRAM。
  • 自動分配基地址。
  • 到HDL Eaxmple欄復制例化模版。
  • 保存並開始generate。

至此,整個系統就搭建完成了,下一步就是將系統加入到Quartus II工程中。首先是將mysystem.qsys文件添加到工程,然后完成工程頂層文件的編寫。這些最基本的操作,這里就不在贅述,以下為工程頂層代碼:

module Framebuffer_VGA(
        input  wire        clk_50m,          
        input  wire        reset_n,          
        output wire        vga_clk,          
        output wire        vga_de,           
        output wire [7:0]  vga_r,            
        output wire [7:0]  vga_g,            
        output wire [7:0]  vga_b,            
        output wire        vga_hs,           
        output wire        vga_vs,           
        output wire        vga_bl,           
        output wire        sdram_clk,        
        output wire [11:0] sdram_addr,       
        output wire [1:0]  sdram_ba,         
        output wire        sdram_cas_n,      
        output wire        sdram_cke,        
        output wire        sdram_cs_n,       
        inout  wire [15:0] sdram_dq,         
        output wire [1:0]  sdram_dqm,        
        output wire        sdram_ras_n,      
        output wire        sdram_we_n,       
        output wire        epcs_dclk,        
        output wire        epcs_sce,         
        output wire        epcs_sdo,         
        input  wire        epcs_data0        
);   

    assign vga_bl = 1;
    
    wire vga_clk_r;
    
    assign vga_clk = ~vga_clk_r;    //對VGA時鍾反相,以保證數據中心對齊
    
    mysystem u0 (
        .clk_50m_clk                       (clk_50m),              
        .reset_50m_reset_n                 (reset_n),              
        .vga_clk                           (vga_clk_r),            
        .vga_de                            (vga_de),               
        .vga_r                             (vga_r),                
        .vga_g                             (vga_g),                
        .vga_b                             (vga_b),                
        .vga_hs                            (vga_hs),               
        .vga_vs                            (vga_vs),               
        .altpll_0_phasedone_conduit_export (),
        .altpll_0_locked_conduit_export    (),    
        .altpll_0_areset_conduit_export    (),    
        .epcs_dclk                         (epcs_dclk),            
        .epcs_sce                          (epcs_sce),             
        .epcs_sdo                          (epcs_sdo),             
        .epcs_data0                        (epcs_data0),           
        .sdram_clk_clk                     (sdram_clk),            
        .sdram_addr                        (sdram_addr),           
        .sdram_ba                          (sdram_ba),             
        .sdram_cas_n                       (sdram_cas_n),          
        .sdram_cke                         (sdram_cke),            
        .sdram_cs_n                        (sdram_cs_n),           
        .sdram_dq                          (sdram_dq),             
        .sdram_dqm                         (sdram_dqm),            
        .sdram_ras_n                       (sdram_ras_n),          
        .sdram_we_n                        (sdram_we_n) 
    );

endmodule

 

 


然后分配引腳,本系統在AC620開發板上的引腳分配請參照AC620開發板引腳分配表。
 

分配引腳后,記得在quartus ii軟件中設置所有雙功能引腳為用戶模式。

 

圖片14

 

編譯工程得到sof編程文件。

至此,整個FrameBuffer系統硬件設計就完成了,下一步,我們就可以編寫相應的驅動程序和應用程序,在目標板上運行了。

創建Eclipse工程

打開NIOS II EDS軟件,導入我們提供的VGA和VGA_bsp兩個工程。修改setting.bsp文件中第7行和第9行兩個位置為自己電腦上對應的位置。關閉文件,然后build工程。如果編譯失敗,提示無權限,請先關閉所有文件,然后選擇整個文件夾右鍵獲取管理員權限。

 

圖片15

 

以下為main函數內容

 

#include "stdio.h"
#include "stdlib.h"
#include "io.h"
#include "sys/alt_alarm.h"
#include "altera_avalon_sgdma.h"
#include "altera_avalon_sgdma_descriptor.h"
#include "altera_avalon_sgdma_regs.h"
#include "alt_types.h"
#include "alt_video_display.h"
#include "unistd.h"
#include "pic1.h"
#include "pic2.h"
#include  "system.h"


#define  WIDTH 640
#define  HEIGHT 480
#define  NUM_FRAME 1


int main() {
    unsigned int d = 0;

    ////Initial LCD Display
    alt_video_display* display_global;
//  printf("Initializing LCD display controller\n  ");
    display_global = alt_video_display_init(LCD_SGDMA_NAME, // Name of video controller
            WIDTH, // Width of display
            HEIGHT, // Height of display
            16, // Color depth (32 or 16)
            SDRAM_BASE + SDRAM_SPAN / 2, // Where we want our frame buffers
            ONCHIP_MEMORY_BASE, // Where we want our descriptors
            NUM_FRAME);
//  if (display_global)
//      printf(" - LCD Initialization OK\n");
//  else
//      printf(" - LCD FAILED\n");
    alt_video_display_clear_screen(display_global, 0xff);
    show_pic(display_global, pic1);
    usleep(1000000);
    while(1){
    }
}

 

編譯完成后下載sof,並在nios ii eds中執行run。連接VGA顯示器,就能在屏幕上顯示一副美女圖,如下圖所示:

如有其它問題,請參見小梅哥博客中關於NIOS II的相關內容。如“【2017最新】解決NIOS II工程移動在磁盤上位置后project無法編譯問題.pdf”和“NIOS II 開發注意點總結.pdf

本文檔對應設計工程名為:AC620_Framebuffer_VGA,請到AC620開發板網盤/光盤資料中獲取。

圖片17圖片18


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM