头图

从本篇文章开始,就来记录一下ZYNQ的学习,本篇博客主要介绍ZYNQ是什么以及在vivado中创建一个ZYNQ工程的过程,其中的知识大部分来自于正点原子的ZYNQ教程,感兴趣的可以去看一下他们家的参考资料

ZYNQ简介

ZYNQ全称ZYNQ-7000 All Programmable SoC,即全可编程片上系统,将处理器的软件可编程性与FPGA的硬件可编程性整合,其本质特征在于它组合了一个双核ARM Cortex-A9处理器作为处理系统(PS)和一个传统的现场可编程门阵列(FPGA)逻辑部件作为可编程逻辑(PL)
因此在ZYNQ中,ARM Cortex-A9是一个应用级处理器,可以运行像Linux一样的操作系统,而可编程逻辑是基于Xilinx 7系列的FPGA架构,同时实现了AXI接口,使两个部分实现了高带宽低延迟的连接

SoC发展史
SoC发展史

PL部分

PL:Programmable Logic
ZYNQ的PL部分等价于Xilinx 7系列的FPGA,FPGA的基本结构由6个部分组成,如下图所示,即可编程输入/输出单元、基本可编程逻辑单元、嵌入式块RAM、丰富的布线资源、底层嵌入功能单元和内嵌专用硬核等
FPGA架构
其中Xilinx 7系列FPGA中的可编程逻辑单元称为CLB(Configurable Logic Block,可配置逻辑块),每个CLB里包含两个逻辑片(Slice),每个Slice由4个查找表、8个触发器和其他一些逻辑所组成的,CLB的示意图如下:
CLB示意图

  • 底层嵌入单元:底层嵌入单元的概念比较笼统,这里是指通用程度较高的嵌入式功能模块,比如PLL(Phase Locked Loop)、DLL(Delay Locked Loop)、DSP、CPU等,这些模块被越来越多嵌入到FPGA的内部,以满足不同场合的需求
  • 内嵌专用硬核:内嵌专用硬核主要指通用性相对较弱,不是所有的FPGA器件都包含硬件。在ZYNQ的PL端有一个数模混合模块——XADC,XADC是一个硬核,包含两个模数转换器、一个模拟多路复用器、片上温度和片上电压传感器等,因此可以利用这个模块监测芯片温度和供电电压,也可以测量外部的模拟电压信号

不同型号的ZYNQ的PL对应的FPGA型号如下图所示:
不同型号的ZYNQ对应的FPGA芯片

PS部分

PS:Processing System
ZYNQ实际上是一个以处理器为核心的系统,PL只是一个它的外设,ZYNQ包含了完整的ARM处理器系统,且处理器系统中集成了内存控制器和大量外设,使Cortex-A9处理器可以完全独立于可编程逻辑单元
 
而且实际上在ZYNQ中PL和PS两部分的供电电路是独立的,这样PS或PL部分不被使用的话就可以被断点
其实FPGA也可以用于搭建嵌入式处理器,比如Xilinx的MicroBlaze处理器或Altera的Nios II处理器,像这种使用FPGA的可编程逻辑资源搭建的处理称为软核处理器,它的优势在于处理器的数量以及实现方式的灵活性。但是ZYNQ中集成的是一个硬核处理器,它的优势是可以获得相对较高的性能。ZYNQ中的硬核处理器和软核处理器并不冲突,可以使用PL的逻辑资源搭建一个Microblaze软核处理器来和ARM硬核处理器协同工作。
 
ZYNQ处理器系统里并非只有ARM处理器,还有一组相关的处理资源,形成了一个应用处理器单元(APU),以ARM处理器为核心的应用处理单元(APU)可以集成丰富的外设,以外还有扩展外设接口,cache存储器,存储器接口,互联接口和时钟发生电路等
ZYNQ的PS示意图如下:
ZYNQ的PS部分

框图中的一些专用术语:
MIO(Multiplexing IO):PS 域可复用的 IO,因为此 IO 管脚的配置在一定范围内有灵活性
EMIO(Extensible MIO):对 MIO 的扩展, 将 PL 域的 IO 口直接连到 PS 域
GIC (General Interrupt Controller):通用的中断控制器
IRQ(Interrupt Request):中断请求
OCM(On Chip Memory):片上存储
DMA(Direct Memory Access):直接存储访问
MMU:内存管理单元
AXI(Advanced eXtensible Interface):是一个总线协议,ZYNQ的PS和PL互联使用的是AXI总线

PL与PS互联

