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 / 1psmodule 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) beginif (rst_n == 1'b0) beginis_init <= 1'b0;     endelse if(find_vbr_ena == 1'b1 && is_init == 1'b0) begin                                  // 當前條件下,初始化要設置的偏壓碼值current_HVCODEis_init <= 1'b1;endelse beginis_init <= is_init;end
end//----------------------------- is_init -----------------------------
always @(posedge clk or negedge rst_n) beginif (rst_n == 1'b0) beginis_finish <= 1'b0;     endelse if(var_available==1'b1 && delta_var >= THRESHOLD) begin                                  // 當前條件下,初始化要設置的偏壓碼值current_HVCODEis_finish <= 1'b1;endelse beginis_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) beginif (rst_n == 1'b0) begincur_hv_code <= DEFAULT_HVCODE;                                   endelse if(find_vbr_ena == 1'b1 && is_init == 1'b0) begincur_hv_code <= pre_hv_code - 12'h040; endelse 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;endelse begincur_hv_code <= cur_hv_code;end
end//----------------------------- is_hv_can_be_set -----------------------------
always @(posedge clk or negedge rst_n) beginif (rst_n == 1'b0) beginis_hv_can_be_set <= 1'b0;                   endelse if(find_vbr_ena == 1'b1 && is_init == 1'b0) begin                                  // 初始化后,isHvCanBeSet被拉高is_hv_can_be_set <= 1'b1;endelse 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)) beginis_hv_can_be_set <= 1'b0;end       else beginis_hv_can_be_set <= is_hv_can_be_set;end
end//----------------------------- hv_dac_start+hv_dac_data -----------------------------          // 設置偏壓操作
always @(posedge clk or negedge rst_n) beginif (rst_n == 1'b0) beginhv_dac_addr     <= 2'd0;hv_dac_start    <= 1'b0;apd_set_wait_cnt<= 'd0;                    endelse if(is_hv_can_be_set == 1'b1)beginif (apd_set_wait_cnt > APD_SET_DELAY / 2) beginhv_dac_start <= 1;endif (apd_set_wait_cnt > (APD_SET_DELAY / 2) + (APD_SET_WIDE / 2)) begin                   hv_dac_data <= cur_hv_code;endif (apd_set_wait_cnt > APD_SET_DELAY / 2 + APD_SET_WIDE) beginhv_dac_start <= 'd0;endif (apd_set_wait_cnt > APD_SET_DELAY) beginapd_set_wait_cnt <= 'd0;endelse beginapd_set_wait_cnt <= apd_set_wait_cnt + 1'b1;end   end
end//----------------------------- is_hv_can_be_wait -----------------------------
always @(posedge clk or negedge rst_n) beginif (rst_n == 1'b0) beginis_hv_can_be_wait <= 1'b0;        endelse if (apd_set_wait_cnt > APD_SET_DELAY) beginis_hv_can_be_wait <= 1'b1;endelse if (is_hv_can_be_wait == 1'b1 && wait_cnt == MAX_WAIT_COUNT) beginis_hv_can_be_wait <= 1'b0;endelse beginis_hv_can_be_wait <= is_hv_can_be_wait;end
end//----------------------------- wait_cnt -----------------------------                          // 計數兩秒
always @(posedge clk or negedge rst_n) beginif (rst_n == 1'b0) beginwait_cnt <= 'd0;        endelse if(is_hv_can_be_wait == 1'b1) beginif (wait_cnt == MAX_WAIT_COUNT) beginwait_cnt <= 'd0;endelse beginwait_cnt <= wait_cnt + 1'b1;endendelse beginwait_cnt <= 'd0;end
end//----------------------------- is_hv_set_completed -----------------------------
always @(posedge clk or negedge rst_n) beginif (rst_n == 1'b0) beginis_hv_set_completed <= 1'b0;    endelse if (is_hv_can_be_wait == 1'b1 && wait_cnt == MAX_WAIT_COUNT) begin                 // 偏壓設置完成后,計數兩秒等待結束,isHvSetCompleted被拉高is_hv_set_completed <= 1'b1;endelse if (is_hv_set_completed == 1'b1 && var_available == 1'b1) begin                    // 方差計算完成后,isHvSetCompleted被拉低is_hv_set_completed <= 1'b0;endelse beginis_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) beginif (rst_n == 1'b0) beginhis_var <= 'd0;                    endelse if(var_available == 1'b1) begin                           // 如果當前偏壓并不是擊穿偏壓,則記錄歷史方差,以便于后續計算方差變化量his_var <= cur_var;endelse beginhis_var <= his_var;end
end//----------------------------- delta_var -----------------------------      
always @(posedge clk or negedge rst_n) beginif (rst_n == 1'b0) begindelta_var <= 'd0;                    endelse if(var_available == 1'b1) begin                           // 如果當前偏壓并不是擊穿偏壓,則記錄歷史方差,以便于后續計算方差變化量if (cur_var >= his_var) begindelta_var <= cur_var - his_var;endelse begindelta_var <= 'd0;endendelse begindelta_var <= delta_var;end
endendmodule

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) beginif (rst_counter < RESET_COUNT_MAX) beginrst_counter <= rst_counter + 1;end else if(rst_counter == RESET_COUNT_MAX)beginrst_counter <= rst_counter;endelse beginrst_counter <= 'd0;end
endalways @(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.pswp.cn/news/40478.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/40478.shtml
英文地址,請注明出處:http://en.pswp.cn/news/40478.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Image captioning】ruotianluo/self-critical.pytorch之1—數據集的加載與使用

【Image captioning】ruotianluo/self-critical.pytorch之1—數據集的加載與使用 作者&#xff1a;安靜到無聲 個人主頁 數據加載程序示意圖 使用方法 示例代碼 #%%from __future__ import absolute_import from __future__ import division from __future__ import print_…

Flink-網絡流控及反壓剖析

參考&#xff1a; Apache Flink學習網

開源,微信小程序 美食便簽地圖(FoodNoteMap)的設計與開發

目錄 0 前言 1 美食便簽地圖簡介 2 美食便簽地圖小程序端開發 2.1技術選型 2.2前端UI設計 2.3主頁界面 2.4個人信息界面 2.5 添加美食界面 2.6美食便簽界面 2.8 美食好友界面 2.9 美食圈子界面 2.10 子頁面-店鋪詳情界面 2.11 后臺數據緩存 2.12 訂閱消息通知 2.1…

Redis為什么能如此之快

推薦閱讀 AI文本 OCR識別最佳實踐 AI Gamma一鍵生成PPT工具直達鏈接 玩轉cloud Studio 在線編碼神器 玩轉 GPU AI繪畫、AI講話、翻譯,GPU點亮AI想象空間 資源分享 「java、python面試題」來自UC網盤app分享&#xff0c;打開手機app&#xff0c;額外獲得1T空間 https://dr…

“深入探索JVM內部機制:解密Java虛擬機原理“

標題&#xff1a;深入探索JVM內部機制&#xff1a;解密Java虛擬機原理 摘要&#xff1a;本文將深入探索Java虛擬機&#xff08;JVM&#xff09;的內部機制&#xff0c;揭示其工作原理和關鍵組成部分&#xff0c;包括類加載、內存管理、垃圾回收、即時編譯和運行時數據區域等。…

探索區塊鏈世界:去中心化應用(DApp)的嶄新前景

隨著科技的不斷發展&#xff0c;區塊鏈技術逐漸引領著數字時代的潮流。在這個充滿創新和變革的領域中&#xff0c;去中心化應用&#xff08;DApp&#xff09;成為了備受矚目的焦點。DApp 不僅改變了傳統應用程序的范式&#xff0c;還在金融、社交、游戲等多個領域展現出了廣闊的…

GRPC 鏈接 NODE 和 GOLANG

GRPC 鏈接 NODE 和 GOLANG GRPC 了解 什么是GRPC gRPC 采用了 Protocol Buffers 作為數據序列化和反序列化的協議&#xff0c;可以更快速地傳輸數據&#xff0c;并支持多種編程語言的跨平臺使用gRPC 提供“統一水平層”來對此類問題進行抽象化。 開發人員在本機平臺中編寫專…

打造專屬照片分享平臺:快速上手Piwigo網頁搭建

文章目錄 通過cpolar分享本地電腦上有趣的照片&#xff1a;部署piwigo網頁前言1.Piwigo2. 使用phpstudy網頁運行3. 創建網站4. 開始安裝Piwogo 總結 &#x1f340;小結&#x1f340; &#x1f389;博客主頁&#xff1a;小智_x0___0x_ &#x1f389;歡迎關注&#xff1a;&#x…

深度學習1:通過模型評價指標優化訓練

P(Positive)表示預測為正樣本&#xff0c;N(negative)表示預測為負樣本&#xff0c;T(True)表示預測正確,F(False)表示預測錯誤。 TP&#xff1a;正樣本預測正確的數量&#xff08;正確檢測&#xff09; FP&#xff1a;負樣本預測正確數量&#xff08;誤檢測&#xff09; TN…

【AI實戰】BERT 文本分類模型自動化部署之 dockerfile

【AI實戰】BERT 文本分類模型自動化部署之 dockerfile BERTBERT 文本分類模型基于中文預訓練bert的文本分類模型針對多分類模型的loss函數樣本不均衡時多標簽分類時 dockerfile編寫 dockerfilebuild鏡像運行docker測試服務 參考 本文主要介紹&#xff1a; 基于BERT的文本分類模…

卷積神經網絡CNN

卷積神經網絡CNN 1 應用領域1 檢測任務2 分類和檢索3 超分辨率重構4 醫學任務5 無人駕駛6 人臉識別 2 卷積的作用3 卷積特征值計算方法4 得到特征圖表示5 步長和卷積核大小對結果的影響1 步長2 卷積核 6 邊緣填充方法7 特征圖尺寸計算與參數共享8 池化層的作用9 整體網絡架構10…

【GitLab私有倉庫】如何在Linux上用Gitlab搭建自己的私有庫并配置cpolar內網穿透?

文章目錄 前言1. 下載Gitlab2. 安裝Gitlab3. 啟動Gitlab4. 安裝cpolar5. 創建隧道配置訪問地址6. 固定GitLab訪問地址6.1 保留二級子域名6.2 配置二級子域名 7. 測試訪問二級子域名 前言 GitLab 是一個用于倉庫管理系統的開源項目&#xff0c;使用Git作為代碼管理工具&#xf…

ngModel和formControlName處理表單控件

ngModel 和 formControlName 不能同時在同一個表單控件上使用&#xff1b; 二者都用于在 Angular 中處理表單控件的值&#xff0c;但是它們的底層實現方式不同。 ngModel 是 Angular 提供的雙向數據綁定指令&#xff0c;它可以將表單控件的值與組件類中的屬性進行雙向綁定。當…

軟考筆記——10.項目管理

進度管理 進度管理就是采用科學的方法&#xff0c;確定進度目標&#xff0c;編制進度計劃和資源供應計劃&#xff0c;進行進度控制&#xff0c;在與質量、成本目標協調的基礎上&#xff0c;實現工期目標。 具體來說&#xff0c;包括以下過程&#xff1a; (1) 活動定義&#…

HLS實現FIR低通濾波器+System Generator仿真

硬件&#xff1a;ZYNQ7010 軟件&#xff1a;MATLAB 2019b、Vivado 2017.4、HLS 2017.4、System Generator 2017.4 1、MATLAB設計低通濾波器 FPGA系統時鐘 50MHz&#xff0c;也是采樣頻率。用 MATLAB 生成 1MHz 和 10MHz 的正弦波疊加的信號&#xff0c;并量化為 14bit 整數。把…

css 用過渡實現,鼠標離開li時,背景色緩慢消息的樣式

要實現鼠標懸停時背景顏色變為黃色&#xff0c;鼠標離開時背景顏色慢慢消失并變回白色的效果&#xff0c; 可以使用CSS的過渡&#xff08;transition&#xff09;屬性 li {background: #fff;color: #000;transition: background 0.5s ease-out; }li:hover {background: #fbb31…

Web網頁瀏覽器遠程訪問jupyter notebook服務器【內網穿透】

文章目錄 前言1. Python環境安裝2. Jupyter 安裝3. 啟動Jupyter Notebook4. 遠程訪問4.1 安裝配置cpolar內網穿透4.2 創建隧道映射本地端口 5. 固定公網地址 前言 Jupyter Notebook&#xff0c;它是一個交互式的數據科學和計算環境&#xff0c;支持多種編程語言&#xff0c;如…

Hyper-v導致Vmware window無法啟動崩潰記錄

最近有幾次vmware啟動window10直接崩潰情況&#xff0c;顯示藍屏報錯。一開始沒在意&#xff0c;以為是因為固態硬盤錯了幾個字節導致的&#xff1f; 但后來想想不對啊。vmware用了也有10來年了&#xff0c;穩得一筆&#xff0c;在仔細思考了一下后發現打不開的win10這三個虛擬…

Oracle/PL/SQL奇技淫巧之Lable標簽與循環控制

在一些存儲過程場景中&#xff0c;可能存在需要在滿足某些條件時跳出循環的場景&#xff0c; 但是在PL/SQL中&#xff0c;不能使用break語句直接跳出循環, 但是可以通過lable標簽的方式跳出循環&#xff0c;例&#xff1a; <<outer_loop>> FOR i IN 1..5 LOOPDBMS…

Python批量替換Excel和Word中的關鍵字

一、問題的提出 有時&#xff0c;我們手頭上有多個Excel或者Word文件&#xff0c;但是領導突然要求對某幾個術語進行批量的修改&#xff0c;你是不是有要崩潰的感覺。因為這么多文件&#xff0c;要一個一個地打開文件&#xff0c;再進行批量替換修改&#xff0c;幾個文件還好&…