文章目录

  • 前言
  • 提高设计的自测性
    • 增加测试管脚
    • 状态寄存器集
    • 虚拟示波器
      • ChipScope&SignalTap
      • 自己编写VirtualScope
    • 编写激励发生测试模块

前言

本文节选自《FPGA之道》。

提高设计的自测性

也许在FPGA设计的板级测试之前,我们已经做了充分的功能和时序仿真,但是仿真毕竟是仿真,它与实际情况之间还是或多或少的存在一定的差距,因此基于FPGA的整个硬件系统必须进行充分的实际上电测试才行。可是,搭建一套完整的硬件测试系统并不是一件容易的事情,即便是费了很大的力气,硬件测试系统已经搭建完毕,但是如果一运行,什么结果也没有或者行为紊乱没有规律,那么也不好定位和修改问题。因此,如果在HDL设计时,适当考虑让FPGA设计自身具有一定的自测功能,那么将会为后期的硬件级调试带来非常大的帮助。
当然了,提高设计的自测性应该遵循一个原则,那就是尽可能少的增加FPGA设计的资源负担,否则,资源的大规模膨胀会极大提升编译器的布局布线难度,降低设计的成功率,进而导致得不偿失。
下面,我们就来介绍一些常用的提高设计自测性能的方法:

增加测试管脚

最简单的增加FPGA设计自测性的方法就是增加测试管脚。即,将一些设计内部的关键信号直接通过芯片的空闲管脚输出,这样在硬件电路中就可以方便的使用万用表、示波器甚至逻辑分析仪来进行监测。不过这种方法会在原始信号上引入不确定的线延迟(FPGA内部及外部),因此在判断多个信号之间的相对位置时需要考虑这一因素。
增加测试管脚的具体做法很简单,就是在HDL中,将需要测试的信号从其产生的模块开始,一层层的输出出来,直至顶层模块,并在对应的管脚约束文件中为其添加相应的管脚分配。当然了,也需要在后续的电路板设计时对其进行硬件连接设计,在板子上放置可达的测试触点。

状态寄存器集

在进行功能仿真的时候,我们可以将模块内部任意一个寄存器或者线网的取值变化情况拿出来观察,并以此作为依据来判断问题的根源。而到了时序仿真的时候,你会发现几乎无法找不到待测模块内部的那些寄存器和线网,因为它们都被编译器优化过了,并且名字也都进行了修改,因此时序仿真时如果出现了问题,是很难定位的。最后,到了板级硬件实测的时候,你根本无法去窥探FPGA内部任何单元的取值情况,因为类似万用表、示波器这样的测试仪器最多只能达到FPGA芯片的管脚部分。
由此可见,随着理论一步步向实际靠近,我们所能利用查找问题的线索也变得越来越少,但是问题仍然可能出现,所以排查问题的工作也变得越来越艰巨。要想让问题排查变得容易,没有别的办法,只能是想办法获取更多关于系统内部的信息,而对于板级硬件实测来说,除了充分利用FPGA芯片的输出端口外也别无他法。因此,可以修改设计,将其中一些关键功能模块的一些关键信息(例如状态、标志位等)通过FPGA芯片的输出端口发送到FPGA芯片的外部,从而可以让我们得到更多的有用信息。
通常来说,FPGA内部关键的信息不止一个,因此,可以针对这些信息建立一个状态寄存器的集合,里面存储了所有我们关心的关键信息,并能够完成定时刷新,这样一来,只需要建立一个从FPGA外界对这部分寄存器的读取机制,就可以轻松获取很多FPGA芯片内部的信息。
不过有一点需要注意,切勿直接将HDL所描述的FPGA内部某一状态机的状态寄存器的内容存储到状态寄存器集中,这是因为状态机的状态定义大多是概念层级的定义,编译器很可能会对其进行重新编码(除非使能user选项),所以在HDL中是不建议直接使用状态机的状态进行非状态机相关的代码设计的。
下面给出一个关于状态寄存器集的HDL示例:

