利用Linux自动编译Vivado工程

工程文件下载:
链接:链接: https://pan.baidu.com/s/1y1xgfNEQGp7xq_6j1KbTEA 提取码: 5qfe
Github(推荐):https://github.com/qiweiii-git/qwi00_led

1 前言

1.1 为什么自动化编译FPGA

自动化编译的优势在于尽量少的关注编译过程,减少“编译时间”。这里的“编译时间”并不是工具编译代码花费的时间,实际上工具编译代码的时间并不能通过手动或自动写入编译命令来减少,只能通过优化布线策略或优化时序结构来减少,所以这里的“编译时间”指的是用户在编译上所关注的时间。在非自动情况下,用户可能需要如下过程来编译一份代码:打开软件,新建工程,选择相应器件,新建代码(添加代码),开始编译,在相应文件夹找到编译结果。而在自动化编译下,用户需要做的工作可能只是:运行编译脚本,在固定文件夹下找到编译结果。由此可知,用户需要手动操作的时间大大削减。

1.2 为什么推荐初学者首先熟悉自动化编译

在初学者学习FPGA时,由于对编译工具不太熟悉,而编译工具的正确操作又是至关重要的一步,因此往往在编译工具上浪费的时间会特别多。很多时候没有得到正确的结果往往都会怀疑是否是编译工具设置出错,这样的问题定位时间其实没有必要去浪费。在自动化编译下,我们只关注代码和编译脚本的编写,编译工作就交给编译工具自己去完成。将时间重点关注在代码本身,减少时间浪费,提高学习效率

1.3 自动化编译方法

在本文中,由于使用的是Xilinx器件,因此采用Xilinx推出的Vivado编译工具。在Windows主机下,安装Ubuntu虚拟机作为简易服务器,用于代码的编译。整体的编译结构为:Linux服务器(安装Vivado)+Windows(安装远程控制软件(SecureCRT等))。编译过程为:Linux服务器正常运行,Windows打开远程控制软件并连接Linux服务器,运行编译脚本

2 环境准备

2.1 Linux虚拟机

2.1.1 VMware软件安装

链接: https://pan.baidu.com/s/1l7CVfRLL4euiC2P73GpEzA 提取码: hhhj

2.1.2 Ubuntu安装

链接: https://pan.baidu.com/s/1SYa2zmjdTAdoamQrBDaX9w 提取码: wxdv

2.1.3 SSH服务开启
sudo apt-get install openssh-server
sudo ps -e | grep ssh
sudo service ssh start

2.2 Vivado软件

2.2.1 Vivado软件下载

链接: https://pan.baidu.com/s/1rAdoIKyGjkLYpmnb0kcyqQ 提取码: pidg

2.2.2 Vivado运行环境变量
VIVADO_VER=2015.4
export VIVADO="/opt/Xilinx/Vivado/${VIVADO_VER}"
VIVADO_PATH="${VIVADO}/bin"
PATH="${PATH}:${VIVADO_PATH}"

2.3 SecureCRT软件

2.3.1 SecureCRT软件安装

链接: https://pan.baidu.com/s/1o_7xB_bv0uNEm4WHaC-gjg 提取码: gc92

3 Vivado工程

本例子工程将运行最简单的LED显示。
LED显示一共需要2个源代码文件,1个是顶层封装模块,1个是功能实现模块,代码的具体功能非常简单。本文关注于自动化编译过程,因此代码方便不会涉及太多,有兴趣者可参考后续更新。

3.1 LED显示代码

  • 顶层封装模块
    顶层封装模块只用于连接外部输入输出管脚与内部模块,并进行工程级的参数设置。
module qwi00_led
(
   input                      CLK                             ,
   output     [LED_NUM-1:0]   LED
);

//*****************************************************************************
// Parameters
//*****************************************************************************
parameter                     LED_NUM         = 1             ;
parameter                     LED_TIME_S      = 1             ;

//*****************************************************************************
// Maps
//*****************************************************************************
led_indicate
#(
   .LED_NUM                   ( LED_NUM                      ),
   .LED_TIME_S                ( LED_TIME_S                   )
)
u_led_indicate
(
   .clk                       ( CLK                          ),
   .led                       ( LED                          )
);

endmodule
  • 功能实现模块
    LED显示功能在led_indicate模块中实现。
module led_indicate
#(
   parameter                  LED_NUM         = 4             ,
   parameter                  LED_TIME_S      = 1
)
(
   input                      clk                             ,
   output     [LED_NUM-1:0]   led
);

//*****************************************************************************
// Parameters
//*****************************************************************************
parameter                     ICLK_FREQ       = 50_000_000    ;
parameter                     TIME_1S_BIT     = 26            ;//1.342s

//*****************************************************************************
// Signals
//*****************************************************************************
genvar                        i                               ;
wire                          iclk                            ;
reg           [LED_NUM-1:0]   oled                  = 0       ;
reg           [31:0]          time_cnt[LED_NUM-1]             ;

