(原創) 如何設計一個數位相框? (SOC) (Quartus II) (SOPC Builder) (Nios II) (TRDB-LTM) (DE2-70)

Abstract
在上一篇blog,我們學會了將wav檔放在SD卡上,實做出一個SD卡wav player,第一次體會出軟硬體設計的威力。由於FAT16格式的讀取,必須牽涉到軟體的動作,所以必須引入Nios II與Avalon Bus,不能再靠純硬體的方式設計。這次我們將圖片放在SD卡上,在DE2-70實做出一個簡易的數位相框。

Introduction
使用環境:Quartus II 8.1 + Nios II EDS 8.1 + DE2-70 (Cyclone II EP2C70F896C6N) + TRDB-LTM

這4篇原本是設計在一起的lab,適合初學者從0開始慢慢熟悉Quartus II、SOPC Builder、Nios II EDS、Avalon Bus Slave、Avalon Bus Master。

(原創) 如何自己用SOPC Builder建立一個能在DE2-70上跑μC/OS-II的Nios II系統? (SOC) (Quartus II) (SOPC Builder) (Nios II) (μC/OS-II) (DE2-70)
(原創) 如何設計一個七段顯示器Controller? (SOC) (Quartus II) (SOPC Builder) (Nios II) (DE2-70)
(原創) 如何設計一個SD卡Wav Player? (SOC) (Quartus II) (SOPC Builder) (Nios II) (DE2-70)
(原創) 如何設計一個數位相框? (SOC) (Quartus II) (SOPC Builder) (Nios II) (DE2-70)

面臨的挑戰
要在DE2-70設計一個數位相框,面臨了幾個挑戰:
1.因為圖片是放在SD卡上,用的是FAT16格式,無論開檔、讀檔都必須透過軟體,關於SD卡使用與FAT16的讀取,在Lab 3我們已經學會,所以已經解決。

2.Altera並沒有提供LTM (touch panel) controller,所以我們必須依照Homework 2的model自己開發一個LTM Controller,這也是本次Lab的重點。

『在DE2-70 CD的範例中,是否有接近的範例可以參考而加以修改呢?』

DE2_70_D5M_LTM範例中,提供了一個相近的範例,不過他的input是500萬像素cmos,而不是SD卡。

album00

SDRAM之前屬於CMOS部分,與本Lab無關,可以不用理他,SDRAM為frame buffer,影像就是存在這裡。LTM Controller與Data Request就是LCD Timing Generator部分,這部分可以保留下來繼續用。

由於讀取SD卡與FAT16需要軟體配合,勢必加上Nios II CPU與Avalon Bus,此時這裡的Multi-Port SDRAM Controller與LTM Controller就很尷尬了,因為他是個純硬體的Controller,無法掛在Avalon Bus上,所以必須將這2個controller換掉。

Altera有提供SDRAM Controller可用,在Lab1 ~ Lab3我們都使用過Altera SOPC版的SDRAM Controller,但Altera並沒有提供SOPC版的LTM Controller,這就得靠我們自己寫了。最後架構如下圖所示:

系統架構圖

album01

 

Master Interface
Slave Interface已經在Lab 2 七段顯示器Controller練習過,主要提供C語言透過Nios II對controller設定register。LTM Controller雖也提供slave interface,但主要的影像傳輸使用的是Master Interface

為什麼要使用Master Interface呢?
由於影像放在SDRAM,當成LTM的frame buffer,LTM必須以33MHz不斷對SDRAM要資料,對LTM做掃描顯示,在Lab 2我曾經說過,master與slave的差別在於Master能夠自己發起傳輸,Slave則必須由Master控制而被動的發起傳輸,若影像傳輸使用Slave Interface,則Nios II CPU必須不斷的介入,命令資料從SDRAM搬到LTM,如此Nios II CPU將非常忙碌,所以比較理想的方式是LTM Controller支援Master Interface,這樣LTM Controller就能主動地讀取SDRAM,不需Nios II CPU介入

album02

硬體部分
Step 1:
使用lab4_files.7z內的DE2_70_LTM_NIOS project

將DE2_70_LTM_NIOS複製到c:\DE2-70下

Step 2:
使用Lab4_files.zip內的LTM_Controller

將LTM_Controller複製到c:\DE2-70\DE2_70_LTM_NIOS\ip目錄下

album03

Step 3:
開發LTM_Controller.v

57行,請補上FIFODEPTH參數,這是設定FIFO的大小
59行,請補上FIFOFULLVALUE參數,這是設定當FIFO還剩下多少時,就認為FIFO已經滿了。
109行,請補上fifo_full的條件。

