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的信号在模块外,因此需要在此模块中引出。

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 拆解目标

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

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

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

1.3 实现目标-硬着头皮写

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

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

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

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

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

`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" *)

(* 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的,因为等你去抓的时候,人家早就已经拉高了。

(* 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的博客。欢迎大家留私信,或者评论区讨论。 分享大家的调试问题和技巧。

未完待续... 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/77196.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

.net连接mysql,提示找不到请求的 .Net Framework Data Provider。可能没有安装

开发完成的.net程序需要连接mysql数据库&#xff0c;在个人电脑上运行没问题&#xff0c;别人运行时提示“提示找不到请求的 .Net Framework Data Provider。可能没有安装”。经过查询&#xff0c;安装Connector/NET 8.1.0&#xff0c;下载地址如下所示&#xff1a; https://d…

AI抢饭碗!新闻集团将使用生成式AI,每周自动写3000篇新闻丨IDCF

作者&#xff1a;AIGC开放社区 8月1日&#xff0c;英国卫报消息&#xff0c;全球最大新闻媒体公司之一的新闻集团&#xff0c;将使用生成式AI每周自动创建3000篇澳大利亚本地新闻。 据悉&#xff0c;新闻集团在内部成立了一个名为“Data Local”的部门只有4名员工&#xff0c;…

c语言操作文件

1、文件存储 文件存取格式 从用户或者操作系统使用的角度&#xff08;逻辑上&#xff09;文件可以分为文本文件、二进制文件 文本文件&#xff1a;存储时&#xff0c;是将字符的ascii码值存在磁盘中&#xff0c;取的时候将ascii码翻译成对应的字符&#xff0c;这个翻译器就是…

ubuntu下gif动态图片的制作

Gif图片比视频小, 比静态JPG图片形象生动, 更适用于产品展示和步骤演示等。各种各样的gif动图为大家交流提供很大的乐趣. 这里简单介绍ubuntu系统下gif图的制作。 一、工具安装: kazam和ffmpeg kazam是linux下的一款简单但是功能强大的屏幕录制工具. 它可录制声音并选择全屏录…

【C++】一文带你初识C++继承

食用指南&#xff1a;本文在有C基础的情况下食用更佳 &#x1f340;本文前置知识&#xff1a; C类 ♈️今日夜电波&#xff1a;napori—Vaundy 1:21 ━━━━━━️&#x1f49f;──────── 3:23 …

数据库--SQL关键字的执行顺序

数据库相关链接&#xff1a; 数据库--数据类型&#xff1a;http://t.csdn.cn/RtqMD 数据库--三大范式、多表查询、函数sql&#xff1a;http://t.csdn.cn/udJSG 数据库--MySQL增删改查&#xff1a;http://t.csdn.cn/xkiti 一、一条sql语句通常包括&#xff1a; select fro…

[PaddlePaddle] [学习笔记] [上] 计算机视觉(卷积、卷积核、卷积计算、padding计算、BN、缩放、平移、Dropout)

1. 计算机视觉的发展历程 计算机视觉作为一门让机器学会如何去“看”的学科&#xff0c;具体的说&#xff0c;就是让机器去识别摄像机拍摄的图片或视频中的物体&#xff0c;检测出物体所在的位置&#xff0c;并对目标物体进行跟踪&#xff0c;从而理解并描述出图片或视频里的场…

pytest自动生成测试类 demo

一、 pytest自动生成测试类 demo # -*- coding:utf-8 -*- # Author: 喵酱 # time: 2023 - 08 -15 # File: test4.py # desc: import pytest import unittest# 动态生成测试类def create_test_class(class_name:str, test_cases:list) -> type:"""生成测试类…

根据源码,模拟实现 RabbitMQ - 通过 SQLite + MyBatis 设计数据库(2)

目录 一、数据库设计 1.1、数据库选择 1.2、环境配置 1.3、建库建表接口实现 1.4、封装数据库操作 1.5、针对 DataBaseManager 进行单元测试 一、数据库设计 1.1、数据库选择 MySQL 是我们最熟悉的数据库&#xff0c;但是这里我们选择使用 SQLite&#xff0c;原因如下&am…

JavaScript、TypeScript、ES5、ES6之间的联系和区别

ECMAScript&#xff1a; 一个由 ECMA International 进行标准化&#xff0c;TC39 委员会进行监督的语言。通常用于指代标准本身。JavaScript&#xff1a; ECMAScript 标准的各种实现的最常用称呼。这个术语并不局限于某个特定版本的 ECMAScript 规范&#xff0c;并且可能被用于…

看看安森美深力科NSI45090JDT4G 是如何点亮汽车内外照明系统解决方案

关于线性恒流调节器&#xff08;CCR&#xff09;&#xff1a;是一种用于控制电流的稳定输出。它通常由一个功率晶体管和一个参考电流源组成。CCR的工作原理是通过不断调节功率晶体管的导通时间来维持输出电流的恒定。当输出电流超过设定值时&#xff0c;CCR会减少功率晶体管的导…

Linux:shell脚本 正则表达式与AWK

一、正则表达式 由一类特殊字符及文本字符所编写的模式&#xff0c;其中有些字符&#xff08;元字符&#xff09;不表示字符字面意义&#xff0c;而表示控制或通配的功能&#xff0c;类似于增强版的通配符功能&#xff0c;但与通配符不同&#xff0c;通配符功能是用来处理文件…

八股文之框架篇(Spring Boot、SSM)

文章目录 Spring中的单例bean是线程安全的吗什么是AOP&#xff0c;项目中有没有使用到AOPSpring中的事务是如何实现的Spring中事务失效的场景有哪些Bean的生命周期Spring中的循环依赖&#xff08;循环引用&#xff09;SpringMVC的执行流程SpringBoot自动配置原理Spring、Spring…

生信豆芽菜-差异基因富集分析

网址&#xff1a;http://www.sxdyc.com/enrichmentEnrich 该工具使用R 语言的clusterProfiler包对关键基因集进行GO和KEGG富集分析&#xff0c;注意这个的关键基因集可以是差异基因&#xff0c;WGCNA的module基因&#xff0c;也可以是表型相关的基因集 1、数据准备 准备一个基因…

kubernetes企业级高可用部署

目录 1、Kubernetes高可用项目介绍 2、项目架构设计 2.1、项目主机信息 2.2、项目架构图 1、Kubernetes高可用项目介绍 2、项目架构设计 2.1、项目主机信息 2.2、项目架构图 2.3、项目实施思路 3、项目实施过程 3.1、系统初始化 3.2、配置部署keepalived服务 3.3、…

【Android Studio】 win11 安装配置 jdk17 超详细

概述 一个好的安装教程能够帮助开发者完成更便捷、更快速的开发。书山有路勤为径&#xff0c;学海无涯苦作舟。我是秋知叶i、期望每一个阅读了我的文章的开发者都能够有所成长。 一、下载JDK JDK官网 这里下载 JDK17 windows x64 installer 二、安装JDK 双击打开下载的 j…

如何在 iOS 上安装并使用 ONLYOFFICE 文档

借助 iOS 版文档应用&#xff0c;您可在移动端设备上访问存储于 ONLYOFFICE 账户中的文件&#xff0c;查看和编辑现有文本文档、电子表格和演示文稿&#xff0c;创建新文档并对其进行整理&#xff0c;以及连接第三方云存储服务。您可与其他门户网站用户协作编辑文档&#xff0c…

16.5.4 【Linux】SELinux 政策内的规则管理

SELinux 各个规则的布林值查询 getsebool 如果想要查询系统上面全部规则的启动与否 &#xff08;on/off&#xff0c;亦即布林值&#xff09;&#xff0c;很简单的通过 sestatus-b 或 getsebool -a 均可&#xff01; SELinux 各个规则规范的主体程序能够读取的文件 SELinux typ…

QT QtXlsx安装使用

QtXlsx介绍 QtXlsx是一个可以读取和写入Excel文件的库。它不需要Microsoft Excel&#xff0c;可以在Qt5支持的任何平台上使用。 这里一定是需要QT5支持的。 须知安装QtXlsx时&#xff0c;需要下载perl 1.安装perl 这里选择官网下载安装即可。 官网地址&#xff1a;https://p…

【Git】(四)子模块

1、增加子模块 进入准备添加子模块所在的目录&#xff0c;例如library。 git submodule add -b 1.0.0.0 gitgitee.com:sunriver2000/SubModule.git参数-b用于指定子模块分支。 2、更新子模块 git submodule update --progress --init --recursive --force --remote -- "…
最新文章