FPGA_学习_14_第一个自写模块的感悟和ila在线调试教程与技巧(寻找APD的击穿偏压)

前一篇博客我们提到了,如果要使用算法找到Vbr,通过寻找APD采集信号的噪声方差的剧变点去寻找Vbr是一个不错的方式。此功能的第一步是在FPGA中实现方差的计算,这个我们已经在上一篇博客中实现了。

继上一篇博客之后,感觉过了很久了,原因是最近陷入的FPGA在线调试的无线循环。 万事开头难,自决定自学FPGA以来已3月有余。 刚开始我以为的万事开头难是如何从零开始在板子上跑个程序。 而真正的万事开头难是根据项目的需求,自己写出的第一个具有特定功能的模块。 而就在刚才,我经历了千辛万苦,终于算是把我第一个模块调通了。要不是我拥有我这个年纪本不该拥有的稳重,差点就热泪盈眶啦,因为调试过程确实比较曲折。根据以往的经验,一旦有所感悟一定要立马记下来,好记性不如烂笔头。 但本文绝不是我在这里发表感慨,而是我认为确实有一些值得记录的点。本文主要分为三个部分:1、第一个自写模块的感悟;2、ila在线调试教程;3、ila在线调试的技巧和注意事项。

由于目前处于自学初级阶段,也只会一些简单的调试技巧,后续如果有了新的技巧,要不断的添加更新。

1 第一个自写模块的感悟

1.1 明确模块要实现的目标,输入输出是什么?

先来回顾一下我自己是怎么写出这个模块的,一开始肯定是一脸懵逼的,不知道从何下手。所以我首先思考的是这个模块的目标是什么,更确切一点就是它需要什么输入,然后它能够输出什么。 我们的目标是找到某一个通道的APD击穿电压。

需要的输入是: 时钟、复位信号、主板温度信息、本模块的使能信号(电平使能)、该通道下ADC的采样数据。

需要的输出是: 找到击穿偏压的标志(脉冲信号,就是只有一个时钟周期的高电平)、ADC采集数据的方差(用于调试观测)、通道1 APD击穿时对应的DAC码值。由于要修改APD的偏压,需要控制DAC,而控制DAC的信号在模块外,因此需要在此模块中引出。

cpp 复制代码
module find_vbr(
        input   wire            clk             ,       // 50M 
        input   wire            rst_n           ,
        input   wire    [15:0]  temper_front    ,       // PS端传入的主板温度(16位为1表示数据有效,数据为8位温度数据+90)
        input   wire            find_vbr_ena    ,       // 寻找击穿偏压的使能信号
        input   wire    [31:0]  adc_data_ch1    ,       // CH1 ADC采样数据

        output  wire            vbr_found_flag  ,       // CH1 已找到击穿偏压标志
        output  reg     [15:0]  adc_var_ch1     ,       // CH1 ADC采样数据的方差
        output  reg     [11:0]  vbr_hv_code_ch1 ,       // CH1 击穿偏压对应的码值

        output  reg     [1:0]   hv_dac_addr     ,
        output  reg     [11:0]  hv_dac_data     ,
        output  reg             hv_dac_start
);

当然啦,模块的输入输出,是会在模块的实现过程中增删 的,这是很正常的事情。所以最开始的时候,也不必想的很全面,我们对这个模块只需要有一个初步的输入输出定义就好啦。

1.2 拆解目标

有了明确的目标,也有了输入输出之后,接下来,就是思考要实现这个目标,我要是实现哪些步骤了。 因此我没先急着写代码,而是先写了点注释。

cpp 复制代码
// 0、什么时候开始
// 1、根据温度获取对应Vbr的码值
// 2、Vbr偏压码值 - 0x50
// 3、步长0x08变化偏压码值,设置后延迟相应的时间让设置的偏压稳定
// 4、待当前偏压稳定后,计算信号的方差
// 5、判断当前信号底噪方差与上一个码值对应的信号底噪方差(默认为0)的差值是否超过阈值(30)
// 6、如果差值超过阈值则Flag拉高,如果差值未超过阈值,则继续加偏压,直到超过阈值为止
// 7、什么时候结束

