比特流是一個常用詞匯,用于描述包含FPGA完整內(nèi)部配置狀態(tài)的文件,包括布線、邏輯資源和IO設(shè)置。大多數(shù)現(xiàn)代FPGA都是基于SRAM的,包括Xilinx Spartan和Virtex系列。在FPGA上電或隨后的FPGA重新配置期間,比特流從外部諸如閃存這樣的非易失性存儲器中讀取,通過FPGA配置控制器的處理,加載到內(nèi)部的配置SRAM中。
在有些情況下,設(shè)計者需要很好地了解FPGA比特流的內(nèi)部結(jié)構(gòu)。例如,使用FPGA物理實現(xiàn)工具的參數(shù)不能訪問自定義的低級比特流、實現(xiàn)復(fù)雜的配置回溯方案、通過內(nèi)部配置端口(ICAP)產(chǎn)生用于FPGA重新配置的短命令序列、讀配置狀態(tài)等。遺憾的是,逆向工程和篡改比特流以非法獲取專利設(shè)計信息也屬于這些使用范疇。
比特流格式
Xilinx FPGA的比特流結(jié)構(gòu)如圖1所示。
比特流包括以下組成部分:填充、同步字、用于訪問配置寄存器的命令、存儲器幀和解同步字。
填充
填充的數(shù)據(jù)是全0或全1序列,被FPGA配置控制器忽略。填充數(shù)據(jù)用于非易失性存儲器中分離比特流。一般使用全1填充較為方便,因為執(zhí)行擦除后閃存的狀態(tài)一般也是全1。
同步字
同步字是一個特殊值(0xAA995566),通知FPGA配置控制器處理后續(xù)的比特流數(shù)據(jù)。
解同步字
解同步字通知FPGA配置控制器比特流的末端位置。解同步字之后,所有的比特流數(shù)據(jù)被忽略,直到遇到下一個同步字。
命令
命令用于讀和寫FPGA配置控制器寄存器。每個比特流中出現(xiàn)的一些命令,有的是ID-CODE,用于標(biāo)識比特流屬于哪個FPGA器件。幀地址寄存器(FAR)、幀數(shù)據(jù)寄存器(FDRI)和無操作(NOOP)將被忽略。
存儲器幀
存儲器幀是配置Xilinx FPGA的比特流基本單元。幀的大小與具體的FPGA系列有關(guān),系列不同,幀的大小也不同。Virtex 6器件的幀有2592位。每個Virtex 6器件具有的幀數(shù)不同,從最小7491(對于LX75T)到最大55548(對于LX550T)。幀用于多個邏輯片、IO、BRAM及其他FPGA的配置。每幀都有一個地址,對應(yīng)于FPGA配置空間的位置。比特流使用FAR和FDRI命令序列來配置幀。
Virtex 6 FPGA配置用戶指南包含足夠的關(guān)于比特流和訪問FPGA配置控制器寄存器命令的文檔。然而,關(guān)于存儲器幀的詳細文檔不僅對于Xilinx FPGA無法獲得,而且對于其他供應(yīng)商的FPGA也是如此。
Xilinx的BITGEN實用程序
BITGEN是Xilinx的實用程序,利用本地電路描述(NCD)格式的布局布線后文件,創(chuàng)建用于FPGA配置的比特流。BITGEN是一個高度可配置的工具,具有100多個命令行選項(在命令行工具用戶指南中描述)。有些選項用于確定比特流輸出格式、啟用壓縮處理減少比特流大小、提高FPGA配置速度、使用CRC來確保數(shù)據(jù)完整性、對比特流加密等。
示例
以下示例用于根據(jù)差異部分配置的短比特流, 通過腳本語言描述比特流命令。
#!/devl/perl/bin/perl useSwitch; #----------------------------------------------------------------------------- #Copyright(C)2011OutputLogic.com #Thissourcefilemaybeusedanddistributedwithoutrestriction #providedthatthiscopyrightstatementisnotremovedfromthefile #andthatanyderivativeworkcontainstheoriginalcopyrightnotice #andtheassociateddisclaimer. # #THISSOURCEFILEISPROVIDED"ASIS"ANDWITHOUTANYEXPRESS #ORIMPLIEDWARRANTIES,INCLUDING,WITHOUTLIMITATION,THEIMPLIED #WARRANTIESOFMERCHANTIBILITYANDFITNESSFORAPARTICULARPURPOSE. #----------------------------------------------------------------------------- # #AscripttoparseXilinxFPGAbitstreamin.RBTformat # if($#ARGV0) { ????die?"Usage:?xilinx_bitstream_parser?"; } $verbose=2; open(RBT_FILE,"<$ARGV[0]")??????????||?die?"Error:?can't?open?$ARGV[0]?for?input "; #?Bus?OP?codes $opcode{"00"}?=?"no?op"; $opcode{"01"}?=?"read"; $opcode{"10"}?=?"write"; $opcode{"11"}?=?"decrypt"; #?Regster?Addresses $reg{"00000"}?=?"CRC?"; $reg{"00001"}?=?"FAR?"; $reg{"00010"}?=?"FDRI"; $reg{"00011"}?=?"FDRO"; $reg{"00100"}?=?"CMD?"; $reg{"00101"}?=?"CTL?"; $reg{"00110"}?=?"MASK"; $reg{"00111"}?=?"STAT"; $reg{"01000"}?=?"LOUT"; $reg{"01001"}?=?"COR?"; $reg{"01010"}?=?"MFWR"; $reg{"01011"}?=?"CBC?"; $reg{"01100"}?=?"ID??"; $reg{"01101"}?=?"AXSS"; $reg{"01110"}?=?"COR1"; $reg{"01111"}?=?"CSOB"; $reg{"10000"}?=?"WBSTAR"; $reg{"10001"}?=?"TIMER"; $reg{"10010"}?=?"RBCRC0"; $reg{"10011"}?=?"RBCRC1"; $reg{"10100"}?=?"RBCRC2"; $reg{"10101"}?=?"EFAR"; $reg{"10110"}?=?"BOOTSTS"; $reg{"10111"}?=?"TESTMODE"; $reg{"11000"}?=?"CTL1"; #?Configuration?Commands $command{"00000"}?=?"NULL"; $command{"00001"}?=?"WCFG"; $command{"00010"}?=?"MFW"; $command{"00011"}?=?"LFRM"; $command{"00100"}?=?"RCFG"; $command{"00101"}?=?"START"; $command{"00110"}?=?"RCAP"; $command{"00111"}?=?"RCRC"; $command{"01000"}?=?"AGHIGH"; $command{"01001"}?=?"SWITCH"; $command{"01010"}?=?"GRESTORE"; $command{"01011"}?=?"SHUTDOWN"; $command{"01100"}?=?"GCAPTURE"; $command{"01101"}?=?"DESYNCH"; $command{"01110"}?=?"DRTEST"; $command{"01111"}?=?"IPROG"; $command{"10000"}?=?"CRCC"; $fa?=?-1; $last_fa?=?-1; $in_LOUT?=?0; @LOUT_wordcnt?=?(); while?( ) { $type=""; $op=""; $reg=""; chop; nextif(!/^[01]/); if(/^10101010100110010101010101100110/){ $type="Syncword"; } elsif(/^11111111111111111111111111111111/){ $type="Dummyword"; } elsif(/^00000000000000000000000010111011/){ $type="BusWidthword"; } elsif(/^00010001001000100000000001000100/){ $type="8/16/32BusWidth"; } elsif(/^001/){ $type="Type1"; } elsif(/^010/){ $type="Type2"; } elsif(/^00000000000000000000000000000000/){ $type="NOOP"; $wordcnt=0; } else{ if(($registereq"FDRI")||($registereq"FDRO")||($last_commandeq"LFRM")){ $type="PartialCRCword"; }else{ $type="TypeUnknown"; } } if($typeeq"Type1"||$typeeq"Type2"){ $s=substr($_,3,2); if($opcode{$s}ne"") { $op=$opcode{$s}; } else { $op="Unknown"; } } if($typeeq"Type1"){ $s=substr($_,14,5); if($reg{$s}ne""){ $register=$reg{$s}; } else{ $register="Unknown"; } $wordcnt=&bin2dec(substr($_,21,11)); } if($typeeq"Type2"){ $wordcnt=&bin2dec(substr($_,5,27)); } $text="$type"; if($opeq"read") { $text.="$op"."$wordcntwordsfrom"."$register"; } elsif(($opeq"write")||($opeq"decrypt")) { $text.="$op"."$wordcntwordsto"."$register"; } elsif($opeq"noop") { $text.="NOOP"; } printf("%10s%s ",&bin2hex($_),$text); #Alittlehacksowecanseethenextstreamalso if(($registereq"LOUT")&&($wordcnt>1)) { $in_LOUT++; push@LOUT_wordcnt,$wordcnt; $wordcnt=0; } #startprintingouteverything for($i=0;$i<$wordcnt?&&?(($op?eq?"write")?||?($op?eq?"decrypt")?||?($op?eq?"read"))?&&?($_?=? );$i++) { chop; if(($opeq"write")&&($registereq"FAR")) { $fa=$_; } if(($registereq"FDRI")||($registereq"FDRO")) { #printf("%s%10s ","dataword".$i,$_); #don'tprintallframewords if($i==0) { printf("%s%d...%d ","datawords",$i,$wordcnt-1); } } elsif($registereq"LOUT") { $this_fa=&bin2dec($_); $BlkType=&bin2dec(substr($_,8,3)); $TopBot=(&bin2dec(substr($_,11,1))==0)?"Top":"Bot"; $MajRow=&bin2dec(substr($_,12,5)); $MajCol=&bin2dec(substr($_,17,8)); $MinCol=&bin2dec(substr($_,25,7)); printf("%10s%s[Block%d%sRow%dCol%dMinor%d] ",&bin2hex($_),"FrameAddress",$BlkType,$TopBot,$MajRow,$MajCol,$MinCol); if($this_fa$last_fa)?{ ?????????????printf?("ERROR:?Frame?Address?going?down?(%d?%d)? ",?$this_fa,?$last_fa); ??????????} ??????????$last_fa?=?$this_fa; ???????} ????????elsif?($register?eq?"FAR?")? ????????{ ????????????$BlkType?=?&bin2dec(substr($_,8,3)); ????????????$TopBot??=?(&bin2dec(substr($_,11,1))?==?0)???"Top"?:?"Bot"; ????????????$MajRow??=?&bin2dec(substr($_,12,5)); ????????????$MajCol??=?&bin2dec(substr($_,17,8)); ????????????$MinCol??=?&bin2dec(substr($_,25,7)); ??????????? ????????????printf?("%10s?%s?[Block?%d?%s?Row?%d?Col?%d?Minor?%d]? ",?&bin2hex($_)?,?$register?.?"?data?word?"?.?$i,?$BlkType,?$TopBot,?$MajRow,?$MajCol,?$MinCol);???????????? ???????????? ????????} ????????elsif?($register?ne?"CMD?")? ????????{ ????????????printf?("%10s?%s ",?&bin2hex($_)?,?$register?.?"?data?word?"?.?$i); ???????????? ????????}? ????????else? ????????{ ????????????$s?=?substr($_,27,5); ????????? ????????????if?($command{$s}?ne?"")? ????????????{ ????????????????$last_command?=?$command{$s}; ?????????????printf?("%10s?%s ",?&bin2hex($_),?$command{$s}?.?"?command",??); ?????????} ????????????else ????????????{ ?????????????printf?("%10s?%s ",?&bin2hex($_),?"Unknown?command"?); ?????????} ????????} ????} }?#?while close(RBT_FILE); sub?bin2dec { ????local($num)?=?@_; ????$retval?=?0; ????while?($num?ne?"") ????{ ????????$retval?=?$retval?*?2; ????????if?(substr($num,0,?1)?eq?"1")? ????????{ ????????????$retval++; ?????} ????????$num?=?substr($num,1); ????} ????return?$retval; } #?convert?32-digit?bin?number?to?hex sub?bin2hex { ????local($num)?=?@_; ????my?$str_hex?=?""; ???? ????for?($i=0;?$i<8;?$i++)? ????{ ????????#?left?to?right ????????my?$chunk?=?substr($num,?4?*?$i?,4);? ????????my?$retval?=?"0"; ????????switch?($chunk)? ????????{ ?????????case?"0000"?{?$retval?=?"0"?} ?????????case?"0001"?{?$retval?=?"1"?}???????? ?????????case?"0010"?{?$retval?=?"2"?} ?????????case?"0011"?{?$retval?=?"3"?}???????? ?????????case?"0100"?{?$retval?=?"4"?} ?????????case?"0101"?{?$retval?=?"5"?}???????? ?????????case?"0110"?{?$retval?=?"6"?} ?????????case?"0111"?{?$retval?=?"7"?}???????? ?????????case?"1000"?{?$retval?=?"8"?} ?????????case?"1001"?{?$retval?=?"9"?}???????? ?????????case?"1010"?{?$retval?=?"A"?} ?????????case?"1011"?{?$retval?=?"B"?}???????? ?????????case?"1100"?{?$retval?=?"C"?} ?????????case?"1101"?{?$retval?=?"D"?}???????? ?????????case?"1110"?{?$retval?=?"E"?} ?????????case?"1111"?{?$retval?=?"F"?}???????? ????????} ??????#??print?$chunk.?"?:?"?.?$retval?.?" "; ????????$str_hex?.=?$retval; ????} ???? ??#??print?$str_hex;???? ????return?$str_hex; }
關(guān)于腳本的使用,可以查看《Verilog數(shù)字系統(tǒng)基礎(chǔ)設(shè)計-CRC》。
仔細觀察比特流,能區(qū)分出同步和解同步命令、屬于Virtex-6 LX240T FPGA的IDCODE,以及兩個405個和243個字的幀。
-
FPGA
+關(guān)注
關(guān)注
1625文章
21620瀏覽量
601231 -
控制器
+關(guān)注
關(guān)注
112文章
16101瀏覽量
177063 -
比特流
+關(guān)注
關(guān)注
0文章
10瀏覽量
8101 -
Vivado
+關(guān)注
關(guān)注
19文章
804瀏覽量
66221
原文標(biāo)題:【Vivado那些事】簡談FPGA比特流結(jié)構(gòu)
文章出處:【微信號:Open_FPGA,微信公眾號:OpenFPGA】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論