PS和PL互联基于ARM的 AMBA3.0协议的AXI总线,最终的形式相当于PL块作为一个IP core挂载在AXI总线上,然后由PS调用
AXI:Advanced Extensible Interface(高级可扩展接口),属于AMBA接口
AMBA:Advanced Microcontroller Bus Architecture
AMBA接口
ZYNQ有三种AXI总线:

  • AXI-4:存储映射,支持突发传输,主要用于处理器访问存储器等需要指定地址的高速数据传输场景
  • AXI-4 Stream:类似FIFO,数据传输时不需要地址,在主从设备之间直接连续读写数据,主要用于视频,高速AD,DMA接口等需要高速数据传输的场合
  • AXI-4 Lite:为外设提供单个数据传输,主要用于访问一些低速外设中的寄存器

AXI有三种接口:

  • AXI-GP(General Purpose AXI,通用AXI):一个32位数据总线,适合PL和PS之间的中低速通信,接口时透传的不带缓冲,总共有4个通用接口:两个PS做主机,另外两个PL做主机

    • 用该接口可以访问PS中的片内外设
  • AXI-HP(High Performance Ports):四个高性能AXI接口,带有FIFO缓冲来提供批量读写操作,并支持PL和PS中的存储器单元的高速率通信,数据宽度为32位或64位,在所有四个接口PL都是做主机的

    • 用于PL访问PS上的存储器(DDR和On-chip RAM)
  • AXI-ACP(Accelerator Coherency Port):在PL和APU内的SCU之间的单个异步连接,总线宽度为64位,PL为主机,用于实现APU cache和PL的单元之间的一致性

在PS和PL之间主要通过一组9个AXI接口,每个接口有多个通道组成
PS和PL的AXI互联
上面的接口可以总结为:
AXI接口
上图中M代表PS为主机,S代表PS是从机

三种接口的吞吐量对比如下图所示:
三种接口的吞吐量对比

vivado使用

Vivado是赛灵思公司为其产品定制的集成开发环境,支持Block Design,Verilog,VHDL等多种设计输入方式,内嵌综合器和仿真器,可以完成从设计输入,综合适配,仿真到下载的完整FPGA设计流程
Vivado还集成了HLS(High Level Synthesis)工具,可实现直接使用C,C++,System C语言对Xilinx的FPGA器件进行编程,用户无需手动创建RTL,通过高层次综合生成HDL级的IP核,从而加速IP创建

Vivado开发流程:
Vivado开发流程

Vivado创建工程

我这里使用的vivado版本是2018.3
选择Create Project

设置工程名字和存储位置
设置工程名字和存储位置

选择RTL Project
如果要添加源文件则不勾选,如果创建完工程后再添加源文件则勾选
RTL Project

接下来就要选择开发的芯片型号
可以直接搜索ZYNQ芯片型号如下:

  • ZYNQ-7020:xc7z020clg400-2
  • ZYNQ-7010:xc7z010clg400-1

也可以像下面一样选择:
ZYNQ-7010
ZYNQ-7020

Vivado工程界面

在上面用vivado创建了一个新的工程,这里主要介绍一下vivado的工程界面
vivado的工程界面

  • 左边的是设计流程导航窗口,从上到下是FPGA的各个设计环节

    • PROJECT MANAGER:工程管理部分,可以添加文件,FPGA的原语模板工具,IP核等
    • IP INTEGRATOR:包含模块化图表形式的创建和打开,生成等管理
    • SIMULATION:包含FPGA设计的仿真功能相关
    • RTL ANALYSIS:包含工程的分析,以及设计的检查,以及设计的原理图逻辑关系的生成等
    • SYNTHESIS:FPGA的综合,是FPGA的最关键部分
    • IMPLEMENTATION:实现部分,完成FPGA的布局布线设计
    • PROGRAM AND DEBUG:FPGA生成配置文件,以及在线实时调试文件生成,下载,调试功能部分
  • 中间的PROJECT MANAGER-MicroPhase_test是工程文件窗口,查看工程的层次结构,编辑工程文件属性
  • 右边的Project Summary是主工作窗口,根据不同的layout有不同的显示内容
  • 下面的蓝色窗口是结果显示窗口,显示各个环节的执行结果,其中,TCL Console可以采用TCL 脚本指令进行软件交互

创建源文件

PROJECT MANAGER
从上到下依次是:

  • 工程设置
  • 添加源文件
  • 提供了一些文件模板
  • 给工程添加IP核

点击Add Sources即可创建各种源文件
也可以直接点击+号创建源文件:
创建源文件

在编写完Verilog代码后,选择RTL ANALYSIS
RTL ANALYSIS
对代码进行分析并将Verilog代码翻译成RTL原理图
同时在这一步中可以直接绑定引脚,设置完引脚后需要保存管脚约束文件,也可以综合后在新建约束文件设置引脚

关于这里的Open Elaborated Design和Run Synthesis的区别可以参考如下的博客:
Vivado中的Elaborate是做什么的?

接下来可以对代码进行综合(也可以在编写完Verilog代码后直接进行综合),点击Run Synthesis:
综合

在综合后弹出的综合完成的对话框中先不要选择Run Implementation,在绑定管脚后再进行 Implementation实现:
综合完成

