【Vivado那些事】简谈FPGA比特流结构

【Vivado那些事】简谈FPGA比特流结构

比特流是一个常用词汇,用于描述包含FPGA完整内部配置状态的文件,包括布线、逻辑资源和IO设置。大多数现代FPGA都是基于SRAM的,包括Xilinx Spartan和Virtex系列。在FPGA上电或随后的FPGA重新配置期间,比特流从外部诸如闪存这样的非易失性存储器中读取,通过FPGA配置控制器的处理,加载到内部的配置SRAM中。

在有些情况下,设计者需要很好地了解FPGA比特流的内部结构。例如,使用FPGA物理实现工具的参数不能访问自定义的低级比特流、实现复杂的配置回溯方案、通过内部配置端口(ICAP)产生用于FPGA重新配置的短命令序列、读配置状态等。遗憾的是,逆向工程和篡改比特流以非法获取专利设计信息也属于这些使用范畴。

比特流格式

Xilinx FPGA的比特流结构如图1所示。

比特流包括以下组成部分:填充、同步字、用于访问配置寄存器的命令、存储器帧和解同步字。

填充

填充的数据是全0或全1序列,被FPGA配置控制器忽略。填充数据用于非易失性存储器中分离比特流。一般使用全1填充较为方便,因为执行擦除后闪存的状态一般也是全1。

同步字

同步字是一个特殊值(0xAA995566),通知FPGA配置控制器处理后续的比特流数据。

解同步字

解同步字通知FPGA配置控制器比特流的末端位置。解同步字之后,所有的比特流数据被忽略,直到遇到下一个同步字。

命令

命令用于读和写FPGA配置控制器寄存器。每个比特流中出现的一些命令,有的是ID-CODE,用于标识比特流属于哪个FPGA器件。帧地址寄存器(FAR)、帧数据寄存器(FDRI)和无操作(NOOP)将被忽略。

存储器帧

存储器帧是配置Xilinx FPGA的比特流基本单元。帧的大小与具体的FPGA系列有关,系列不同,帧的大小也不同。Virtex 6器件的帧有2592位。每个Virtex 6器件具有的帧数不同,从最小7491(对于LX75T)到最大55548(对于LX550T)。帧用于多个逻辑片、IO、BRAM及其他FPGA的配置。每帧都有一个地址,对应于FPGA配置空间的位置。比特流使用FAR和FDRI命令序列来配置帧。

Virtex 6 FPGA配置用户指南包含足够的关于比特流和访问FPGA配置控制器寄存器命令的文档。然而,关于存储器帧的详细文档不仅对于Xilinx FPGA无法获得,而且对于其他供应商的FPGA也是如此。

Xilinx的BITGEN实用程序

