FPGA學習心得及(flash讀寫,+lwip+資料傳送等問題)
前段時間應老闆的專案需求,對硬體絲毫不懂得我開始接觸edk硬體程式設計,感覺這段時間跟硬體打交道,自己都老了不少。首先,硬體程式設計編譯很慢,編譯一次有時候得10-20分鐘,尤其是用verilog寫得程式比較大的時候。其次,除錯非常麻煩,不能像利用c或者c#那樣斷點除錯了,只能通過chipscope看波形除錯。有時候一個小小的錯誤竟然花一兩天還找不出來,初學的我上不起啊。總體感覺寫verilog程式時序非常重要,一定要保證時序不出一點差錯,才能保證程式執行如自己所願。下面主要記錄下在寫flash控制器與通過網路傳輸資料程式時的一些心得。
關於falsh的讀寫
學習FPGA一個月後,老闆就讓我們寫flash控制器,我們用的板子是virtex 5,falsh是intel的strata p30,內嵌在FPGA中.在這個過程中確實感覺datasheet非常重要,先前沒看datasheet的時候寫得程式只能讀資料,但是不能寫資料,化了幾天時間都沒弄明白,後來看了下datasheet,才發現在寫資料之前必須將資料塊解鎖並擦除,然後才可以寫。對flash讀與寫步驟如下:
讀一個數據的流程:
1寫 x00ff,表示要讀資料了
2讀取資料
3寫0x70 ,表示讀狀態暫存器,判斷讀資料是否完成
寫資料的過程:
1:讀暫存器狀態(write0x70,read rs[7])
2:對當前寫資料所屬記憶體塊解鎖write 0x60,write 0xD0
3:擦除資料所屬塊 write 0x20, write 0xD0
4:讀暫存器狀態
5:往指定地址寫資料
6:讀暫存器狀態
關於lwip的資料傳輸。
在板子上做基於網路的資料傳輸的目的是想:在遠端控制板子的一些外圍裝置的引數,這就需要將引數通過網路傳輸到板子,並在板子上部署一個微型網路伺服器,對這些資料進行接收,並存儲在flash中,當板子掉電重啟後,從flash中讀取引數,以使板子外圍裝置正常執行。
當老闆提出這個需求的時候,我就傻眼了,客戶端是高階語言c#或c++,flash讀寫是硬體描述語言,如何進行通訊啊?後來經過王工大牛的指點,才知道xinlinx都為我們想好了,有個sdk可以編軟體,並且是c語言的,只要在xps中將硬體設計好,然後在sdk中編寫網路傳輸以及flash讀寫的程式即可了,這樣客戶端發來的資料就可以被板子的伺服器接收並存儲起來了。到了這一步,又遇到問題了,如何利用c語言對flash進行讀寫呢?以前寫的可是用verilog寫得啊!正當我無望的時候,又是王工大牛出現了,這才知道,通過xps設計好硬體後,xps會為每個外圍裝置(包括flash)分配一個地址範圍,我們對flash進行讀寫,就是對相對基地址讀寫。由於ise11.4中沒有讀寫flash的ip核,起初準備自己寫個flash讀寫的ip核,沒想到這個過程是出奇的複雜。搞了一週都沒搞出來,後來一次偶然試驗,直接向匯流排寫類似在verilog中讀寫flash的命令就可實現flash讀寫及擦出操作。以下是對flash中地址blockAddr的各種操作
對Blockaddr地址所屬塊擦除
int EraseBlock(u32 Blockaddr)
{
u8 reg_status;
//unlock
XIo_Out16(FlashBaseAddr+2*Blockaddr,0x0060);
XIo_Out16(FlashBaseAddr+2*Blockaddr,0x00D0);
//erase
XIo_Out16(FlashBaseAddr+2*Blockaddr,0x0020);
XIo_Out16(FlashBaseAddr+2*Blockaddr,0x00D0);
//read register status
while(1)
{
XIo_Out16(FlashBaseAddr,0x0070);
reg_status = XIo_In16(FlashBaseAddr);
//xil_printf("%x\r\n",reg_status);
if(reg_status == 0x0080)
break;
}
return 1;
}
對Blockaddr地址寫資料data
int WriteDataToBlockAddr(u32 Blockaddr,u16 data)
{
u8 reg_status;
XIo_Out16(FlashBaseAddr+2*Blockaddr,0x0040);
XIo_Out16(FlashBaseAddr+2*Blockaddr,data);
while(1)
{
XIo_Out16(FlashBaseAddr,0x0070);
reg_status = XIo_In16(FlashBaseAddr);
//xil_printf("%x\r\n",reg_status);
if(reg_status == 0x0080)
break;
}
return 1;
}
讀取地址blockAddr中的資料
u16 ReadDataByWord(u32 dataAddr)
{
XIo_Out16(FlashBaseAddr+2*dataAddr,0x00ff);
u16 value = XIo_In16(FlashBaseAddr+2*dataAddr);
return value;
}
在c中對flash讀寫可以實現了,下面就是網路伺服器的構建了,這裡用到了lwip協議棧,據說是瑞典科學院一般人弄的,很適合在嵌入式系統中做網路傳輸。只要在伺服器端監聽客戶端連線並解析客戶端傳來的資料就行了。比如,客戶端傳一個read/addr,命令給伺服器,flash中地址addr的資料就被讀取出來傳給客戶端,客戶端傳一個write/addr/data命令給伺服器端,那麼flash中地址為addr就被寫上資料data。由於flash寫操作的特性,在寫之前需要將此塊先擦除,所以利用這種方式寫一個數據,那麼此塊中以前的資料都被擦除了。由於要保證原有在flash中的資料在寫一個數據後還存在,就必須將此塊的資料全部讀取出來,然後再與要寫的資料一起寫回去,就可以保證寫一個數據後原有資料不被擦除。在寫這一部分的時候遇到了鬱悶一週的問題:就是當在伺服器端讀取資料傳送給客戶端時,發了200多bytes就出現tcp_write errror!試了很多方法,包括改pbuf的大小都不行,後來想在伺服器端建一個大陣列,將讀取的資料儲存在其中,這就不要傳給客戶端,但是結果還是不行,因為系統記憶體資源太小不可能分配一個能儲存128kB的空間。後來呢才想到,傳送命令給伺服器,讓讀取的資料不斷地傳回來,是不是也會使空間不夠啊?然後試著傳送一個命令只獲取塊中512個數據,不斷地傳送命令直至此塊資料獲取完畢,竟然成功了!喜悅啊!
這就是今晚弄成功的,心情愉悅,故記之。
以上內容可能可能語言組織不好,有些問題沒說清,今天實在很晚了,下次有時間按再來改!敬請各位諒解