接下来约束输入,分配管脚:
首先创建一个约束文件,点击sources窗口的+号,选择Add or create constraints
添加约束
点击Create File创建一个新的约束文件
创建约束文件
输入约束文件的名称,并完成约束文件的创建
约束文件命名
应当注意,vivado的约束文件是以.xdc为后缀的文本文件,存储的是一条条的xdc约束命令

分配管脚
或者在最右边
分配管脚
约束输入完毕后即可开始实现设计,点击Run Implementation
实现

在下载程序之前,首先要生成用于下载到器件中的比特流文件,文件后缀为.bit,点击Generate Bitstream(也可以先点直接生成bit文件包含了实现操作)
生成比特流

在完成后弹出的界面中选择打开硬件管理器 Open Hardware Mnager
点击Open target或Hardware子窗口中的Auto Connect按钮
连接下载器

点击Program Device并Program即可下载程序:
下载程序
下载程序

这样就完成了ZYNQ的FPGA的开发流程,可以总结为如下步骤:

graph TD
    A[新建工程] -->B[添加源文件]
    B --> C[编写Verilog代码]
    C --> E[综合]
    E --> F[添加约束文件]
    F --> G[实现]
    G --> H[生成比特流]
    H --> I[下载程序]
graph TD
    A[新建工程] -->B[添加源文件]
    B --> C[编写Verilog代码]
    C --> E[综合]
    E --> F[在分析中绑定管脚并保存为约束文件]
    F --> G[实现]
    G --> H[生成比特流]
    H --> I[下载程序]

ZYNQ-7010点灯

本节中通过一个简单的LED等闪烁来完成上面的流程,我使用的是微相的ZYNQ开发板,如下图所示:
微相ZYNQ-7010开发板
根据官方提供的原理图可知,微相ZYNQ开发板有三个LED等,有一个PS端的LED灯,还有两个PL端的LED等,如下图所示:
原理图中LED的连接

接下来创建一个顶层模块,用于;同时创建一个led模块,用于对开发板上的LED进行操作,Verilog代码如下:

module Top_level(
    input wire clk,
    input wire rst_n,
    output reg [1:0] led
    );

reg [27:0] cnt;
wire add_cnt;
wire end_cnt;

always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        cnt <= 'd0;
    end
    else if(add_cnt)begin
        if(end_cnt)
            cnt <= 'd0;
        else
            cnt <= cnt + 1'b1;
    end
end

assign add_cnt = 1;
assign end_cnt = add_cnt && cnt == 10_000_000 - 1;

always @(posedge clk or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        led <= 2'b10;
    end
    else if(end_cnt)begin
        led <= {led[0], led[1]};
    end
    else begin
        led <= led;
    end
end


endmodule

接下来按照上面的步骤走一遍即可,其中在约束文件(.xdc文件)添加约束
对于普通IO口只需约束引脚和电压:

  • 管脚约束:set_property PACKAGE_PIN "引脚编号" [get_ports “端口名称” ]
  • set_property IOSTANDARD "电压" [get_ports “端口名称” ]

其中端口名称是数组的话用{}括起来,端口名称要和源代码中的名字一致
对于点亮LED灯,约束文件编写如下:

create_clock -period 20.000 [get_ports clk]

set_property PACKAGE_PIN N18 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]

set_property PACKAGE_PIN P16 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]