//*****************************************************************************
// Processes
//*****************************************************************************
//interface
assign iclk = clk;
assign led = oled;

//led process
generate
   for (i = 0; i < LED_NUM; i++)
   begin: led_gen
      always @(posedge iclk)
      begin
         if(time_cnt[i] == TIME_1S_BIT-1+LED_TIME_S+i)
            time_cnt[i] <= 0;
         else
            time_cnt[i] <= time_cnt[i] + ~&time_cnt[i];
      end

      always @(posedge iclk)
      begin
         if(time_cnt[i] == TIME_1S_BIT-1+LED_TIME_S+i)
            oled[i] = ~oled[i];
      end
   end
endgenerate

endmodule

3.2 管脚约束代码

除了功能源代码外,我们还需要管脚约束代码,这样的话才可以将代码中的输入输出真正与芯片管脚对应起来.

set_property PACKAGE_PIN U18 [get_ports CLK]
set_property PACKAGE_PIN M14 [get_ports LED]

set_property IOSTANDARD LVCMOS33 [get_ports CLK]
set_property IOSTANDARD LVCMOS33 [get_ports LED]

在本章中,我们一共得到了3份代码,他们的存放位置如下:

扫描二维码关注公众号,回复: 12458043 查看本文章
build
   constraints
      pinout.xdc
source
   qwi00_led.sv
   led_indicate.sv

4 TCL脚本

4.1 本脚本需要做到的事情

本脚本的目的在于将我们需要在vivado中进行的操作通过脚本的形式实现,如综合布线等,在实际编译过程中,只需要运行本脚本即可完成所需操作。

4.2 与Vivado编译相关的tcl脚本

创建工程:

create_project PRJ_NAME -part DEVICE

添加工程文件:

add_files ../source/NAME.sv

添加约束文件:

add_files -fileset constrs_1 -norecurse ./constraints/pinout.xdc

综合:

launch_runs synth_1 -jobs 2

布局布线:

launch_runs impl_1 -jobs 2

生成bit文件:

launch_runs impl_1 -to_step write_bitstream -jobs 2

4.3 脚本创建

4.3.1 run.tcl脚本

执行Run $buildName $chipType 即可编译代码。
如Run qwi00_led xc7z020clg400-2

proc Run {
    
     buildName chipType } {
    
    
   # Set CPU count
   set cores 1
   if {
    
    ![catch {
    
    open "/proc/cpuinfo"} f]} {
    
    
      set coreNum [regexp -all -line {
    
    ^processor\s} [read $f]]
      close $f

      if {
    
    $coreNum > 0} {
    
    
         set cores $coreNum
      }
   }

   # create project
   create_project $buildName -part $chipType

   # add working path
   set current_path [pwd]

   # add source file to project
   source ./file_list.tcl

   # set top
   set_property top $buildName [current_fileset]
   update_compile_order -fileset sources_1

   # synthesize
   reset_run synth_1
   launch_runs synth_1 -jobs $cores
   wait_on_run synth_1

   # Generate the HDF for the SDK.
   file mkdir ./$buildName.sdk
   write_hwdef -force -file ./$buildName.sdk/$buildName.hdf

   # implement
   launch_runs impl_1 -jobs $cores
   wait_on_run impl_1

   # Generate the bitstream.
   launch_runs impl_1 -to_step write_bitstream -jobs $cores
   wait_on_run impl_1
}

# Run $buildName $chipType
# Run qwi00_led xc7z020clg400-2
4.3.2 file_list.tcl脚本
#add constraints
add_files -fileset constrs_1 -norecurse ./constraints/pinout.xdc

#add source
add_files ../source/qwi00_led.sv
add_files ../source/led_indicate.sv

file_list.tcl脚本主要作用是添加工程代码文件,在开发过程中,工程代码文件可能不断添加或删除,格外创建file_list.tcl便于维护。

在本章中,我们一共得到了2份tcl脚本,他们的存放位置如下:

build
   file_list.tcl
   run.tcl

5 Shell脚本

5.1 本脚本需要做到的事情

本脚本的目的在于将诸如调用vivado、将vivado编译后的bit文件提取至文件夹等操作通过脚本的形式实现。在实际应用过程中,只需要运行本脚本即可实现vivado调用、编译(编译过程使用4中创建的脚本)、bit文件提取等操作。

5.2 脚本关键代码

5.2.1 文件夹指示

文件夹指示用于指示当前工程目录,bit文件存在位置等信息。

BuildDir='.build'
BitFileDir='impl_1/'
CopyBitRoute='../../../..'

.build为编译代码时的临时文件夹(为了不影响原始数据)。
除此之外,其余的文件夹指示,如生成的bit文件复制到哪里通过调用该shell时指定。