BITGEN是Xilinx的实用程序,利用本地电路描述(NCD)格式的布局布线后文件,创建用于FPGA配置的比特流。BITGEN是一个高度可配置的工具,具有100多个命令行选项(在命令行工具用户指南(Xilinx Command Line Tools User Guide,http://www.xilinx.com/support/documentation/sw_manuals/xilinxl 2_2/devref.pdf)中描述)。有些选项用于确定比特流输出格式、启用压缩处理减少比特流大小、提高FPGA配置速度、使用CRC来确保数据完整性、对比特流加密等。

示例

以下示例用于根据差异部分配置的短比特流, 通过脚本语言描述比特流命令。

#!/devl/perl/bin/perl

use Switch;

# -----------------------------------------------------------------------------
#  Copyright (C) 2011 OutputLogic.com 
#  This source file may be used and distributed without restriction 
#  provided that this copyright statement is not removed from the file 
#  and that any derivative work contains the original copyright notice 
#  and the associated disclaimer. 
#  
#  THIS SOURCE FILE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS 
#  OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 
#  WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
# -----------------------------------------------------------------------------
#
# A script to parse Xilinx FPGA bitstream in .RBT format
#
if ($#ARGV < 0)
{
    die "Usage: xilinx_bitstream_parser <rbt file>\n";
}

$verbose = 2;

open(RBT_FILE,"<$ARGV[0]")          || die "Error: can't open $ARGV[0] for input\n";

# 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 (<RBT_FILE>)
{
   $type = "";
   $op   = "";
   $reg  = "";
   chop;
   next if (! /^[01]/);   
   
   if (/^10101010100110010101010101100110/) {
       $type = "Sync word";
   }
   elsif (/^11111111111111111111111111111111/) {
       $type = "Dummy word";
   }
   elsif (/^00000000000000000000000010111011/) {
       $type = "Bus Width word ";
   }
   elsif (/^00010001001000100000000001000100/) {
       $type = "8/16/32 BusWidth";
   }
   elsif (/^001/) {
       $type = "Type 1";
   }
   elsif (/^010/) {
       $type = "Type 2";
   }
   elsif (/^00000000000000000000000000000000/) {
       $type = "NO OP";
       $wordcnt = 0;
   }
   else {
       if (($register eq "FDRI") || ($register eq "FDRO") || ($last_command eq "LFRM")) {
          $type = "Partial CRC word";
       }else{
          $type = "Type Unknown";
       }
   }
   
   if ($type eq "Type 1" || $type eq "Type 2")    {
      $s = substr($_,3,2);
      if ($opcode{$s} ne "") 
      {
          $op = $opcode{$s};
      }
      else 
      {
          $op = "Unknown";
      }
   }
   
   if ($type eq "Type 1") {
      $s = substr($_,14,5);
      if ($reg{$s} ne "") {
       $register = $reg{$s};
      }
      else {
          $register = "Unknown";
      }
      $wordcnt = &bin2dec(substr($_,21,11));
   }
   
   if ($type eq "Type 2"){
      $wordcnt = &bin2dec(substr($_,5,27));
   }
   
   $text = "$type";
   if ($op eq "read") 
   {
      $text .= " $op " . "$wordcnt words from " . "$register";
   }
   elsif (($op eq "write") || ($op eq "decrypt"))
   {
      $text .= " $op " . "$wordcnt words to " . "$register";
   }
   elsif ($op eq "no op") 
   {
      $text .= " NO OP";
   }
   printf ("%10s %s\n", &bin2hex($_), $text);


   # A little hack so we can see the next stream also
   if (($register eq "LOUT") && ($wordcnt > 1)) 
   {
      $in_LOUT++;
      push @LOUT_wordcnt, $wordcnt;
      $wordcnt = 0;
   }
    
    
    # start printing out everything
    for ($i=0; $i<$wordcnt && (($op eq "write") || ($op eq "decrypt") || ($op eq "read")) && ($_ = <RBT_FILE>); $i++) 
    {
        chop;

        if (($op eq "write") && ($register eq "FAR "))
        {
         $fa = $_;
        }
        
        if (($register eq "FDRI") || ($register eq "FDRO")) 
        {
            # printf ("%s %10s\n", "data word " . $i, $_ );

            # don't print all frame words
            
            if ($i == 0) 
            {
             printf ("%s %d...%d \n", "data words " , $i , $wordcnt-1);
            }
       }
       elsif ($register eq "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 %s Row %d Col %d Minor %d] \n", &bin2hex($_) , "Frame Address", $BlkType, $TopBot, $MajRow, $MajCol, $MinCol);        
                
                
          if ($this_fa < $last_fa) {
             printf ("ERROR: Frame Address going down (%d < %d)?\n", $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] \n", &bin2hex($_) , $register . " data word " . $i, $BlkType, $TopBot, $MajRow, $MajCol, $MinCol);            
            
        }
        elsif ($register ne "CMD ") 
        {
            printf ("%10s %s\n", &bin2hex($_) , $register . " data word " . $i);
            
        } 
        else 
        {
            $s = substr($_,27,5);
         
            if ($command{$s} ne "") 
            {
                $last_command = $command{$s};
             printf ("%10s %s\n", &bin2hex($_), $command{$s} . " command",  );
         }
            else
            {
             printf ("%10s %s\n", &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 . "\n";

        $str_hex .= $retval;
    }
    
  #  print $str_hex;    
    return $str_hex;
}

关于脚本的使用,可以查看《Verilog数字系统基础设计-CRC》。

仔细观察比特流,能区分出同步和解同步命令、属于Virtex-6 LX240T FPGA的IDCODE,以及两个405个和243个字的帧。

上诉脚本及比特流文件如下:

** 链接:https://pan.baidu.com/s/1nXknXYGbiDGx6e4YOb3zrg 提取码:open  **

参考资料

Xilinx UG360

Xilinx Command Line Tools User Guide

推荐阅读

【Vivado那些事】如何查找官网例程及如何使用官网例程

【Vivado使用误区与进阶】总结篇

【Vivado那些事】Vivado中常用的快捷键(二)其他常用快捷键

SystemVerilog数字系统设计_夏宇闻 PDF

图书推荐|ARM Cortex-M0 全可编程SoC原理及实现

1202年了,还在使用虚拟机吗?Win10安装Ubuntu子系统及图形化界面详细教程

Github 上有哪些优秀的 VHDL/Verilog/FPGA 项目

AD936x+ZYNQ搭建OpenWIFI

面试中经常会遇到的FPGA基本概念,你会几个?

Xilinx FPGA MIPI 接口简单说明

介绍一些新手入门FPGA的优秀网站

Vivado ML(机器学习) 2021尝鲜

推荐一些可以获取免费的国外的原版书籍(电子版)网站

FPGA 的重构

浅析FPGA局部动态可重构技术

ISP(图像信号处理)算法概述、工作原理、架构、处理流程

国产CPU概括

从电子游戏历史看IC发展的助推剂

80年代电子游戏及电脑游戏的发展历史

PCIe总线的基础知识

万字长文带你回顾电子游戏的七十多年历史(完整版)

FPGA中异步复位,同步释放的理解

OpenFPGA系列文章总结

用Verilog设计一个16 位 RISC 处理器

介绍一些新手入门FPGA的优秀网站(新增)
如何提高FPGA的工作频率

【Verilog】深入理解阻塞和非阻塞赋值的不同

简谈 Xilinx FPGA 原理及结构

【官方推荐】学习Zynq-7000的入门书单

【Vivado那些事】Xilinx 7系列时钟结构详解
Verilog数字系统基础设计-CRC

点击上方字体即可跳转阅读

  • 3
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OpenFPGA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值