set_property PACKAGE_PIN P15 [get_ports {led[0]}]
set_property PACKAGE_PIN U12 [get_ports {led[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[*]}]

vivado仿真

在进行功能仿真之前,可以先看一下FPGA的设计流程:
FPGA的设计流程
从上图中可以看出,在设计输入之后设计综合之前进行RTL级仿真,称为综合前仿真,也称为前仿真功能仿真,功能仿真主旨在于验证电路的功能是否符合设计要求,其特点是不考虑电路门延迟和线延迟,在完成一个设计的代码编写工作之后,可以直接对代码进行仿真,检验源代码是否符合功能要求,此时仿真的对象是HDL代码,因此可以比较直观的观察波形的变化,在设计的最初阶段发现问题
而在布局布线后进行的仿真称为布局布线后仿真,也称为后仿真时序仿真,时序仿真真实反映了逻辑的时延与功能,综合考虑电路的路径延迟与门延迟的影响,验证电路是否在一定时序条件下满足设计构想的过程,是否存在时序违规

 
vivado内部集成了仿真器Vivado Simulator,同时能够在设计流程的不同阶段运行设计的功能仿真和时序仿真,结果可以在vivado集成的波形查看器中显示,vivado还支持与诸如ModelSim等第三方仿真器的联合仿真。下面就说明在vivado进行仿真的具体步骤。

首先需要创建一个testbench,在vivado里的Simulation Sources中点击右键后选择Add Sources,如下图所示:
在vivado工程里添加testbench

在弹出的窗口中选择Add or create simulation sources,如下图所示:
创建仿真源文件

点击next后,在弹出的界面中点击Create File,如下图所示:
创建文件

在弹出的界面中输入文件名称,我这里是对上面的那个LED模块进行仿真,故命名为TB_LED,如下图所示:
对文件进行命名

创建完成后点击Finish即可,如下图所示:
结束创建

然后在弹出的模块定义窗口直接点击OK即可,如下图所示:
模块定义

接下来就需要编写testbench的代码,如下所示:

`timescale 1ns / 1ps
module TB_LED();

reg clk;
reg rst_n;

wire [1:0] led;

initial begin
    clk = 1'b0;
    rst_n = 1'b0;
    #200
    rst_n = 1'b1;
end

// generate clock signal
always #10 clk = ~clk;

// LED module
LED led_module(
    .clk(clk),
    .rst_n(rst_n),
    .led(led)
);
endmodule

然后在vivado左侧的窗口中选择Run Simulation并选择Run Behavioral Simulation
运行仿真

打开后的仿真界面的一些功能可以参考下面的这篇博客:
实验笔记——Vivado仿真模拟

创建IP核

在vivado工程左侧,选择IP Catalog,如下图所示:
选择IP Catalog

然后在弹出的窗口中输入想要的IP核的名称,这里我输入DDS,结果如下图所示:
搜索想要的IP核

我们选择其中的一个,会弹出Customize IP的窗口,如下图所示:
DDS IP核的Customize IP窗口

接下来就可以配置IP核的参数,参考IP核的datasheet进行配置即可

vivado工程的目录以及一些常用文件的存放位置

一个vivado工程包括以下文件和文件夹:

  • srcs:包含所有的源文件

    • sources:.v/.vhd/.vh/.sv(设计文件)、.xci/.xcix(IP文件)
    • constrs:.xdc(约束文件)
    • sim:.v/.vhd(仿真相关文件)
  • sim:包括仿真过程文件和仿真波形、结果等
  • runs:运行中生成的文件synth_1

    • synth:综合后的结果文件夹

      • .rpt:报告文件
      • .dcp/.edn:网表文件,记录设计的所有信息
      • .log:日志文件
    • impl:实现后的结果文件夹

      • .rpt:报告文件
      • .bit:生成的比特流文件
  • .xpr:工程项目文件
  • .jou:记录所有执行过的命令以及vivado交互中的信息,可用于定位、调试以及复现流程操作
  • .log:记录各个阶段详细的告警、错误等信息,可用于各个阶段的问题定义

在线逻辑分析仪的使用

传统的FPGA板级调试是由外接的逻辑分析仪连接到FPGA的控制管脚,然后由将内部信号引出到引脚IO上,进行板级调试,这种方法的缺点在于首先需要一个逻辑分析仪,而逻辑分析仪一般价格昂贵,且对于需要测试很多引脚时,使用外接的逻辑分析仪会比较繁琐。在线逻辑分析仪克服了以上的缺点,利用FPGA中的逻辑资源,将上述功能植入到FPGA的设计当中

使用在线逻辑分析仪的原理框图如所示。
在线逻辑分析仪的应用原理框图
其中待测设计(Design Under Test,DUT)就是用户逻辑,它和片内的在线逻辑分析仪都位于FPGA中,在线逻辑逻辑分析仪通过一个或多个探针(Probe)来采集希望观察的信号,然后通过片内的JTAG硬核组件,来讲捕获到的数据传送给下载器,进而上传到vivado IDE以供用户查看,同时vivado IDE也能够按照上述的数据路径,反向地向FPGA中的在线逻辑分析仪传送一些控制信息,可见在线逻辑分析仪不需要将待测信号引出至I/O上,也不需要电路板走线或探测点,不需要外部的逻辑分析仪,在vivado中就可以将在线逻辑分析仪添加到设计中,但是在线逻辑分析仪会占用一定数量的内部逻辑资源,如块RAM、查找表、触发器等

在vivado中,在线逻辑分析仪的功能称为集成逻辑分析器(Integrated Logic Analyzer,ILA),它以IP核的形式来加入到用户设计中,vivado提供了三种具有不同集成层次的插入ILA方法,如下所示:

  • 直接在HDL代码中例化一个ILA IP核,也被称为HDL实例化调试探针流程,这是集成层次最高的方法,ILA IP核可以在IP Catalog中找到,并对其进行配置,以符合所需的调试需求,这是最直接的方法,但是灵活性较差,在调试工作完成后还需要在HDL源代码中删除ILA IP核,然后重新综合并实现以生成最终的比特流
  • 在综合后的网表中,分别标记要进行调试观察的各个信号,然后通过一个简单的Setup Debug向导来设置各个探针和ILA IP核的工作参数,然后工具会根据用户设置的参数自动地生成各个ILA IP核,这个方法也被称为网表插入调试探针流程,在此流程中,用户不需要修改HDL源代码,并且能够单独控制每个ILA IP核以及每个探针,这样就提供了很大的灵活性,用户设置的调试信息会以Tcl XDC调试命令的形式保存到XDC约束文件中,在实现阶段,vivado会读取这些XDC调试命令。并在布局布线时加入这些ILA IP核,在调试工作完毕之后,用户就可以在综合后的网表删除ILA IP核,或者在XDC文件中删除调试命令,然后再对设计进行实现,以生成最终的比特流
  • 手动地在XDC约束文件中书写对应的Tcl XDC调试命令,在实现阶段工具会自动读取这些命令,并在布局布线时加入这些ILA IP核,在调试工作完毕之后,用户还需要在XDC约束文件中删除这些命令,然后实现最终的设计,这种方法的集成层次最低,一般不会采用这种方法

HDL实例化调试探针流程

这一流程需要在HDL源代码中实例化ILA IP核,首先点击Flow Navigator窗口的IP Catalog按钮,如下图所示:
IP Catalog
在打开的窗口中的搜索栏直接输入ILA,并双击ILA(Integrated Logic Analyzer):
Integrated Logic Analyzer
接下来就会弹出ILA IP核的配置界面,如下图所示:
ILA IP核的配置界面——Component Name
ILA IP核的配置界面——Probe_Ports(0..0)
其中Component Name一栏用于设置ILA IP核的名称,ILA IP核的具体配置分为两个子页面,分别是General OptionsProbe_Ports(0..0),General Options页面用于ILA IP核的总体设置,Probe_Ports(0..0)页面用于对每个探针的参数进行设计,两者可配置的内容如下:

  • General Options

    • Number of Probes:设置所需的探针数量,一般一个探针用于连接一个待测信号
    • Sample Data Depth:设置采样深度,在每个采样时钟下,ILA会将捕获到的探针信号的值送入RAM中,由于RAM的存储空间是有限的,因此此选项用于设置RAM最大存储多少个探针信号的值,在此保持其默认值1024,其数值越大,消耗的RAM资源也越多,其它选项保持默认即可
  • Probe_Ports(0..0)

    • Probe Width[1..4096]:设置探针宽度

配置完成后点击OK按钮即可,接下来就会弹出Generate Output Products框,保持默认设置并点击Generate即可,接下来vivado就会开始对该ILA IP核进行OOC综合:
创建ILA IP核
vivado的OOC(Out-of-Context)综合不同于全局(Global)综合方式,对于顶层设计,vivado使用自顶向下的全局综合方式,将顶层之下的所有逻辑模块都进行综合,但是设置为OOC方式的模块除外,它们独立于顶层设计而单独综合,通常在整个设计周期中,顶层设计会被多次修改并综合,但有些子模块在创建完毕之后不会因为顶层设计的修改而被修改,比如IP,因此它们被设置为OOC综合方式,OOC模块只会在综合顶层之前被综合一次,这样在顶层的设计迭代过程中,OOC模块就不必跟随顶层模块,而一次次的产生相同结果的多余综合了,因此OOC流程减少了设计的周期,可以保存和重用综合结果
OOC综合是一种自底向上的设计流程,默认情况下,vivado使用OOC的设计流程来综合OOC模块,OOC模块可以是来自IP Catalog的IP、来自vivado IP integrator的block design或顶层模块下手动设置为OOC方式的任何子模块,来自IP Catalog的IP就默认使用OOC的综合方式,比如上图中的Synthesis Options选项就设置为Out of Context Per IP,这些IP会在顶层的全局综合之前,单独进行OOC综合并生成输出产品(Generate Output Products),包括综合后的网表等各种文件,在对顶层进行综合时,OOC模块会被视为黑盒子,并且不会参与到顶层的综合中来,在综合之后的实现过程中OOC模块的黑盒子才会被打开,这是其网表才是可见的,并参与到全局设计的实现过程中来

OOC综合完毕的状态如下所示:
OOC综合完毕
此时在Source窗口已经出现了ILA IP核,如下图所示:
Source窗口出现的ILA IP核
此时由于还没有将其例化到顶层的HDL代码中,因此在层次结构上它与顶层并排,下面对其进行例化,在Source窗口中的IP Sources选项卡中双击ILA IP核的例化模板文本文件,可以找到例化的模板:
IP核的例化模板
代码如下所示:

ila_led your_instance_name (
    .clk(clk), // input wire clk


    .probe0(probe0), // input wire [0:0]  probe0  
    .probe1(probe1), // input wire [1:0]  probe1 
    .probe2(probe2) // input wire [27:0]  probe2
);

现在就以上面的点灯程序为例,对ILA IP核进行例化,代码如下,这里只给出了例化ILA IP核的代码,其它代码可以看一下上面给出的:

ila_led u_ila_led (
    .clk(clk), // input wire clk

    .probe0(rst_n), // input wire [0:0]  probe0  
    .probe1(led), // input wire [1:0]  probe1 
    .probe2(cnt) // input wire [27:0]  probe2
);

例化后就可以直接综合、实现最后生成比特流,然后就可以将比特流下载到FPGA中,并对信号进行在线观察,首先需要下载比特流,打开Hardware Manager,连接到开发板,并下载比特流,在弹出的窗口中,vivado会自动识别比特流文件和具有调试探针信息的.ltx文件
自动识别的比特流文件和.ltx文件
.ltx文件存储了调试探针的信息,用于传递给vivado IDE,它是从我们的设计中被提取出来的,通常调试探测文件是在实现过程中自动创建的,并位于和比特流文件相同的目录下,若实现后的设计包含了ILA IP核,则在下载比特流时,vivado会自动识别出.ltx文件,此时可以直接点击Program,vivado会自动打开ILA的调试窗口,如下图所示:
打开的ILA的调试窗口
如果Waveform窗口中显示的信号不全,可以点击Waveform-hw_ila_1窗口中的加号,来将所有的探针信号添加到波形窗口中,如果默认已经显示所有待观察的信号,不用重复添加,如下图所示:
添加信号到波形窗口中
此时直接点击触发采集信号的按钮,可以观察到此时信号的波形,如下图所示,同时采集到信号后还可以点击放大和缩小的图标来对波形进行放大和缩小:
点击触发按钮观察信号波形
波形是默认以十六进制显示的,可以点击cnt计数器,选择Radix,然后选择Unsigned Decimal,即可切换到无符号的十进制显示,如下图所示:
切换到无符号的十进制显示
触发条件也可以进行设置,ILA会将采集到的探针数据存放在RAM中,然后通过JTAG和下载器上传到vivado,触发实际上就是决定ILA会在什么时候将RAM中的探针值数据上传到vivado,当ILA检测到触发条件得到满足后,就会把RAM中的探针值数据上传到vivado,然后vivado将探针数据的波形显示出来,可以在Trigger Setup窗口添加触发条件,点击加号将cnt信号添加进来,如下图所示:
添加触发条件
根据Verilog程序中设置的计数值,为了方便led翻转状态的查看,这里将触发条件设置为cnt等于十进制数10_000_000 - 1,即当ILA检测到cnt计数器的值等于十进制10_000_000 - 1时将RAM中的数据上传到vivado,如下图所示:
设置触发条件
然后就可以开始进行触发动作了,波形窗口一共有四个触发动作,如下图所示:
四个触发动作
从左到右依次如下:

  • 自动触发开关:和“开始触发”按钮联合在一起使用。若打开了此选项,则在ILA开始运行触发(即点击了“开始触发”按钮)后,会不断地对触发条件进行检测,每次触发条件被满足时(即cnt计数到了9999999),都会将RAM中存储的所有的探针值数据上传到vivado,vivado上显示的波形也会随之不断更新,直到用户点击了“停止触发”按钮。若没有点击此选项,则在ILA开始运行触发(即点击了“开始触发”按钮)后,在检测到触发条件得到满足并完成了上传数据之后,就会停止触发,等待用户下一步的指令
  • 开始触发:它和“自动触发开关”按钮联合在一起使用,点击之后ILA就会开始进行触发操作
  • 立即触发:立即将当前ILA RAM中的数据上传到vivado,而不管触发条件是否得到满足
  • 停止触发:停止当前正在进行的触发活动

现在点击开始触发,而不打开自动触发开关,即可看到窗口中出现了波形:
窗口中触发更新的波形
将波形放大后即可看到cnt的具体值,如下图所示:
窗口放大后可以看到cnt的具体值
可见计数到某个值之后,led的状态就会发生跳变
调试工作完成后可以在原来的Verilog代码文件中删除或注释对ILA IP核的例化,然后重新综合并实现,以生成最终的比特流

网表插入调试探针流程

网表插入调试探针流程需要在综合后的网表中将要进行调试观察的各个信号标记mark_debug属性,然后通过Setup Debug向导来设置ILA IP核的参数,最后工具会根据参数来自动创建ILA IP核,可以在综合之后的网表中手动选择网络并点击mark_debug按钮,也可以在综合之前在HDL代码中为想要观察的reg或wire信号添加mark_debug综合属性,比如(* mark_debug = "true" *)reg [25:0] cnt ;,其中( mark_debug = "true" *)必须紧挨在变量声明的前面,这样在综合完成之后并打开综合后的设计时,cnt信号就自动被标记了Mark Debug属性,此外被添加了( mark_debug = "true" *)属性的reg或wire信号不会被工具优化掉

这里选择第二种方法,在HDL代码中添加综合属性,修改后的代码如下所示:

module LED_mark_debug(
    input clk,
    (* mark_debug = "true" *) input rst_n,
    (* mark_debug = "true" *) output reg [1:0] led
    );

(* mark_debug = "true" *) reg [27:0] cnt;
wire add_cnt;
wire end_cnt;

......

endmodule

添加了mark_debug属性之后就可以开始综合,综合完之后需要点击Flow Navigator窗口中的Open Synthesized Design按钮,如下图所示:
Open Synthesized Design
在综合后设计的窗口布局选择器中,选择Debug窗口布局:
选择Debug窗口布局
此时vivado会打开Netlist子窗口、Schematic子窗口以及Debug子窗口,其中Netlist子窗口和Schematic子窗口都可以用于标记要进行观察的信号,Debug子窗口用于显示并设置ILA IP核的各个参数,如下图所示:
打开的一些子窗口
在Debug子窗口中又包含两个选项卡Debug Cores和Debug Nets,这两个选项卡用于显示所有的已标记为mark_debug的信号,但Debug Cores选项卡是一个更加以ILA IP核为中心的视图,所有已标记为mark_debug的信号并且已经被分配到ILA探针的信号都会被显示在各个ILA IP核的视图树下,已标记为mark_debug的信号但是还没有被分配到ILA探针的信号被显示在Unassigned Debug Nets之下,当然也可以在其中查看和设置 ILA IP 核的各种属性和参数;而Debug Nets选项卡仅显示已标记为mark_debug的信号,但不显示ILA IP核,所有已标记为mark_debug的信号且已经被分配到ILA探针的信号都会被显示在Assigned Debug Nets下,已标记为mark_debug的信号但是没有分配到ILA探针的信号被显示在Unassigned Debug Nets下

(1)在综合后的网表中手动为信号添加mark_debug属性
如果未在HDL代码中书写(* mark_debug = "true" *)综合属性,则需要首先标记要进行观察的信号,在综合后的网表中,信号的名称可能会发生一定的变化,比如对于led信号,在Netlist子窗口中的Nets目录下,需要找到led_OBUF网络,点击后会发现右边的Schematic子窗口会自动高亮选择此网络,因为这两个子窗口的对象是交叉选择的,右键网络后可以选择Mark Debug,如下图所示:
手动添加mark_debug
也可以在Schematic子窗口中选择网络,然后右键Mark Debug,如下图所示:
在Schematic子窗口手动添加
在Debug子窗口中的Debug Nets选项卡的Unassigned Debug Nets目录下会出现标记后的网络,如果是在HDL代码中添加了mark_debug综合属性的信号,则会自动出现:
Debug Nets选项卡
为网络标记了mark_debug之后,就可以进行ILA IP核的配置了,点击Debug子窗口中的Setup Debug按钮,如下图所示:
Setup Debug
弹出Setup Debug向导后点击Next按钮即可:
Setup Debug向导
接下来需要选择用于采样待测信号的时钟域,vivado会自动识别出各个待测信号所属的时钟域并将其自动设定为其采样时钟,如下图所示:
设定时钟域
用户可以手动指定各个用于采样待测信号的时钟域,右键待测信号,选择Select Clock Domain,弹出Select Clock Domain窗口,如下图所示:
手动指定时钟域1
手动指定时钟域2
需要注意的是Setup Debug向导会为每个采样时钟生成一个单独的IP核,由于这里只有一个时钟,因此最后只会生成一个ILA IP核,设置完采样时钟后,点击next,接下来的页面用于设置ILA IP核的全局设置:
ILA IP核的全局设置
其中Sample of data depth用于设置采样深度,Input pipe stages用于设置待测信号和其采样时钟之间的同步级数,如果在上一个设置时钟域页面中,存在与其采样时钟之间是异步的待测信号,则为了避免亚稳态,此数值最好不要低于2,由于这里的待测信号的采样时钟是同步的,所以可以设置为0
再点击next后就会进入概览页面,确认后点击finish按钮即可:
最后的概览页面
在Debug子窗口中的Debug Cores选项卡中可以看到vivado已经添加了ILA IP核,且Unassigned Debug Nets目录下已经没有未分配的信号了,如下图所示:
Debug Cores选项卡
而网表中被比较为mark_debug的信号也变成了虚线,表示其完成了ILA IP核的分配,如下图所示:
信号变成了虚线
前面提到过在网表插入调试探针流程中用户设置的调试信息最终会以Tcl XDC调试命令的形式保存在XDC约束文件中,在实现阶段vivado会从XDC约束文件中读取这些XDC调试命令,并在布局布线时加入ILA IP核,此时我们做的所有的更改和设置还只是停留在电脑内存中,需要将其保存到硬盘的XDC约束文件中,以供vivado在实现阶段读取,点击工具栏的保存按钮:
保存约束文件
如果弹出Save Constraints对话框,询问用户将约束保存在哪个XDC约束文件中,这里只有一个约束文件,选择Select an existing file,然后点击OK按钮即可,不过有时也不会弹出这个对话框
此时打开约束文件后就会看到在用户约束的下面,vivado自动写入了用于debug的约束命令,如下图所示:
自动写入的用于debug的约束命令
在实现阶段,vivado会读取这些约束,并按照命令的参数来自动加入ILA IP核,到这里就成功将ILA IP核添加到了设计中,接下来实现并生成比特流,最后将比特流下载到FPGA中,以对信号进行在线观察,对信号进行在线观察的方法和之前的HDL实例化调试探针的一样,没什么区别,调试工作完毕后就可以删除代码中信号的mark_debug属性以及XDC文件中包含调试信息的Tcl文件,然后重新综合、实现并生成最终的比特流

师兄说的两个问题

之前有位师兄跟我说在学习FPGA开发前必须要知道两个问题,下面就对两个问题进行解答

原码、反码和补码

量化的概念以及fi(π, 1, 8, 3)的结果分析

fi函数用于构造定点数值对象,它的不同调用方式如下:

  • a = fi返回一个不带值的有符号fi对象,其字长为16位,小数长度为15位
  • a = fi(v)返回一个有符号fi对象,其值为v,字长为16位,具有最佳精度小数长度
  • a = fi(v, s)返回一个fi对象,其值为v,符号性为s,字长为16位,具有最佳精度小数长度
  • a = fi(v, s, w)返回一个fi对象,其值为v,符号性为s,字长为w
  • a = fi(v, s, w, f)返回一个fi对象,其值为v,符号性为s,字长为w,小数长度为f
  • a = fi(v, s, w, slope, bias)返回一个fi对象,其值为v,符号性为s,斜率为slope且偏置为bias
  • a = fi(v, T)返回一个fi对象,其值为v,数据类型为numerictype T

fi函数的输入变量如下:

  • v:值,可以为指定为标量、向量、矩阵或多维数组,返回的fi对象的值是量化为fi构造函数中指定的数据类型的输入v的值
  • s:fi对象的符号性,指定为数值或逻辑值1(true)或0(false),默认为1,1代表有符号数据类型,0代表无符号数据类型
  • w:fi对象的以位为单位的字长,默认为16,fi对象的字长限制为65535位
  • f:fi对象的以位为单位的小数长度,默认为15,如果不指定小数长度,则对于指定的值、字长和符号性,fi对象会自动使用可提供最佳精度的小数长度
  • slope:fi对象的定标的斜率,斜率偏置定标数的真实值为\( real - word\ value=(slope\times integer)+bias \)
  • bias:fi对象的定标的偏置
  • T:fi对象的数值类型属性

一些示例如下:

  • a = fi
    matlab运行后的结果如下:
    matlab运行结果1
    可见返回了一个不带值的有符号fi对象,其字长为16位,小数长度为15位
  • a = fi(pi)
    matlab运行后的结果如下:
    matlab运行结果2
    可见创建了一个有符号fi对象,其值为pi,字长为16位,具有最佳精度小数长度
  • a = fi(pi,0)
    matlab运行后的结果如下:
    matlab运行结果3
    可见创建了一个值为pi的无符号fi对象,仅指定fi对象的值和符号性时,该对象具有最佳精度小数长度,字长默认为16位
  • a = fi(pi,1,8)
    matlab运行后的结果如下:
    matlab运行结果4
    可见创建了一个有符号fi对象,其字长为8位,具有最佳精度小数长度,在例子中小数长度为5,因为有符号时值的整数部分需要3位来表示,如果采用的是b = fi(pi,0,8),即fi对象无符号,则只需2位来表示整数部分,剩下6位小数位,matlab运行后的结果如下:
    matlab运行结果5
  • a = fi(pi,1,8,3)
    matlab运行后的结果如下:
    matlab运行结果6
    可见创建了一个有符号fi对象,其值为pi,字长为8位,小数长度为3位
    这里我们可以使用bin函数,bin函数为fi对象的存储整数的无符号二进制表示,即b = bin(a)以无符号二进制格式返回fi对象a的存储整数作为字符向量,定点数可以表示为\( real-world\ value=2^{-fraction\ length}\times stored\ integer \),或者等效于\( real-world\ value=(slope\times stored\ integer)+bias \),其中存储整数是原始二进制数字,并假定二进制小数点位于字的最右侧
    需要注意的是,bin返回的是fi对象的存储整数的无符号二进制表示,要获取fi对象的真实值的二进制表示,请使用dec2bin
    bin函数仅有一个输入参数a,需要为fi对象

因此这里直接bin(a),matlab运行后的结果如下:
matlab运行结果7
可见运行结果为00011001,这里是无符号数,由于小数长度为3位,则整数长度为4位,为0011,即3,小数为001,即2的-3次方,为0.125,最接近3.1415926...


Kazusa
5 声望7 粉丝

梦里不觉秋已深,余情岂是为他人