-- VHDL example
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;entity stateRegArray is
port (extClk : in  std_logic;addr : in  std_logic_vector(1 downto 0);stateSel : out std_logic_vector(7 downto 0);mod0En : in  std_logic;mod0StateData : in std_logic_vector(7 downto 0);mod1En : in  std_logic;mod1StateData : in std_logic_vector(7 downto 0);mod2En : in  std_logic;mod2StateData : in std_logic_vector(7 downto 0);mod3En : in  std_logic;mod3StateData : in std_logic_vector(7 downto 0)
);
end stateRegArray;architecture Behavioral of stateRegArray istype arrayType is array(3 downto 0) of std_logic_vector(7 downto 0);signal stateArray : arrayType;
beginprocess(extClk)beginif(extClk'event and extClk = '1')thenstateSel <= stateArray(conv_integer(addr));if(mod0En = '1')thenstateArray(0) <= mod0StateData;end if;if(mod1En = '1')thenstateArray(1) <= mod1StateData;end if;if(mod2En = '1')thenstateArray(2) <= mod2StateData;end if;if(mod3En = '1')thenstateArray(3) <= mod3StateData;end if;end if;end process;
end Behavioral;entity mod0 is
port (inerClk : in  std_logic;mod0En : out  std_logic;mod0StateData : out std_logic_vector(7 downto 0)
);
end mod0;architecture Behavioral of mod0 is     signal counter : std_logic_vector(3 downto 0);signal importantSignal : std_logic_vector(7 downto 0);
beginmod0En <= '1' when counter > 5 and counter < 10 else '0';process(inerClk)beginif(inerClk'event and inerClk = '1')thencounter <= counter + 1;if(counter = 0)thenmod0StateData <= importantSignal;end if;end if;end process;-- importantSignal are generate below-- ......end Behavioral;-- mod1, mod2, mod3 would define following
-- ......
// Verilog examplemodule stateRegArray(input extClk,input [1:0] addr,output reg [7:0] stateSel,input mod0En,input [7:0] mod0StateData,input mod1En,input [7:0] mod1StateData,    input mod2En,input [7:0] mod2StateData,input mod3En,input [7:0] mod3StateData);reg [7:0] stateArray[3:0];always@(posedge extClk)beginstateSel <= stateArray[addr];if(mod0En == 1'b1)beginstateArray[0] <= mod0StateData;endif(mod1En == 1'b1)beginstateArray[1] <= mod1StateData;endif(mod2En == 1'b1)beginstateArray[2] <= mod2StateData;endif(mod3En == 1'b1)beginstateArray[3] <= mod3StateData;endendendmodulemodule mod0(input inerClk,output mod0En,output reg [7:0] mod0StateData);reg [3:0] counter;reg [7:0] importantSignal;assign mod0En = ((counter > 4'd5) && (counter < 4'd10))? 1'b1 : 1'b0;always@(posedge inerClk)begincounter <= counter + 1'b1;if(counter == 4'b0)beginmod0StateData <= importantSignal;endend // importantSignal are generate below// ......endmodule// mod1, mod2, mod3 would define following// ......

从上述示例可以看出,为了解决跨时钟域问题,mod0模块中的数据mod0StateData稳定16个内部时钟周期,但mod0En确仅在mod0StateData稳定的中间4个内部时钟周期有效。这样一来,只要保证4个内部时钟周期大于1个外部时钟周期,那么mod0的内部状态数据就必然可以正确被stateRegArray采样。这便是【本篇->编程思路->时钟及时钟域->跨时钟域问题->握手法】小节中介绍的思路。

虚拟示波器

如果说状态寄存器集的方法类似于用万用表去测量电路电压的方式,即它仅适合观察一些变化缓慢或不需要连续记录的信息,那么对于一些变化非常快或需要连续记录的信息,自然连线到采用类似示波器的形式。没错,本章节就来介绍利用虚拟示波器技术提高设计自测性的方法。

ChipScope&SignalTap

在FPGA的集成开发环境中,一般都集成了类似的测试工具,例如Xilinx公司的ChipScope系列调试工具和Altera公司的SignalTap系列的调试工具。这些工具能够较为方便的帮助我们完成硬件级的在线调试,其使用方法一般也比较简单,通过调用相应功能的IP核并完成信号观测配置,重新编译工程,完成好硬件连接,最后即可使用该在线调试工具完成信号的触发、采集和显示等等。
当然了,要想使用这些在线调试工具,也是需要付出一定代价的。首先,这需要在我们的FPGA设计中添加相应的逻辑功能,因此需要耗费一定的资源。其次,这些工具一般都是通过JTAG接口来完成数据的回传,因此采集能力会受一定的限制。第三,如果希望在成品产品中使用这一测试工具,必须设计专门的硬件接口。最后,这些工具不一定都是免费的,而且你的测试环境离不开安装完成这样工具的一台电脑。

自己编写VirtualScope

仿照ChipScope以及SignalTap的在线调试思路,我们也可以自定义设计出更适合自己的虚拟示波器——VirtualScope。相比于ChipScope、SignalTap这类现成的调试工具,自己设计的VirtualScope具有以下优点:
首先,VirtualScope可以利用系统现成的高速硬件数据接口来进行在线调试,无需引入额外硬件接口或者连线。
其次,VirtualScope可以利用系统现成的显示设备来显示采集到的FPGA内部数据,无需引入额外的显示设备。
第三,VirtualScope作为FPGA项目的一部分集成于整个设计之中,因此可以比较方便的在调试模式和正常工作模式之间进行切换。
最后,“自己动手、丰衣足食”,在FPGA设计中加入自己编写的VirtualScope模块是完全开源和免费的,并且实现起来也不复杂。
下面,就给出一个VirtualScope模块的HDL示例:

-- VHDL example
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;entity VirtualScope is
port (extClk : in  std_logic;extRst : in  std_logic;sampleFlag : in  std_logic;paraChangeEn : in  std_logic;source : in std_logic_vector(1 downto 0);sampleNum : in std_logic_vector(9 downto 0);waveEn : out  std_logic;waveform : out  std_logic_vector(7 downto 0);innerClk : in  std_logic;innerRst : in  std_logic;mod0En : in  std_logic;mod0Data : in std_logic_vector(7 downto 0);mod1En : in  std_logic;mod1Data : in std_logic_vector(7 downto 0);mod2En : in  std_logic;mod2Data : in std_logic_vector(7 downto 0);mod3En : in  std_logic;mod3Data : in std_logic_vector(7 downto 0)
);
end VirtualScope;architecture Behavioral of VirtualScope iscomponent AsynFifo
port (
rst: in std_logic;
wr_clk: in std_logic;
rd_clk: in std_logic;
din: in std_logic_vector(7 downto 0);
wr_en: in std_logic;
rd_en: in std_logic;
dout: out std_logic_vector(7 downto 0);
full: out std_logic;
empty: out std_logic);
end component;signal rd_en, wr_en : std_logic;
signal empty : std_logic;
signal din : std_logic_vector(7 downto 0);
signal sourceLock : std_logic_vector(1 downto 0);
signal sampleNumLock : std_logic_vector(9 downto 0);
signal sampleFlagLock : std_logic;
signal startSample : std_logic;
signal modXEn : std_logic;
type stateType is (s_idle, s_sample);
signal state, nextState : stateType;
signal counter, nextCounter : std_logic_vector(9 downto 0);begin-- fifo instatiation, fifo depth must be bigger than sampleNum
m0 : AsynFifoport map (rst => extRst,wr_clk => innerClk,rd_clk => extClk,din => din,wr_en => wr_en,rd_en => rd_en,dout => waveform,empty => empty);
-- read data out of fifo and send to ext
-- may need doing something to make sure the ext sample the data right
-- ......
rd_en <= not empty;
process(extClk)
beginif(extClk'event and extClk = '1')thenwaveEn <= rd_en;end if;
end process;-- parameter passed from ext to inner
-- handshake must be introduced for safety
process(innerClk)
beginif(innerClk'event and innerClk = '1')thenif(paraChangeEn = '1')thensourceLock <= source;sampleNumLock <= sampleNum;end if;sampleFlagLock <= sampleFlag;end if;
end process;-- source select for sample
process(sourceLock, mod0En, mod1En, mod2En, mod3En, mod0Data, mod1Data, mod2Data, mod3Data)
begincase (sourceLock) is when "00" =>modXEn <= mod0En;din <= mod0Data;when "01" =>modXEn <= mod1En;din <= mod1Data;when "10" =>modXEn <= mod2En;din <= mod2Data;when "11" =>modXEn <= mod3En;din <= mod3Data;when others =>modXEn <= mod0En;din <= mod0Data;end case;
end process;-- sample data and store into fifo
startSample <= sampleFlagLock xor sampleFlag;process(innerClk)
beginif(innerClk'event and innerClk = '1')thenif(innerRst = '1')thenstate <= s_idle;counter <= (others => '0');elsestate <= nextState;counter <= nextCounter;end if;end if;
end process;process(state, startSample, counter, modXEn, sampleNum)
beginwr_en <= '0';case (state) is -- wait sample flagwhen s_idle =>         nextCounter <= (others => '0');if(startSample = '1')thennextState <= s_sample;elsenextState <= s_idle;end if;-- sampling     when s_sample =>if(modXEn = '1')thennextCounter <= counter + 1;elsenextCounter <= counter;end if;wr_en <= modXEn;if(nextCounter >= sampleNum)thennextState <= s_idle;elsenextState <= s_sample;end if;when others =>nextState <= s_idle;end case;
end process;end Behavioral;
// Verilog example
module VirtualScope(
input extClk,
input extRst,
input sampleFlag,
input paraChangeEn,
input [1:0] source,
input [9:0] sampleNum,
output reg waveEn,
output [7:0] waveform,input innerClk,
input innerRst,
input mod0En,
input [7:0] mod0Data,
input mod1En,
input [7:0] mod1Data,
input mod2En,
input [7:0] mod2Data,
input mod3En,
input [7:0] mod3Data
);wire rd_en;
reg [7:0] din;
reg wr_en;
wire empty;
reg [1:0] sourceLock;
reg [9:0] sampleNumLock;
reg sampleFlagLock;
wire startSample;
reg modXEn;
parameter s_idle = 0, s_sample = 1;
reg state, nextState;
reg [9:0] counter, nextCounter;// fifo instatiation, fifo depth must be bigger than sampleNum
AsynFifo m0(.rst(extRst),.wr_clk(innerClk),.rd_clk(extClk),.din(din), // Bus [7 : 0] .wr_en(wr_en),.rd_en(rd_en),.dout(waveform), // Bus [7 : 0] .empty(empty));// read data out of fifo and send to ext
// may need doing something to make sure the ext sample the data right
// ......
assign rd_en = ~ empty;
always@(posedge extClk)
beginwaveEn <= rd_en;
end// parameter passed from ext to inner
// handshake must be introduced for safety
always@(posedge innerClk)
begin if(paraChangeEn == 1'b1)beginsourceLock <= source;sampleNumLock <= sampleNum;endsampleFlagLock <= sampleFlag;
end // source select for sample
always@(sourceLock, mod0En, mod1En, mod2En, mod3En, mod0Data, mod1Data, mod2Data, mod3Data)
begincase (sourceLock)2'b00: beginmodXEn = mod0En;din = mod0Data;end2'b01: beginmodXEn = mod0En;din = mod1Data;end2'b10: beginmodXEn = mod0En;din = mod2Data;end2'b11: beginmodXEn = mod0En;din = mod3Data;enddefault:beginmodXEn = mod0En;din = mod0Data;endendcase
end// sample data and store into fifo
assign startSample = sampleFlagLock ^ sampleFlag;always@(posedge innerClk)
beginif(innerRst == 1'b1)beginstate <= s_idle;counter <= 10'b0;endelsebeginstate <= nextState;counter <= nextCounter;end
endalways@(state, startSample, counter, modXEn, sampleNum)
beginwr_en = 1'b0;case (state)// wait sample flags_idle: beginnextCounter = 10'b0;if(startSample == 1'b1)beginnextState = s_sample;endelsebeginnextState = s_idle;endend// samplings_sample: begin         if(modXEn == 1'b1)beginnextCounter = counter + 1'b1;endelsebeginnextCounter = counter;endwr_en = modXEn;if(nextCounter >= sampleNum)beginnextState = s_idle;endelsebeginnextState = s_sample;endenddefault: beginnextState = s_idle;endendcase
endendmodule

从上述示例可以看出,为了解决跨时钟域问题,本例主要采用了【本篇->编程思路->时钟及时钟域->跨时钟域问题->异步FIFO法】小节中介绍的方法。当然了,对于采样源选择信号和采样点数信号的稳定可靠更新,仍需采用【本篇->编程思路->时钟及时钟域->跨时钟域问题->握手法】小节中介绍的思路来解决。

编写激励发生测试模块

上述介绍的方法是适用于任何情况的一种通用的自测方法,它们的目的均是窥探FPGA芯片的内部情况,但对于一些专有的应用,仅仅能够观察到FPGA芯片内部的情况还远远不够,为了提高设计的自测性,还需要FPGA设计自身能够产生一些特定的激励。
举个例子,如果要做一个视频采集之类的项目,在硬件级的测试时,肯定需要有实实在在的视频信息输入,才能够对应得到实实在在的视频显示信号。可是为了产生视频显示信号,往往需要额外的设备和硬件电路接入,比较麻烦。此时,若能够提前在FPGA设计中嵌入一个简单的视频信号产生模块,那么就可以在没有任何外界激励源的情况下,迅速的利用视频信号产生模块发出的测试信号来初步的测试一下整个设计工作的情况,例如,彩条测试模块、雪花点测试模块都是视频应用项目中常见的提高设计自测性的内嵌激励模块,这样一来,只需要将视频信号采集源选择为这些模块的输出,便可以在显示设备中看到对应的图像,并可通过图像的质量来判断整个设计是否通过了初步的验证。
编写激励发生测试模块的方法比较适用于以处理数据包为核心业务的FPGA设计,当然了,该方法除了能提高设计的自测性以外,还能够起到应对输入异常的作用,例如我们在看电视时,当视频信号特别差,我们就可以直接切换到彩条、蓝屏或者雪花点测试模块,而不用把非常难看、晃眼的图像效果呈现给客户。

FPGA之道(70)提高设计的综合性能(二)提高设计的自测性相关推荐

  1. FPGA之道(72)提高设计的综合性能(四)提高设计的移植性与保密性

    文章目录 前言 提高设计的移植性 保持良好的代码风格 按照硬件依赖性区分代码 少使用专有IP核 提高设计的保密性 动态配置参数法 采用具有保密性的技术 前言 本文节选自<FPGA之道>. ...

  2. FPGA之道(71)提高设计的综合性能(三)提高设计的重用性与易改性

    文章目录 前言 提高设计的重用性 构建自己的IP库 提高设计的易改性 常量参数化模块设计 结构参数化模块设计 总线参数化 规模参数化 功能参数化 参数化设计的参数管理与组织 参数相关性 可传递的模块参 ...

  3. FPGA之道(69)提高设计的综合性能(一)提高设计的鲁棒性

    文章目录 前言 提高设计的鲁棒性 一些影响设计正常工作的原因 非法输入 环境干扰 应对方法之输入预处理 应对方法之RAM替换FIFO 应对方法之状态机超时跳转 应对方法之三模冗余 应对方法之全局复位 ...

  4. FPGA之道(76)静态时序分析(二)一道时序分析的例题

    文章目录 前言 一道时序分析的例题 解答一:能否正确工作分析 解答二:最大时钟速率分析 延伸二:最小时钟速率? 解答三:保持时间不足情形分析 前言 本文来自于<FPGA 之道>,在正式讲解 ...

  5. 系统架构师设计培训心得之二——架构设计

    培训过程中,老师用例子说明了一个项目的架构设计的流程.按步骤可以分为: 框架技术的选择应用: 架构平台重构与设计过程: 领域建模: 行为建模: 这四个步骤中,第三步与第四步是最重要的核心. 一. 框架 ...

  6. 数据库设计技巧系列(二)——设计表和字段

    设计表和字段 1. 检查各种变化 我在设计数据库的时候会考虑到哪些数据字段将来可能会发生变更.比方说,姓氏就是如此(注意是西方人的姓氏,比如女性结婚后从夫姓等).所以,在建立系统存储客户信息时,我倾向 ...

  7. FPGA之道(51)数据的存储

    文章目录 前言 数据的存储 为什么需要数据存储 数据存储的载体 FPGA芯片内部的载体 触发器 查找表 块存储 FPGA芯片外部的资源 数据存储的形式.实现及应用场合 寄存器 特征简介 实现载体 应用 ...

  8. FPGA之道(18)FPGA设计的编译过程

    文章目录 前言 FPGA设计的实现过程 编译概述 编译流程之综合 综合的输入 HDL代码 综合设置 综合的输出 综合的工具 编译流程之翻译融合 翻译融合的输入 翻译融合的输出 翻译融合工具 编译流程之 ...

  9. FPGA之道(50)复位的设计

    文章目录 前言 复位的设计 为什么FPGA设计中要有复位 复位方式的分类 同步复位 异步复位 复位的设计方法 同步信号同步复位 同步信号异步复位 异步信号同步复位 异步信号异步复位 复位高扇出的解决方 ...

最新文章

  1. CSS a:hover伪类在IE6下的问题
  2. ArrayList与数组
  3. 【SDOI2017】硬币游戏【KMP】【概率期望】【高斯消元】
  4. 值得关注的AI信息安全公司
  5. 端到端测试 VS 单元测试
  6. Hadoop大数据平台构建与应用
  7. 程序员常用软件:一款个人知识笔记管理神器
  8. 高级运维工程师证书_华为认证云运维高级工程师(HCIP-CDCO)
  9. 怎么判断私网地址_判断本机IP地址是公网地址还是私网地址
  10. 深入理解MATLAB中contour函数
  11. PHP写一个简单的留言板
  12. alter table新增字段操作究竟有何影响?(上篇)
  13. antd表单设置默认值
  14. 操作系统之进程调度 (十一) --- 进程调度的时机、进程调度的方式、进程调度的狭义与广义...
  15. Java 百度地图 根据经纬度(坐标) 获取乡镇
  16. 基于深度学习的行人Re-ID问题的综述和展望
  17. 宅兔APP技术服务支持
  18. abaqus实例手册_《ABAQUS 6.14超级学习手册》——1.6 实例快速入门-阿里云开发者社区...
  19. pomelo使用笔记
  20. GPS从入门到放弃(十一) --- 差分GPS

热门文章

  1. 深入理解卷II ---ICS源代码下载
  2. 转:ASP.NET程序中常用小技巧
  3. python中多重if语句用法_python – 在Pandas中使用Apply使用多个if语句的Lambda函数
  4. morlet包络检波matlab,布里渊光纤传感系统中的信号处理的研究
  5. python自动化上传图片_接口自动化之Python3_Requests之上传头像
  6. 大班体育游戏电子计算机教案,幼儿园大班体育游戏教案《夹球走走走》
  7. 划分微型计算机的标志是,划分微型计算机的标志为
  8. php如何一篇文章替换敏感字,php实现的替换敏感字符串类实例
  9. 利用LED+LDR组成可控组件搭建的振荡电路
  10. 智能车竞赛线上培训:英飞凌AURIX的智能车应用--基础四轮篇