要实现我的目标,那就按照上述步骤一步一步实现就可以了。由于以前C语言编写的比较多,潜意识里都是串行思路,因此在思考和拆解大目标的时候,习惯用的是串行思维 。 在后续的开发过程中要注意习惯并行思维的应用。当然了,即使是到写博客的现在呢,我仍然是认为这个模块就应该用串行的思维来思考和拆解。 只是警醒一下自己,不要忘记有并行的思维。

1.3 实现目标-硬着头皮写

即使明确了目标,也拆解了目标,对于一个FPGA初学者来讲,要动手去从0到1的实现,也是需要很大的魄力的。 开发板的例程,你有得抄,更注重理解。而现在你真要上了, 没有代码给你抄(上一篇博客我们其实是参考了C站的C知道给出的答案,有点走捷径的感觉),你得自己尝试着写了。 这里就只能硬着头皮写了,没有捷径,没有任何技巧。当然了,硬着头皮写的前提是基于开发板的基础学习还是要扎实的,不然你头发掉光了也是写不出来的,多少有点自欺欺人了。

硬着头皮写呢,有时候也会陷入一种瞻前顾后,犹豫不决,害怕失败的感觉,迟迟不敢往下写,这是正常的。 我可以肯定的告诉你,你第一把写出来的程序,百分之百有问题。 你根本不用担心失败不失败的问题,因为肯定有问题。

先写出来 ,我们主要追求的是一个完整性。

别看功能也不复杂,硬着头皮完整写完,这个步骤我基本上花了1周的时间。 调试我花了两个周 ,哈哈。 在后面的调试过程中,我又做了很多修改。 我觉得其中值得注意的一点就是, 你要在草稿纸上简单画一画时序图, 你希望你的这些信号的时序图长什么样子。 这是你在实现的时候思考和关注的问题。后续有在调试的时候,也要看实测抓出的波形是不是如你设计的那样。

我把最终成功运行的代码贴出来吧,供参考和备忘。

