如何将FPGA设计验证效率提升1000倍以上(1)

发布于:2025-06-26 ⋅ 阅读:(16) ⋅ 点赞:(0)

我们将以三个设计样例,助力您提升设计开发效率。

对于FPGA应用开发来说,代码是写出来的,更是调试出来的。软件仿真拥有最佳的信号可见性和调试灵活性,被大多数工程师熟练使用,能够高效捕获很多显而易见的常见错误。

然而,由软件实现的数字仿真过程运行速度有限,很难做到100%代码覆盖。导致那些隐藏的设计问题,将不可避免的逃逸,只能以FPGA 在线调试方式解决。换而言之,硬件在线调试将成为查找设计bug、解决设计潜在缺陷的最后防线。

如果对处于全速(at-speed)运行下的FPGA调试,在现有通用“能力技术”基础上,再增加“硬件断点”功能,那么也就对高速运行FPGA,拥有像调试软件程序类似的完整的可控制能力:

  • 冻结时钟,FPGA“原地”保持静止不动,获取周期精确的信号数据;
  • 释放时钟:就像松开了FPGA“刹车片”,继续恢复至全速运行状态。

以赛灵思FPGA为例,用户已经能从硬件的运行特征出发,为设计增加两类硬件断点:

其中最基础的是时钟断点(Clock-based Breakpoint),可视为“运行时钟周期数触发的中断”,即FPGA设计在运行过程中,一旦遇到时钟断点,立即暂停执行,原地原位保持静止状态,用于获取周期精确的信号数据。

  • 用户在设计阶段,只需将下面的钟断点控制模块(interruption logic),事先集成到待测DUT的顶层(hw_top),就可获得这样的在线调试能力。

本文是系列文章的第一篇,将以最基础的 counter设计作为样例,以按图索骥的方式,详细介绍在设计中集成时钟断点的实现过程,对原有设计的改动非常小,仍然保持优异的时序性能。

以下为正文

1.样例简要介绍

输入时钟为125 MHz,按秒计时输出,由8个LED灯循环显示当前读秒计数。对应的顶层设计top.v如下:

//===========================================================================
// top.v 
//===========================================================================
module top(
  input gclk,
  input reset,  
  output [7:0] count_o
);
  
  wire [26:0] i_counter;
  
  // instantiate module count
  count inst_count (
    .rst       (reset),
    .clk       (gclk),
    .count_out (count_o),
    .count     (i_counter)
  );
endmodule