最後完整程式如下所示:
LTM_Controller.v / Verlilog

1 /*
2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3
4 Filename : LTM_Controller.v
5 Compiler : Quartus II 8.1
6 Description : LTM Controller for Avalon Bus
7 Release : 12/18/2008 1.0
8  */
9
10 `default_nettype none
11
12  module LTM_Controller (
13 // Avalon clock interface siganals
14   input csi_clockreset_clk, // avalon-mm clk
15   input csi_clockreset_reset_n, // avalon-mm reset_n
16 // Signals for Avalon-MM master port
17   output [DATAWIDTH-1:0] avm_m1_address, // avalon-mm master address
18   output [BYTEENABLEWIDTH-1:0] avm_m1_byteenable, // avalon-mm master byteenable
19   output avm_m1_read, // avalon-mm master read
20   input avm_m1_readdatavalid, // avalon-mm master readdatavalid
21   input [DATAWIDTH-1:0] avm_m1_readdata, // avalon-mm master readdata
22 input avm_m1_waitrequest, // avalon-mm master waitrequest
23 // Signals for Avalon-MM slave port
24 input [ADDRWIDTH-1:0] avs_s1_address, // avalon-mm slave address
25 input [BYTEENABLEWIDTH-1:0] avs_s1_byteenable, // avalon-mm slave byteenable
26 input avs_s1_write, // avalon-mm slave write
27 input [DATAWIDTH-1:0] avs_s1_writedata, // avalon-mm slave writedata
28 input avs_s1_read, // avalon-mm slave read
29 output [DATAWIDTH-1:0] avs_s1_readdata, // avalon-mm slave readdata
30 // LTM couduit input
31 input coe_ltm_export_iCLK_50, // 50MHz
32 input coe_ltm_export_iRST0, // reset delay 0
33 input coe_ltm_export_iRST2, // reset delay 2
34 // LTM couduit output
35 output coe_ltm_export_oLTM_CLK, // ltm clk
36 output [LTM_DATAWIDTH-1:0] coe_ltm_export_oR, // ltm R
37 output [LTM_DATAWIDTH-1:0] coe_ltm_export_oG, // ltm G
38 output [LTM_DATAWIDTH-1:0] coe_ltm_export_oB, // ltm B
39 output coe_ltm_export_oHD, // ltm h.sync
40 output coe_ltm_export_oVD, // ltm v.sync
41 output coe_ltm_export_oDEN, // ltm data enable
42 output coe_ltm_export_oSCLK, // ltm I2S clk
43 inout coe_ltm_export_ioSDAT, // ltm I2S data
44 output coe_ltm_export_oSCEN, // ltm I2S clk enable
45 output coe_ltm_export_oFIFO_FULL, // for debug use only (ltm fifo empty)
46 output coe_ltm_export_oFIFO_EMPTY // for debug use only (ltm fifo full)
47 );
48
49 // LTM parameter
50 parameter LTM_DATAWIDTH = 8; // ltm data width
51
52 // Avalon-MM parameter
53 parameter DATAWIDTH = 32; // width of databus
54 parameter BYTEENABLEWIDTH = 4; // width of byteenable
55 parameter ADDRESSBASE = 32'h0080_0000; // SDRAM frame buffer start address
56 parameter LENGTH = 800 * 480; // LTM width : 800, height = 480
57 parameter FIFODEPTH = 8192; // FIFO depth : number of words
58 parameter FIFODEPTHLOG2 = 13; // FIFO width of depth
59 parameter FIFOFULLVALUE = 195; // FIFO full value
60 parameter ADDRWIDTH = 3; // avalon-mm slave address width
61
62 localparam FIFOALMOSTFULL = FIFODEPTH - FIFOFULLVALUE; // FIFO almost full value
63
64 // LTM wire
65 wire ltm_nclk; // ltm clk
66 wire read; // sdram read request
67
68 // fifo wire
69 wire fifo_wrclk; // fifo write clk
70 wire fifo_aclr; // fifo asynchronous clk
71 wire fifo_wrreq; // fifo write request
72 wire [DATAWIDTH-1:0] fifo_wrdata; // fifo write data
73 wire fifo_rdclk; // fifo read clk
74 wire fifo_rereq; // fifo read request
75 wire [DATAWIDTH-1:0] fifo_rddata; // fifo read data
76 wire fifo_empty; // fifo empty
77 wire fifo_full; // fifo full
78 wire [FIFODEPTHLOG2-1:0] fifo_used; // fifo data used
79
80 // internal logic
81 reg [DATAWIDTH-1:0] address; // address of sdram
82 wire [DATAWIDTH-1:0] end_address; // end address of sdram
83 wire is_address_sload; // is sload of address?
84 wire is_increment_address; // is increment address of sdram?
85 reg [FIFODEPTHLOG2-1:0] reads_pending; // pending reads
86
87 // master output
88 assign avm_m1_address = address; // avalon-mm master address
89 assign avm_m1_byteenable = 4'b1111; // avalon-mm master byteenable (32 bit)
90 assign avm_m1_read = (!fifo_full); // avalon-mm master read
91
92 // export output
93 assign coe_ltm_export_oLTM_CLK = ltm_nclk; // ltm clk
94 assign coe_ltm_export_oFIFO_FULL = fifo_full; // for debug use only (ltm fifo full)
95 assign coe_ltm_export_oFIFO_EMPTY = fifo_empty; // for debug use only (ltm fifo empty)
96
97 // fifo input
98 assign fifo_wrclk = csi_clockreset_clk; // fifo write clk
99 assign fifo_aclr = ~csi_clockreset_reset_n; // fifo asynchronous clear
100 assign fifo_wrreq = avm_m1_readdatavalid; // fifo write request
101 assign fifo_wrdata = avm_m1_readdata; // fifo write data
102 assign fifo_rdclk = ltm_nclk; // fifo read clk
103 assign fifo_rereq = read; // fifo read request
104
105 // internal logic
106 assign end_address = ADDRESSBASE + LENGTH * BYTEENABLEWIDTH; // end address of sdram
107 assign is_address_sload = (address == end_address); // is sload of address?
108 assign is_increment_address = (!fifo_full) && (!avm_m1_waitrequest); // is increment address of sdram?
109 assign fifo_full = (fifo_used + reads_pending) > (FIFOALMOSTFULL); // is fifo full
110
111 // address register
112 always@(posedge csi_clockreset_clk, negedge csi_clockreset_reset_n) begin
113 if (!csi_clockreset_reset_n)
114 address <= 0;
115 else begin
116 if (address == 0) // initailize
117 address <= ADDRESSBASE;
118 else if (is_address_sload) // is sload of address?
119 address <= ADDRESSBASE;
120 else if (is_increment_address) // is increment address of sdram?
121 address <= address + BYTEENABLEWIDTH;
122 end
123 end
124
125 // reads_pending register
126 always@(posedge csi_clockreset_clk, negedge csi_clockreset_reset_n) begin
127 if (!csi_clockreset_reset_n)
128 reads_pending <= 0;
129 else begin
130 if (is_increment_address) begin // is increment address of sdram?
131 if (!avm_m1_readdatavalid)
132 reads_pending <= reads_pending + 1'b1;
133 end
134 else begin
135 if (avm_m1_readdatavalid) // read data valid
136 reads_pending <= reads_pending - 1'b1;
137 end
138 end
139 end
140
141 // ltm clk
142 ltm_pll ltm_pll0 (
143 .inclk0(coe_ltm_export_iCLK_50), // 50MHz
144 .c0(ltm_nclk) // 33MHz
145 );
146
147 // I2S ltm config
148 lcd_3wire_config wire0 (
149 // Host Side
150 .iCLK(coe_ltm_export_iCLK_50), // 50MHz
151 .iRST_n(coe_ltm_export_iRST0), // reset delay 0
152 // 3 wire Side
153 .o3WIRE_SCLK(coe_ltm_export_oSCLK), // I2S clk
154 .io3WIRE_SDAT(coe_ltm_export_ioSDAT),// I2S data
155 .o3WIRE_SCEN(coe_ltm_export_oSCEN) // I2s clk enable
156 );
157
158 // tcon controller
159 touch_tcon tcon0 (
160 .iCLK(ltm_nclk), // 33MHz
161 .iRST_n(coe_ltm_export_iRST2), // reset delay 2
162 // sdram side
163 .iREAD_DATA1({1'b0, fifo_rddata[15:11], fifo_rddata[7:0], 2'b00}), // G[9:5]B[9:0]
164 .iREAD_DATA2({1'b0, fifo_rddata[10:8], 2'b00, fifo_rddata[23:16], 2'b00}), // G[4:0]R[9:0]
165 .oREAD_SDRAM_EN(read), // sdram read request
166 // lcd side
167 .oLCD_R(coe_ltm_export_oR), // ltm R
168 .oLCD_G(coe_ltm_export_oG), // ltm G
169 .oLCD_B(coe_ltm_export_oB), // ltm B
170 .oHD(coe_ltm_export_oHD), // ltm h. sync
171 .oVD(coe_ltm_export_oVD), // ltm v.sync
172 .oDEN(coe_ltm_export_oDEN) // ltm data enable
173 );
174
175 // ltm fifo
176 dcfifo # (
177 .lpm_width(DATAWIDTH), // data width of fifo
178 .lpm_numwords(FIFODEPTH), // fifo depth of fifo
179 .lpm_widthu(FIFODEPTHLOG2) // fifo width of depth
180 ) fifo0 (
181 .wrclk(fifo_wrclk), // fifo write clk
182 .aclr(fifo_aclr), // fifo asynchronous clear
183 .wrreq(fifo_wrreq), // fifo write request
184 .data(fifo_wrdata), // fifo write data
185 .rdclk(fifo_rdclk), // fifo read clk
186 .rdreq(fifo_rereq), // fifo read request
187 .q(fifo_rddata), // fifo read data
188 .rdempty(fifo_empty), // fifo empty
189 .wrusedw(fifo_used) // fifo used data
190 );
191
192 endmodule

LTM Controller使用的是效率較佳的Master Pipelined Read Transfer

album18

(1) 此時Master開始對Avalon Bus做讀取的動作,因此對Avalon Bus發出addr1信號並將read信號拉high。此時剛好碰到Avalon Bus給master的waitrequest為high,表示Avalon Bus正在忙碌中,因此addr1與read必須再多delay 1個clk。

(2) 此時Avalon Bus已經不忙碌,所以Avalon Bus將waitrequest拉low,此時Avalon Bus接受了Master所發出的addr1與read信號。

(3) 此時Avalon Bus不忙碌,所以Avalon Bus的waitrequest為low,此時Avalon Bus接受了下一筆Master所發出的addr2與read信號。在此同時,Avalon Bus發給Master的readdatavalid為high信號,表示Master可從Avalon Bus去讀取addr1的數據data1。

(4) 此時Avalon Bus不忙碌,所以Avalon Bus的waitrequest為low,此時Avalon Bus接受了下一筆Master所發出的addr3與read信號,所以在此時已經有兩筆數據pending在Avalon Bus還未傳輸。

(5) 此時Avalon Bus發給Master的readdatavalid為high,所以Master從Avalon Bus讀取數據 (data2)。

(6) 此時Avalon Bus發給Master的readdatavalid為low,所以Master無法從Avalon Bus讀取數據。在此同時,Master對Avalon Bus發出addr1、read與flush為high信號,告訴Avalon Bus讀取第4筆數據並且放棄讀取pending在Avalon Bus而尚未讀取的數據(data3)

(7) 此時Avalon Bus發給Master的readdatavalid為high,所以Master從Avalon Bus讀取數據(data4),因為data3以前被放棄。

Step 4:
開發HAL

目前僅提供一個HAL,就是讓C語言可以指定x, y座標與該pixel的RGB,如此才能將讀取的bmp圖片寫進SDRAM frame buffer。

第9行,請補上offset的計算方式。

最後完整程式如下所示:

ltm.c / C

1 /*
2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3
4 Filename : ltm.c
5 Compiler : Nios II 8.1
6 Description : LTM API for LTM Controller
7 Release : 12/18/2008 1.0
8 */
9
10 #include "alt_types.h" // alt_u32, alt_u8
11 #include "ltm.h"
12 #include "ltm_regs.h" // register map of ltm controller
13
14 void ltm_write_rgb_to_pixel(alt_u32 base, alt_u16 x, alt_u16 y, alt_u8 r, alt_u8 g, alt_u8 b) {
15 alt_u32 offset;
16 alt_u32 data;
17
18 offset = ((y * LTM_WIDTH)+(x)) * BYTEENABLEWIDTH;
19
20 data = g & 0xff;
21 data = (data << 8) | (r & 0xff);
22 data = (data << 8) | (g & 0xff);
23 data = (data << 8) | (b & 0xff);
24
25 IOWR_LTM_RGB(base + offset, data);
26 }

Step 5:
使用SOPC Builder將IP打包

在Lab 2我們已經學會將七段顯示器controller打包的方法,使用相同的方式將LTM controller打包成ip。唯一不同的是,因為LTM Controller的module較多,所以必須加入多個.v檔。注意Top Level Module是LTM_Controller.v

album05 

建立成功會在左上角顯示LTM_Controller。

album06 

Step 6:
加入Pipeline Bridge

在Quartus II 7.1之後,提出了Pipeline Bridge架構,可以將master與slave間的address、write、writedata、read、readdata等信號加上pipeline stage,可以提高整個系統的Fmax,更詳細的解釋請參考: (原創) 如何使用Pipeline Bridge增進Nios II系統的Fmax? (SOC) (Quartus II) (Nios II) (SOPC Builder) (DE2-70)

album07

album08

album09

Step 7:
加入LTM Controller

接受原來LTM_Controller.v預設的parameter即可。

album10

由於ltm是透過pipeline_bridge與sdram相連。所以將

ltm的master與pipeline_bridge_ltm的slave相連。
pipeline_bridge_ltm的master與sdram的slave相連。
ltm的slave與CPU的data_master相連。

album11

設定LTM Controller的Bus Arbitration Rule
由於LTM的更新速度很快(33MHz),所以LTM Contoller需要不斷的對SDRAM要資料顯示,因此預設的bus佔有率已經無法滿足LTM的正常顯示,必須加以調整。

顯示Arbitration

album12

將Arbitration Rule調85,表示LTM將占據SDRAM 85%的頻寬。

album13

事實上,LTM要能穩定的顯示,有三個重要的參數彼此影響:

1.LTM Controller的Bus Arbitration Rule
2.LTM Controller的FIFO depth
3.LTM Controller的FIFO almost full value

我花了很多時間去tune這三個參數讓LTM的顯示穩定,若3個參數沒調好,可能會造成LTM影像上下shift或者上下左右不斷的輪播..等等問題。

最後Auto-Assign Base Address,解決address overlap的錯誤。

album14

按Generate開始產生SOPC System。

Step 8:
Quartus II編譯與Programmer燒入

top module部分我就沒要求同學修改,Quartus II直接編譯即可,不過還是建議同學花時間看看top module是怎麼寫的。

軟體部分
Step 9:
Import hello_world_0與hello_world_0_syslib
Run As Hardware

整個程式的架構與Lab 3的DE2_70_SD_Card_Audio_Player類似,只是Wav Lib改成Bmp Lib,Audio HAL改成LTM HAL。程式難度不高,同學可自行研究。

唯一要同學加上的是140行,使用我們自己寫的ltm_write_rgb_to_pixel()將RGB載入SDRAM。

最後完整程式如下所示:

hello_world.c / C

1 /*
2 (C) OOMusou 2008 http://oomusou.cnblogs.com
3
4 Filename : hello_world.c
5 Compiler : Nios II 8.1
6 Description : main() for photo frame
7 Release : 12/18/2008 1.0
8 */
9 #include <stdio.h>
10 #include "my_includes.h"
11 #include "my_types.h"
12 #include "FatFileSystem.h" // FAT16 lib
13 #include "SDCardDriver.h" // SDCard HAL
14 #include "bitmap.h" // bmp lib
15 #include "ltm.h" // LTM HAL
16
17 #define WAITING_SEC 5
18 // bmp file list parameter
19 #define MAX_FILE_NUM 128 // maximum file number in file list
20 #define FILENAME_LEN 32 // length of file name
21
22 typedef struct {
23 int filenum;
24 char filename[MAX_FILE_NUM][FILENAME_LEN];
25 } BmpFileList;
26
27 static BmpFileList bmp_file_list;
28
29 // bmp parameter
30 #define BMP_WIDTH 800
31 #define BMP_HEIGHT 480
32 #define BMP_RGB_OFST 54
33 #define NUM_RGB 3
34 #define OFST_R 2
35 #define OFST_G 1
36 #define OFST_B 0
37
38 // wait sdcard insert into socket
39 void wait_sdcard_insert(void) {
40 bool bFirstTime2Detect = TRUE;
41
42 while(!SD_card_init()) {
43 if (bFirstTime2Detect){
44 printf("Please insert SD card.\n");
45 bFirstTime2Detect = FALSE;
46 }
47 }
48
49 printf("Find SD card.\n");
50 }
51
52 // build bmp file list
53 int build_bmp_play_list(void) {
54 int filecnt = 0; // number of bmp
55 FAT_BROWSE_HANDLE hFileBrowse; // FAT browse handle
56 FAT_DIRECTORY Directory; // FAT directory
57 FAT_FILE_HANDLE hFile; // FAT file handle
58 alt_u8 header[BMP_RGB_OFST];
59 char filename[FILENAME_LEN];
60
61 bmp_file_list.filenum = 0;
62
63 // FatFileSystem.h
64 if (!Fat_FileBrowseBegin(&hFileBrowse)){
65 printf("browse file fail.\n");
66 return 0;
67 }
68
69 // FatFileSystem.h
70 while (Fat_FileBrowseNext(&hFileBrowse, &Directory)) {
71 // only bmp in file list
72 if (strncmpi(Directory.Extension, "BMP", 3))
73 continue;
74
75 // compose filename
76 Fat_ComposeFilename(&Directory, filename);
77
78 // fopen() (FatFileSystem.h)
79 if (!Fat_FileOpen(&hFile, filename)) {
80 printf("bmp file open fail.\n");
81 continue;
82 }
83
84 // fread() (FatFileSystem.h)
85 if (!Fat_FileRead(&hFile, header, sizeof(header))) {
86 printf("bmp file read fail.\n");
87 continue;
88 }
89
90 // fclose() (FatFileSystem.h)
91 Fat_FileClose(&hFile);
92
93 // convert to bmp header (bitmap.h)
94 BitmapHeader *bmp_header = get_bmp_header(header);
95
96 // check valid bmp format
97 if (!chk_valid_bmp(bmp_header)) {
98 printf("%s is invalid bmp for DE2-70 LTM.\n", filename);
99 continue;
100 }
101
102 // copy filename into file list
103 strcpy(bmp_file_list.filename[filecnt], filename);
104 filecnt++;
105 } // while
106
107 bmp_file_list.filenum = filecnt;
108
109 return filecnt;
110 }
111
112 // play bmp by filename
113 bool play_bmp(char *filename){
114 FAT_FILE_HANDLE hFile; // FAT file handle
115
116 // fopen() (FatFileSystem.h)
117 if (!Fat_FileOpen(&hFile, filename)){
118 printf("Fat_FileOpen fail.\n");
119 return FALSE;
120 }
121
122 printf("BMP file name is %s.\n", filename);
123
124 if (!Fat_FileSeek(&hFile, FILE_SEEK_BEGIN, BMP_RGB_OFST)) {
125 printf("Fat_FileSeek fail.\n");
126 return FALSE;
127 }
128
129 // bmp RGB array
130 alt_u8 buff_bmp[BMP_WIDTH * BMP_HEIGHT * NUM_RGB];
131
132 // fread() (FatFileSystem.h)
133 if (!Fat_FileRead(&hFile, buff_bmp, sizeof(buff_bmp))) {
134 printf("Fat_FileRead fail.\n");
135 return FALSE;
136 }
137
138 // move to sdram frame buffer
139 int x, y;
140 for(x=0; x < BMP_WIDTH; x++) {
141 for(y=0; y < BMP_HEIGHT; y++) {
142 // get RGB
143 int r = buff_bmp[(y * BMP_WIDTH + x) * NUM_RGB + OFST_R];
144 int g = buff_bmp[(y * BMP_WIDTH + x) * NUM_RGB + OFST_G];
145 int b = buff_bmp[(y * BMP_WIDTH + x) * NUM_RGB + OFST_B];
146
147 // write rgb to sdram frame buffer (ltm.h)
148 ltm_write_rgb_to_pixel(SDRAM_BASE, x,y, r, g, b);
149 }
150 }
151
152 printf("Finsh reading FAT16\n");
153 // file close
154 Fat_FileClose(&hFile);
155
156 return TRUE;
157 }
158
159 int main() {
160 int play_index = 0;
161 alt_u8 filename[FILENAME_LEN];
162
163 // check SD card
164 wait_sdcard_insert();
165
166 // Mount SD-CARD
167 if (!Fat_Mount(FAT_SD_CARD)) {
168 printf("SD card mount fail.\n");
169 return -1;
170 }
171
172 // build wave list in gWavePlayList
173 if (build_bmp_play_list() == 0) {
174 printf("There is no bmp file in the root directory of SD card.\n");
175 return -1;
176 }
177
178 while(1) {
179 strcpy(filename, bmp_file_list.filename[play_index]);
180
181 if (!play_bmp(filename)){
182 printf("bmp play fail.\n");
183 return -1;
184 }
185
186 play_index++;
187
188 // repeat
189 if (play_index >= bmp_file_list.filenum)
190 play_index = 0;
191
192 // waiting 5 sec between bmp
193 printf("Waiting %d sec...\n", WAITING_SEC);
194 usleep(WAITING_SEC * 1000 * 1000);
195 }
196
197 return 0;
198 }
199

lab4_files.7z提供了3張bmp圖檔,請將這3個圖檔copy到SD卡根目錄,即可開始測試。每張照片中顯示會間格5秒鐘。

希志あいの

 aino

七海なな

nana

瑤瑤

yoyo

若你要使用自己的bmp也可以,但有幾個限制:
1.使用24位元的bmp且不能壓縮。
2.大小為800 * 480,配合LTM的解析度。

Step 10:
將軟體與硬體存到Flash

從Lab 1到Lab 3,每次要執行Nios II程式,一定得經過Quartus II燒入sof,並且用Nios II EDS Run As Hardware,才能將elf載入到SDRAM或SRAM執行,對於一個完整的嵌入式產品,不可能每次都要靠PC將軟體與硬體傳入DE2-70。所幸DE2-70提供了兩個Flash,儘管斷電之後,資料仍然存在,如此只要一Power on後,就可以執行Nios II程式,不需要PC的介入。

album15

接下來我們會將硬體sof放到epcs_flash,將軟體elf放到cfi_flash,這樣以後只要電源按鈕按下,就可以執行我們的數位相框,不再需要PC了。

Step 11:
使用Nios II EDS的Flash Programmer

Nios II EDS
Tools -> Flash Programmer

新增一個configuration

album16

album17

若成功會出現以下訊息: 

#!/bin/sh
#
# This file was automatically generated by the Nios II IDE Flash Programmer.
#
# It will be overwritten when the flash programmer options change.
#

cd C:
/DE2-70/DE2_70_LTM_NIOS/software/hello_world_0/Debug

# Creating .flash file
for the FPGA configuration
"$SOPC_KIT_NIOS2/bin/sof2flash" --epcs --input="C:/DE2-70/DE2_70_LTM_NIOS/DE2_70
.sof" --output="DE2_70.flash"
Info: *******************************************************************
Info: Running Quartus II Convert_programming_file
Info: Command: quartus_cpf
--no_banner --convert --device=EPCS128 --option=DE2_7
0.opt C:/DE2-70/DE2_70_LTM_NIOS/DE2_70.sof DE2_70.pof
Info: Quartus II Convert_programming_file was successful.
0 errors, 0 warnings
Info: Peak
virtual memory: 72 megabytes
Info: Processing ended: Wed Dec
24 01:00:31 2008
Info: Elapsed time:
00:00:03
Info: Total CPU time (on all processors):
00:00:03
Info:
*******************************************************************
Info: Running Quartus II Convert_programming_file
Info: Command: quartus_cpf
--no_banner --convert DE2_70.pof DE2_70.rpd
Info: Quartus II Convert_programming_file was successful.
0 errors, 0 warnings
Info: Peak
virtual memory: 66 megabytes
Info: Processing ended: Wed Dec
24 01:00:33 2008
Info: Elapsed time:
00:00:02
Info: Total CPU time (on all processors):
00:00:02

# Programming flash with the FPGA configuration
"$SOPC_KIT_NIOS2/bin/nios2-flash-programmer" --epcs --base=0x09000000 --sidp=0x0
90008a8
--id=1669606734 --timestamp=1230048124 "DE2_70.flash"
Using cable
"USB-Blaster [USB-0]", device 1, instance 0x00
Resetting and pausing target processor: OK
Reading System ID at address
0x090008A8: verified

: Checksumming existing contents

00000000 : Verifying existing contents

00010000 : Verifying existing contents

00020000 : Verifying existing contents

00030000 : Verifying existing contents

00040000 : Verifying existing contents

00050000 : Verifying existing contents

00060000 : Verifying existing contents

00070000 : Verifying existing contents

00080000 : Verifying existing contents

00090000 : Verifying existing contents

00000000 : Reading existing contents

00010000 : Reading existing contents

00020000 : Reading existing contents

00030000 : Reading existing contents

00040000 : Reading existing contents

00050000 : Reading existing contents

00060000 : Reading existing contents

00070000 : Reading existing contents

00080000 : Reading existing contents

00090000 : Reading existing contents

Checksummed
/read 640kB in 15.4s

00000000 ( 0%): Erasing

00010000 (10%): Erasing

00020000 (20%): Erasing

00030000 (30%): Erasing

00040000 (40%): Erasing

00050000 (50%): Erasing

00060000 (60%): Erasing

00070000 (70%): Erasing

00080000 (80%): Erasing

00090000 (90%): Erasing

Erased 640kB
in 5.9s (108.4kB/s)

00000000 ( 0%): Programming

00010000 (10%): Programming

00020000 (20%): Programming

00030000 (30%): Programming

00040000 (40%): Programming

00050000 (50%): Programming

00060000 (60%): Programming

00070000 (70%): Programming

00080000 (80%): Programming

00090000 (90%): Programming

Programmed 589KB
+51KB in 13.5s (47.4KB/s)
Did not attempt to verify device contents
Leaving target processor paused

# Creating .flash file
for the project
"$SOPC_KIT_NIOS2/bin/elf2flash" --base=0x0a800000 --end=0xaffffff --reset=0xa800
000 --input="hello_world_0.elf" --output="cfi_flash.flash" --boot="C:/altera/81/
ip/altera/nios2_ip/altera_nios2/boot_loader_cfi.srec"

# Programming flash with the project
"$SOPC_KIT_NIOS2/bin/nios2-flash-programmer" --base=0x0a800000 --sidp=0x090008a8
--id=1669606734 --timestamp=1230048124 "cfi_flash.flash"
Using cable
"USB-Blaster [USB-0]", device 1, instance 0x00
Resetting and pausing target processor: OK
Reading System ID at address
0x090008A8: verified

: Checksumming existing contents

00000000 : Verifying existing contents

00002000 : Verifying existing contents

00004000 : Verifying existing contents

00006000 : Verifying existing contents

00008000 : Verifying existing contents

0000A000 : Verifying existing contents

0000C000 : Verifying existing contents

0000E000 : Verifying existing contents

00010000 : Verifying existing contents

Checksummed
/read 81kB in 1.9s
Erase not required

00000000 ( 0%): Programming

00002000 ( 0%): Programming

00004000 ( 0%): Programming

00006000 ( 0%): Programming

00008000 ( 0%): Programming

0000A000 (
0%): Programming

0000C000 (
0%): Programming

0000E000 (
0%): Programming

00010000 ( 0%): Programming

Programmed 81KB
in 0.0s
No change to device contents
Leaving target processor paused

重新Power On,就可以看到數位相框自動執行了!!

album19 

完整程式碼下載
lab4_files.7z (一個未完成的半成品,可以根著本文一步一步完成)
DE2_70_LTM_NIOS.7z (最後完整的結果)

Question
(這是我當時給學生的homework,各位有興趣可以自己自做做看)

1.雖然設定每張圖片間隔5秒鐘顯示,為什麼實際執行時,圖片與圖片的間格時間超過5秒鐘呢?

2.目前LTM controller的SDRAM base address使用的是Verilog的parameter,請改用register的方式,讓Nios II的C語言可以透過Slave Interface動態更改SDRAM的base address。

3.目前每一次都要重新從SD卡讀取像片至SDRAM,很花時間,請試著改成只有第一次需從SD卡讀取相片至SDRAM,以後就不必從SD卡讀取。(有很多種方法,請發揮你的創意)。

4.請結合(原創) 如何設計一個SD卡Wav Player? (SOC) (Quartus II) (SOPC Builder) (Nios II) (DE2-70)並搭配μC/OS-II,讓DE2-70可以同時當數位像框,並可播放音樂。

Conclusion
很多人想將原本純硬體的設計加上Nios II CPU變成HW/SW Co-Design,最常見的問題就是SDRAM要怎麼讓軟硬體共用,若你還是一直執著於使用SDRAM_Control_4Port那條路,將永遠無法解決,既然打算掛上Nios II CPU,就要引進Avalon Bus概念,我歸納出3點:

1.使用Avalon Bus將整個系統的Nios II CPU與SDRAM切開。

2.使用Altera為Avalon Bus所提供的SDRAM Controller,而不要使用SDRAM_Control_4Port。

3.為你自己的硬體週邊寫Master或Slave Controller。

如此整個系統才會變成SOPC Enable,整體的效能才會好,當然所付出的代價就是你要去了解Avalon-MM Interface Specification ,並且慢慢的調試。

See Also
(原創) 如何自己用SOPC Builder建立一個能在DE2-70上跑μC/OS-II的Nios II系統? (SOC) (Quartus II) (SOPC Builder) (Nios II) (μC/OS-II) (DE2-70)
(原創) 如何設計一個七段顯示器Controller? (SOC) (Quartus II) (SOPC Builder) (Nios II) (DE2-70)
(原創) 如何設計一個SD卡Wav Player? (SOC) (Quartus II) (SOPC Builder) (Nios II) (DE2-70)
(原創) 如何設計一個數位相框? (SOC) (Quartus II) (SOPC Builder) (Nios II) (DE2-70)
(原創) 如何使用Pipeline Bridge增進Nios II系統的Fmax? (SOC) (Quartus II) (Nios II) (SOPC Builder) (DE2-70)

Reference
Avalon-MM Interface Specification

posted on 2010-08-14 14:23  真 OO无双  阅读(26266)  评论(30编辑  收藏  举报

导航