PrjName=$1
ChipType=$2
BuildFileDir=$3
CopyBitFileDir=$4
CopyHdf=$5
5.2.2 运行vivado编译(编译过程使用4中创建的脚本)
vivado -mode batch -source run.tcl
5.2.3 bit文件移动
cp $PrjName.bit $CopyBitRoute/$CopyBitFileDir
5.2.4 删除工程编译文件夹
rm -rf $BuildDir

5.3 脚本创建

5.3.1 makefw.sh脚本
#!/bin/bash

BuildDir='.build'
BitFileDir='impl_1/'
CopyBitRoute='../../../..'

PrjName=$1
ChipType=$2
BuildFileDir=$3
CopyBitFileDir=$4
CopyHdf=$5
echo "Info: Project Name is $PrjName, ChipType is $ChipType"

#make dir
if [ -d $BuildDir ]; then
   echo "Warning: Building Directory $BuildDir Exist"
   rm -r $BuildDir
   echo "Info: Old Building Directory $BuildDir Removing"
fi
mkdir $BuildDir
echo "Info: Building Directory $BuildDir Establish"

#copy source files
cp * $BuildDir -r

#building
cd $BuildDir/$BuildFileDir
echo "Run $PrjName $ChipType" >> run.tcl
echo "Info: $PrjName $ChipType is building"
vivado -mode batch -source run.tcl

#finish building
echo "Info: $PrjName Project finish building"

#copy the bit file
cd $PrjName.runs/$BitFileDir
if [ -f $PrjName.bit ]; then
   cp $PrjName.bit $CopyBitRoute/$CopyBitFileDir
   echo "Info: $PrjName bit file moved to BIN"
   if [ $CopyHdf -eq 1 ]; then
      cp $CopyBitRoute/$BuildDir/$BuildFileDir/$PrjName.sdk/$PrjName.hdf $CopyBitRoute/$CopyBitFileDir
      echo "Info: $PrjName hdf file moved to BIN"
   fi
   #clean
   cd $CopyBitRoute
   rm -rf $BuildDir
   echo "Info: $PrjName finish building"
   echo -e "\n   Success \n"
else
   echo "Error: $PrjName Project built failed"
   echo "Error: $PrjName Project make failed"
   echo -e "\n   Failure \n"
fi
5.3.2 makeall.sh脚本
#!/bin/bash

PrjName='qwi00_led'
ChipType='xc7z020clg400-2'

MakeFwFileDir='make'
BuidFwFileDir='build'
OutpFwFileDir='bin'

if [ ! -d $OutpFwFileDir ]; then
   mkdir $OutpFwFileDir
   echo "Info: $OutpFwFileDir created"
fi

./$MakeFwFileDir/makefw.sh $PrjName $ChipType $BuidFwFileDir $OutpFwFileDir 0

实际运行工程中,只需要运行makeall文件即可,makeall会调用makefw脚本进行编译,增加makeall脚本的原因为便于后期进行软件编译时,只需要增加makesw,然后继续通过makeall调用的方式进行。

在本章中,我们一共得到了2份shell脚本,他们的存放位置如下:

make
   makefw.sh
makeall.sh

至此,所有工程所需文件已准备好(4个目录6个文件)。

build
   constraints
      pinout.xdc
   file_list.tcl
   run.tcl
make
   makefw.sh
source
   qwi00_led.sv
   led_indicate.sv
makeall.sh

6 自动化编译

本章将联合上述所有章节进行实际自动化编译操作。

6.1 Secure CRT软件连接Ubuntu

  • 查看Ubuntu中ssh服务状态。
ps -e | grep ssh

ssh
这证明ssh已经在运行了,进程名为sshd.。

  • 在Secure CRT中连接Ubuntu
    查看Ubuntu的IP地址。
ifconfig

ifconfig

  • 连接Ubuntu。
    securtcrt
  • 输入密码后连接
    在这里插入图片描述
    在Secure CRT正常连接Ubuntu后可以完全不用再管Ubuntu,直接放入后台。

6.2 将本地工程上传至Ubuntu

使用FileZilla软件或直接复制文件夹等方式上传工程至Ubuntu。
upload

6.3 Secure CRT进入工程目录运行工程

6.3.1 进入工程目录

在这里插入图片描述

6.3.2 打开脚本执行权限
chmod 777 makeall.sh && chmod 777 ./make/makefw.sh
(6.3.3) 定位到交叉编译器。
source /opt/Xilinx/SDK/2015.4/settings64.sh
6.3.4 运行脚本

运行makeall脚本。

./makeall.sh

在这里插入图片描述

6.3.5 编译完毕

等待一段时间后,编译完毕。
在这里插入图片描述
此时bin目录下已有编译后的bit文件。
在这里插入图片描述

7 总结

至此,利用Linux自动编译Vivado工程已完成,在实际应用中,无需关注Vivado界面,在代码实现后可直接运行脚本进行编译。

猜你喜欢

转载自blog.csdn.net/u011239266/article/details/96578579