cpp 复制代码
`timescale 1ns / 1ps

module find_vbr(
        input   wire            clk             ,       // 50M 
        input   wire            rst_n           ,
        input   wire    [15:0]  temper_front    ,       // PS端传入的主板温度(16位为1表示数据有效,数据为8位温度数据+90)
        input   wire            find_vbr_ena    ,       // 寻找击穿偏压的使能信号

        input   wire    [31:0]  adc_data_ch1    ,       // CH1 ADC采样数据
        output  wire            vbr_found_flag  ,       // CH1 已找到击穿偏压标志
        output  reg     [15:0]  adc_var_ch1     ,       // CH1 ADC采样数据的方差
        output  reg     [11:0]  vbr_hv_code_ch1 ,       // CH1 击穿偏压对应的码值

        output  reg     [1:0]   hv_dac_addr     ,
        output  reg     [11:0]  hv_dac_data     ,
        output  reg             hv_dac_start
);

//==================================================================
//                        Parameter define
//==================================================================
parameter       THRESHOLD       = 50;
parameter       MAX_WAIT_COUNT  = 100_000_000 - 1;      // 20ns x 100_000_000 = 2 s
parameter       HVCODE_STEP     = 8;                    // 偏压码值变化8,偏压实际变化约等于0.25V
parameter       DEFAULT_HVCODE  = 12'h4E0;              // 默认APD偏压码值(修改后可设置默认偏压)
parameter       APD_SET_DELAY   = 32'd500_000;          // 设置单通道APD后等待时间
parameter       APD_SET_WIDE    = 32'd500;              // 设置使能脉宽

// parameter       THRESHOLD       = 30;
// parameter       MAX_WAIT_COUNT  = 20 - 1;      // 20ns x 100_000_000 = 2 s
// parameter       HVCODE_STEP     = 8;                    // 偏压码值变化8,偏压实际变化约等于0.25V
// parameter       DEFAULT_HVCODE  = 12'h4E0;              // 默认APD偏压码值(修改后可设置默认偏压)
// parameter       APD_SET_DELAY   = 32'd20;          // 设置单通道APD后等待时间
// parameter       APD_SET_WIDE    = 32'd10;              // 设置使能脉宽

//==================================================================
//                        Internal Signals
//==================================================================
(* MARK_DEBUG="true" *) wire    [11:0]  rom_hv_code;            // rom查找的APD偏压码值 官方给出的击穿电压再减去2V所对应的码值
(* MARK_DEBUG="true" *) reg     [27:0]  wait_cnt;               // 延迟计数变量
(* MARK_DEBUG="true" *) wire    [11:0]  pre_hv_code;
(* MARK_DEBUG="true" *) reg     [11:0]  cur_hv_code;  
(* MARK_DEBUG="true" *) reg             is_init;
(* MARK_DEBUG="true" *) reg             is_finish;
(* MARK_DEBUG="true" *) reg     [31:0]  apd_set_wait_cnt;       //自动设置状态停留计数

(* MARK_DEBUG="true" *) reg             is_hv_can_be_set;       // 偏压是否进入可设置状态
(* MARK_DEBUG="true" *) reg             is_hv_can_be_wait;      // 偏压是否进入等待响应状态
(* MARK_DEBUG="true" *) reg             is_hv_set_completed;    // 偏压设置是否已完成
(* MARK_DEBUG="true" *) wire     [15:0]  cur_var;                // 当前信号方差
(* MARK_DEBUG="true" *) wire             var_available;          // 当前信号方差可用
(* MARK_DEBUG="true" *) reg     [15:0]  his_var;                // 历史信号方差
(* MARK_DEBUG="true" *) reg     [15:0]  delta_var;              // 方差变化量


// (* MARK_DEBUG="true" *) reg rst_n;
// reg [7:0] rst_counter;                  // 默认为0
// parameter RESET_COUNT_MAX = 100;

// always @(posedge clk) begin
//         if (rst_counter < RESET_COUNT_MAX) begin
//                 rst_counter <= rst_counter + 1;
//         end 
//         else if(rst_counter == RESET_COUNT_MAX)begin
//                 rst_counter <= rst_counter;
//         end
//         else begin
//                 rst_counter <= 'd0;
//         end
// end

// always @(posedge clk) begin 
//         if(rst_counter==RESET_COUNT_MAX) begin 
//             rst_n <= 1'b1;
//         end else begin 
//             rst_n <= 1'b0;
//         end
// end

//----------------------------- pre_hv_code -----------------------------
// assign  pre_hv_code      = (temper_front[15] == 1'b1) ? rom_hv_code:DEFAULT_HVCODE;             // 验证温度数据是否有效(当disable拉高时,上电设置默认偏压值)
assign  pre_hv_code      = DEFAULT_HVCODE;  

APD_rom R_APD_rom (                                                                             // 通过Rom读取当前温度对应的官方击穿偏压 - 2V所对应的码值
        .a(temper_front[7:0]),          // input wire [7:0] a
        .spo(rom_hv_code)               // output wire [11:0] spo
);

//----------------------------- is_init -----------------------------
always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
                is_init <= 1'b0;     
        end
        else if(find_vbr_ena == 1'b1 && is_init == 1'b0) begin                                  // 当前条件下,初始化要设置的偏压码值current_HVCODE
                is_init <= 1'b1;
        end
        else begin
                is_init <= is_init;
        end
end

//----------------------------- is_init -----------------------------
always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
                is_finish <= 1'b0;     
        end
        else if(var_available==1'b1 && delta_var >= THRESHOLD) begin                                  // 当前条件下,初始化要设置的偏压码值current_HVCODE
                is_finish <= 1'b1;
        end
        else begin
                is_finish <= is_finish;
        end
end

//----------------------------- is_finish -----------------------------                         // 若方差已计算,且方差变化量大于等于阈值,则结束。
// assign is_finish = (var_available==1'b1 && delta_var >= THRESHOLD) ? 1'b1:1'b0;


//----------------------------- cur_hv_code -----------------------------
always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
                cur_hv_code <= DEFAULT_HVCODE;                                   
        end
        else if(find_vbr_ena == 1'b1 && is_init == 1'b0) begin
                cur_hv_code <= pre_hv_code - 12'h040; 
        end
        else if( (is_hv_set_completed == 1'b1) && (var_available == 1'b1) && (is_finish == 1'b0) && cur_hv_code < 12'h578) begin            // 若已初始化,方差已计算,且未结束,则偏压码值按固定步长增长进入下一轮。 
                cur_hv_code <= cur_hv_code + HVCODE_STEP;
        end
        else begin
                cur_hv_code <= cur_hv_code;
        end
end

//----------------------------- is_hv_can_be_set -----------------------------
always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
                is_hv_can_be_set <= 1'b0;                   
        end
        else if(find_vbr_ena == 1'b1 && is_init == 1'b0) begin                                  // 初始化后,isHvCanBeSet被拉高
                is_hv_can_be_set <= 1'b1;
        end
        else if( (is_hv_set_completed == 1'b1) && (var_available == 1'b1) && (is_finish == 1'b0)) begin            // 若已初始化,方差已计算,且未结束,isHvCanBeSet被拉高 
                is_hv_can_be_set <= 1'b1;
        end                                                                        
        else if ( (is_hv_can_be_set ==1'b1) && (is_hv_can_be_wait==1'b1)) begin
                is_hv_can_be_set <= 1'b0;
        end       
        else begin
                is_hv_can_be_set <= is_hv_can_be_set;
        end
end

//----------------------------- hv_dac_start+hv_dac_data -----------------------------          // 设置偏压操作
always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
                hv_dac_addr     <= 2'd0;
                hv_dac_start    <= 1'b0;
                apd_set_wait_cnt<= 'd0;                    
        end
        else if(is_hv_can_be_set == 1'b1)begin
                if (apd_set_wait_cnt > APD_SET_DELAY / 2) begin
                        hv_dac_start <= 1;
                end

                if (apd_set_wait_cnt > (APD_SET_DELAY / 2) + (APD_SET_WIDE / 2)) begin                   
                        hv_dac_data <= cur_hv_code;
                end


                if (apd_set_wait_cnt > APD_SET_DELAY / 2 + APD_SET_WIDE) begin
                        hv_dac_start <= 'd0;
                end

                if (apd_set_wait_cnt > APD_SET_DELAY) begin
                        apd_set_wait_cnt <= 'd0;
                end
                else begin
                        apd_set_wait_cnt <= apd_set_wait_cnt + 1'b1;
                end   
        end
end


//----------------------------- is_hv_can_be_wait -----------------------------
always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
                is_hv_can_be_wait <= 1'b0;        
        end
        else if (apd_set_wait_cnt > APD_SET_DELAY) begin
                is_hv_can_be_wait <= 1'b1;
        end
        else if (is_hv_can_be_wait == 1'b1 && wait_cnt == MAX_WAIT_COUNT) begin
                is_hv_can_be_wait <= 1'b0;
        end
        else begin
                is_hv_can_be_wait <= is_hv_can_be_wait;
        end
end

//----------------------------- wait_cnt -----------------------------                          // 计数两秒
always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
                wait_cnt <= 'd0;        
        end
        else if(is_hv_can_be_wait == 1'b1) begin
                if (wait_cnt == MAX_WAIT_COUNT) begin
                        wait_cnt <= 'd0;
                end
                else begin
                        wait_cnt <= wait_cnt + 1'b1;
                end
        end
        else begin
                wait_cnt <= 'd0;
        end
end

//----------------------------- is_hv_set_completed -----------------------------
always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
                is_hv_set_completed <= 1'b0;    
        end
        else if (is_hv_can_be_wait == 1'b1 && wait_cnt == MAX_WAIT_COUNT) begin                 // 偏压设置完成后,计数两秒等待结束,isHvSetCompleted被拉高
                is_hv_set_completed <= 1'b1;
        end
        else if (is_hv_set_completed == 1'b1 && var_available == 1'b1) begin                    // 方差计算完成后,isHvSetCompleted被拉低
                is_hv_set_completed <= 1'b0;
        end
        else begin
                is_hv_set_completed <= is_hv_set_completed;
        end
end

//----------------------------- var_compute -----------------------------
var_compute var_calculator (
        .clk            ( clk )                 ,
        .rst_n          ( rst_n )               ,
        .data_in        ( adc_data_ch1[7:0] )   ,
        .valid_in       ( is_hv_set_completed ) , 
        .variance       ( cur_var )             ,
        .valid_out      ( var_available )
);

//----------------------------- delta_var -----------------------------                         // 方差变化量
//assign delta_var         = (var_available == 1'b1 && cur_var > his_var) ? (cur_var - his_var) : 'd0;

//----------------------------- vbr_found_flag -----------------------------                    // 是否找到击穿偏压
assign vbr_found_flag     = (delta_var >= THRESHOLD) ? 1'b1 : 1'b0;

//----------------------------- his_var -----------------------------      
always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
                his_var <= 'd0;                    
        end
        else if(var_available == 1'b1) begin                           // 如果当前偏压并不是击穿偏压,则记录历史方差,以便于后续计算方差变化量
                his_var <= cur_var;
        end
        else begin
                his_var <= his_var;
        end
end


//----------------------------- delta_var -----------------------------      
always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
                delta_var <= 'd0;                    
        end
        else if(var_available == 1'b1) begin                           // 如果当前偏压并不是击穿偏压,则记录历史方差,以便于后续计算方差变化量
                if (cur_var >= his_var) begin
                        delta_var <= cur_var - his_var;
                end
                else begin
                        delta_var <= 'd0;
                end
                
        end
        else begin
                delta_var <= delta_var;
        end
end





endmodule

1.4 前仿真和后仿真

硬着头皮写完之后呢,就是进行仿真了,有经验的老师傅说,一般来讲前仿真(功能仿真)通过了,在线就不会有太大的问题。 而后仿真呢,是最接近与上机跑的真实情况的,但是后仿真编译的时间也比较长,所以很少有人搞后仿真的。 Modelsim搞仿真还是有一套的,再次安利一波。 在讲仿真的这里,我特别想强调的一个点,就是一定要尽量模拟真实的情况。 否则,你所认为的仿真"通过"了,就很片面,局限。 仿真这里一定要考虑全面。 我就是吃了这方面的亏, 在上一篇博客,我实现了一个方差计算小模块,我用的输入数据是0~255,这个数据仿真是没问题的(V2.0)。 但是我在用真机调试的时候,就出现了问题。 一方面在计算方差的时候,位宽 的问题,我也没有考虑周到,一方面方差的计算精度 ,我也没有考虑到,最后优化后都是V4.0了。由于仿真的粗心 ,在调更大模块的时候,我的先验知识就让我不要去考虑是不是方差计算模块出了问题,而是去考虑其他地方的问题,但是恰巧就是方差模块出了问题,这样就导致了无法准确定位的问题 真正的位置。 所以在仿真的时候, 要考虑全面,细致 。 比如我们的真实数据是在7f和80之间来回变化,那么我们如何在Testbench代码中把真实的数据模仿出来,这个是要好好考虑的问题(就在写博客的时候我已经想到如何实现了,比如根据求计数器余数的办法给出是7f是80,所以别畏难,肯定有办法)。 在调试的时候,由于一直无法定位问题,后仿真我也测试过,也是"通过"的。但是上机还是通不过,我还怀疑是板子硬件有问题,还去换了板子测试,结果是一样的。

所以仿真的全面和细致 真的很重要,另外,对于所谓的仿真"通过", 是要保持一颗怀疑的心的

1.5 对自己吹一口彩虹屁

之前在调试的时候,总是找不到问题,也请教了前辈,但是仍然没有解决问题。 他们就说我的这种实现方式(没有用状态机)有点不稳定,很容易出问题。 我当时也认同没有用状态机可能程序没那么稳定的观点。 我也想过要不就用状态机重新实现一遍。但如果让我稀里糊涂的重构用状态机实现,我心里是不甘心,不服气的。即使我的实现方式有问题,那我也一定要找到我目前这种实现方法的问题在那里,不然不明不白的重写我是无法接受的。 我的确也没有重构,通过两星期持续的坚持调试,我最终定位到了问题,并且也解决了。 戏剧的点是,其实根本就不是我实现方式的问题,而是方差子模块的问题。 因此我要感谢自己,感谢自己的不甘心,感谢自己的不服气。学习FPGA编程的态度,当如是也!

2 ila在线调试教程

ila是一种FPGA常用的在线调试方式,和DSP、STM32的断点调试不同,ila是通过抓取信号来判断你的程序是否正常运行的。学习ila我是看了B站的一个up主的视频的:Vivado在线调试工具ILA使用教程【小梅哥FPGA】_哔哩哔哩_bilibili,全程1个半小时,很受用。如视频所说的,用ila实现在线调试的方式有好几种,在这里呢,我把我最近用的这种方式记录下来,供大家和未来的自己参考。

第一步:

在所有在线调试需要抓取的变量前 添加(* MARK_DEBUG="true" *)

cpp 复制代码
(* MARK_DEBUG="true" *) wire    [11:0]  rom_hv_code;            // rom查找的APD偏压码值 官方给出的击穿电压再减去2V所对应的码值
(* MARK_DEBUG="true" *) reg     [27:0]  wait_cnt;               // 延迟计数变量
(* MARK_DEBUG="true" *) wire    [11:0]  pre_hv_code;
(* MARK_DEBUG="true" *) reg     [11:0]  cur_hv_code;  
(* MARK_DEBUG="true" *) reg             is_init;
(* MARK_DEBUG="true" *) reg             is_finish;
(* MARK_DEBUG="true" *) reg     [31:0]  apd_set_wait_cnt;       //自动设置状态停留计数

第二步:

综合电路

第三步:

打开综合设计

正常的话,会等待一段时间

第四步:

第五步:

Next三下

第六步:

添加带观测变量并设置时钟域

第七步:

选择采样的数据深度、勾选捕获和触发。

第八步:

Finish

第九步:

后面会出现一堆提示,一路OK下去。

第十步:

重新综合、布线、生成bit文件,以便后续烧写程序,在线调试

3 ila在线调试的技巧和注意事项

工程 比较 的时候,在vivado版本 比较 的时候,在你不熟悉 vivado 的时候,你去用ila在线调试,你会遇到各种奇葩的问题 ,解决办法也很奇葩。 只要你动了项目 里微小的东西,比如加个IP核,减个IP核,甚至哪怕你源代码中多了一个空格 。同样的工程,以前编译能够通过的,现在很有可能编译通不过了。 编译通不过的原因一般出现时布局布线上面,这里面有很多随机性。 同样地,你在ila调试的时候,你增加一个观测变量,你删除一个观测变量。 都有可能造成编译无法通过,最终无法生成bit文件。

调试技巧1

当你编译通不过了,你尝试删除几个ila的观测变量,如果再通不过,那就再删除几个。 你这次删除后编译通过了,下次编译你再慢慢加上去,也可以的。

调试技巧2

如果编译通过了,硬件上电正常,仿真器连接都正常,但是,你始终没办法打开你的硬件。 解决办法可以是:重新打开另一个vivado

调试技巧3

正常的调试流程,也稍微说一下

先把程序烧写到板子上

选择.bit文件

下载之后,Refresh device一下,这个操作一定别忽略。

在触发设置的窗口,添加触发信号 ,可以用 这个加号添加,也可以用拖动的方式。

然后设置触发条件,并且运行。

运行之后,如果系统捕获到了你的触发条件,那么波形窗口就会显示出来。

这个按钮是连续触发的意思,你先选中这个按钮,然后再点运行,它就会根据你的触发条件,连续不断的触发。刷新你的波形数据。 在调试一些需要观察数据变化的时候可以用使用。

另外,当你想要用多个信号来进行触发的时候,需要点击这个按钮。

调试技巧4

调试的过程,一般是由顶层逐步向下再展开去看信号是否正常, 比如顶层的top,top里面实例化了一个 find_vbr子模块叫inst_find_vbr, find_vbr子模块里面实例化了一个var_compute子模块 叫inst_var_compute。 你调试的过程应该是先看 top层的信号正不正常, 再看find_vbr层的信号正不正常,再看var_compute层的信号正不正常。 逐步的深入。 这三层模块的内部信号,都是可以使用(* MARK_DEBUG="true" *)标记,然后在线调试观测的。

调试技巧5

如果你想判断程序是否执行了某个条件,那么你可以添加一个test_flag变量, 复位的时候拉低,然后在你想检测条件下面把这个flag拉高。 这样我们就可以判断出,这个条件是否被执行过。

注意事项

代码只要烧写进去了,它就会自己跑起来,不会等你点 这个按钮,它才开始跑。

比如你自己写了一个内部的复位信号, 你在调试的时候,你是抓不到rst_n的上升沿的。 复位时间是小于1ms的,因为等你去抓的时候,人家早就已经拉高了。

cpp 复制代码
(* MARK_DEBUG="true" *) reg rst_n;
reg [7:0] rst_counter;                  // 默认为0
parameter RESET_COUNT_MAX = 100;

always @(posedge clk) begin
        if (rst_counter < RESET_COUNT_MAX) begin
                rst_counter <= rst_counter + 1;
        end 
        else if(rst_counter == RESET_COUNT_MAX)begin
                rst_counter <= rst_counter;
        end
        else begin
                rst_counter <= 'd0;
        end
end

always @(posedge clk) begin 
        if(rst_counter==RESET_COUNT_MAX) begin 
            rst_n <= 1'b1;
        end else begin 
            rst_n <= 1'b0;
        end
end

越学习,越觉得自己无知,后面应该会有一篇讲VIO的博客。欢迎大家留私信,或者评论区讨论。 分享大家的调试问题和技巧。

未完待续...

相关推荐
贾saisai11 分钟前
Xilinx系FPGA学习笔记(九)DDR3学习
笔记·学习·fpga开发
北岛寒沫16 分钟前
JavaScript(JS)学习笔记 1(简单介绍 注释和输入输出语句 变量 数据类型 运算符 流程控制 数组)
javascript·笔记·学习
铁匠匠匠2 小时前
从零开始学数据结构系列之第六章《排序简介》
c语言·数据结构·经验分享·笔记·学习·开源·课程设计
架构文摘JGWZ3 小时前
Java 23 的12 个新特性!!
java·开发语言·学习
小齿轮lsl3 小时前
PFC理论基础与Matlab仿真模型学习笔记(1)--PFC电路概述
笔记·学习·matlab
Aic山鱼4 小时前
【如何高效学习数据结构:构建编程的坚实基石】
数据结构·学习·算法
qq11561487074 小时前
Java学习第八天
学习
天玑y4 小时前
算法设计与分析(背包问题
c++·经验分享·笔记·学习·算法·leetcode·蓝桥杯
2301_789985944 小时前
Java语言程序设计基础篇_编程练习题*18.29(某个目录下的文件数目)
java·开发语言·学习
橄榄熊4 小时前
Windows电脑A远程连接电脑B
学习·kind