//===========================================================================
// count.v
//===========================================================================
module count (
   clk,
   rst,
   count_out,
   count
);

   input rst;                // Active low reset
   input clk;                // 125MHz input clock
   output reg [7:0] count_out;   // Output to LEDs

   output reg [26:0] count;
   
   //Counter to reduce speed of output
   always @(posedge clk)
      if (!rst) begin
         count <= 0;
      end
      else begin 
         count <= count + 1;
      end

    always @(posedge clk)
      if (!rst)
         count_out <= 8'h00;
      else begin
         if (count == 27'h773_5940) begin
            count_out <= count_out + 1;
         end
      end
      
endmodule

对应的约束文件:

#Arty A7-35T Pinout

## Clock signal
create_clock -name gclk -period 8.000 [get_ports gclk]
set_property IOSTANDARD LVCMOS33 [get_ports { gclk }]
set_property PACKAGE_PIN E3 [get_ports { gclk }]

#Counter 8-bit input and outputs - RST signal and LED indicators
## 4 LEDs
## Blue LEDs for another 4 led counting
set_property IOSTANDARD LVCMOS33 [get_ports {count_o[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {count_o[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {count_o[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {count_o[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {count_o[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {count_o[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {count_o[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {count_o[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports reset]

set_property PACKAGE_PIN H5 [get_ports {count_o[0]}]
set_property PACKAGE_PIN J5 [get_ports {count_o[1]}]
set_property PACKAGE_PIN T9 [get_ports {count_o[2]}]
set_property PACKAGE_PIN T10 [get_ports {count_o[3]}]
set_property PACKAGE_PIN E1 [get_ports {count_o[4]}]
set_property PACKAGE_PIN G4 [get_ports {count_o[5]}]
set_property PACKAGE_PIN H4 [get_ports {count_o[6]}]
set_property PACKAGE_PIN K2 [get_ports {count_o[7]}]
set_property PACKAGE_PIN A8 [get_ports reset]

当您按照创建Vivado设计项目,导入以上约束文件时,请按照自己的硬件板卡定义进行修改。

2.  时钟断点控制模块

该模块位于forgedaX软件/breakpoint/controller/clock-based子目录。

module interruption_logic(
   	(* dont_touch = "true" *) output reg [63:0] cycles,
   	input sys_clk,
	input sys_reset,
	output task_clk
);

	wire clk_en;
	wire [63:0] breakpoint;
    reg [63:0] counter;
    reg break;
    
    il_vio_0 il_vio (
         .clk(sys_clk),
         .probe_in0(counter),
         .probe_in1(cycles),
         .probe_out0(breakpoint),
         .probe_out1(clk_en)
    );

	always @(posedge sys_clk)
	begin
		if (!sys_reset)
		begin
			counter <= 0;
			break <= 0;
		end
		else if (clk_en)
		begin
			if (counter == breakpoint)
			begin
				break <= 1'b1;
			end
			else
			begin
				counter <= counter + 1;
				break <= 1'b0;
			end
		end
	end

	BUFGCE inst_bufgce (
		.O(task_clk),
		.I(sys_clk),
		.CE(clk_en & ~break)
	);
	
	always @(posedge task_clk)
	begin
        if (!sys_reset) 
        begin
            cycles <= 0;
        end  
        else
        begin
            cycles <= cycles + 1;
        end
    end

endmodule

该模块非常精简,对您的原生设计的改动很小,所占用的硬件资源也非常少,应用设计在集成断点后,仍然保持优异的时序性能。

在本样例中,该断点控制模块仅使用了不到700个LUT,在7A35T芯片总计20800个LUT资源中,占比约为3.36%。

3.  将时钟断点模块集成到设计中

时钟断点控制模块与原有设计的融合集成,全程在Xilinx Vivado环境下完成,共有三个步骤。

Step 1. 导入断点控制模块

在Vivado Project Manager中,选择“Add Source”

选择“Add or create design Source”,单击“Next”

选择“Add Files”

选择forgedaX软件提供的时钟断点控制模块interruption logic。

模块代码位于breakpoint/controller/clock-based/interruption_logic_v1.v,单击“OK”。

单击“Finish”,确认导入该模块文件。

模块成功导入,在Project Manager中显示如下:

Step 2. 导入断点控制模块所使用的VIO IP

在Vivado中,选择“IP Catalog”,导入VIO IP核。

在IP Catalog搜索框中,直接输入VIO,显示如下:

在Customize IP核界面,对VIO IP进行参数设置:

设置两个输出端口的参数:

点击“OK”,确认以上参数配置。

在IP生成对话框中,保持默认设置,点击“Generate”,生成VIO IP核。

显示“Generation of output products completed successfully”消息,表示IP核已成功导入。

在Project Manager窗口显示如下:

Step 3. 在顶层设计中,对中断控制模块进行实例化,获得受控的时钟信号

对设计顶层(hw_top)的时钟进行处理,对原有设计的改动非常少,就可实现对该时钟控制模块的融合集成,两者的比对如下:

备注:在本样例中,原始设计的顶层文件位于

<forgedaX软件目录>/demo/vivado_counter/counter/counter.srcs/sources_1/imports/Source/top.v

集成断点控制模块后的顶层文件,详见:

<forgedaX软件目录>/demo/vivado_counter_clk_bp/counter-bp/counter-bp.srcs/sources_1/imports/Source/top.v

4.  重新启动Vivado编译过程

启动Vivado编译,生成新的设计结果文件(.dcp), 得到的配置位流文件top.bit,以及调试支撑文件debug_nets.ltx。

5.  用Hardware Manager验证断点的集成过程

您可直接在Vivado Hardware Manager中,验证以上集成过程是否正确。

当对应的位流文件、调试支撑文件载入到FPGA之后,样例设计暂停运行。打开VIO调试窗口,选择Add Probes:

如下图所示,断点地址位于以上第2部分设置的初始值,即0xF。

继续在VIO窗口,将断点地址设置为FFFF_FFFF_FFFF_FFFF,清除断点,样例继续全速运行,如下图。

以上两个操作正确完成,标志着时钟断点已成功集成到本样例设计中。

后续将介绍事件断点(Event-based Breakpoint)的集成和使用,持续更新中。

全文完,感谢您的耐心阅读


网站公告

今日签到

点亮在社区的每一天
去签到