電實(shí)驗(yàn):ABS絕對(duì)值模塊的Vivado仿真與上板實(shí)現(xiàn)全解析)
簡(jiǎn)介面向大二數(shù)字電子技術(shù)課程這份資源提供絕對(duì)值加法器ABS實(shí)驗(yàn)的完整Vivado設(shè)計(jì)與FPGA實(shí)現(xiàn)方案適合正在學(xué)習(xí)組合邏輯、時(shí)序邏輯以及FPGA開發(fā)流程的學(xué)生參考。包內(nèi)共79個(gè)文件整包約435KB以Vivado工程文件為主涵蓋工程配置xpr、xdc、dcp、仿真驗(yàn)證wdf、vds、綜合實(shí)現(xiàn)報(bào)告rpt、rst以及自動(dòng)化腳本tcl、sh、bat等既能在Vivado中直接打開工程復(fù)現(xiàn)設(shè)計(jì)也可按需查閱約束和日志排查問題。目前已有359人學(xué)習(xí)下載參考價(jià)值較高。通過研讀這套資料讀者可以理解ABS加法器的設(shè)計(jì)思路掌握利用Verilog/VHDL編寫邏輯、編寫測(cè)試向量、進(jìn)行行為仿真并最終下載到FPGA驗(yàn)證的完整流程對(duì)鞏固數(shù)電理論和提升工程實(shí)踐能力都有直接幫助。1. 數(shù)電實(shí)驗(yàn)從仿真到 FPGA 上板這份 abs 實(shí)驗(yàn)包到底能幫你省多少事大二數(shù)電實(shí)驗(yàn)里“用 Vivado 做仿真并上板實(shí)現(xiàn)”這類題目卡住人的往往不是 Verilog 本身而是仿真和上板之間的那條鴻溝——仿真波形明明對(duì)燒到板子上就是全亂或者壓根不知道 testbench 怎么寫、管腳怎么約束。手頭這份“數(shù)電實(shí)驗(yàn)仿真加 FPGA 實(shí)現(xiàn) abs.zip”核心就是用 Vivado 完整走一遍 ABS 絕對(duì)值計(jì)算模塊的設(shè)計(jì)、仿真、綜合、上板全流程適合正在被數(shù)電實(shí)驗(yàn)折磨的大二學(xué)生也適合想快速回顧數(shù)字電路設(shè)計(jì)流程的從業(yè)者。它不是一份只有代碼的源碼包更像一份能照著做不翻車的實(shí)驗(yàn)手冊(cè)把 Vivado 從建工程到下載比特流的每一步都串了起來。接下來按我拆完這套資源的順序把每一步怎么走、參數(shù)怎么設(shè)、坑在哪講清楚。2. ABS 絕對(duì)值模塊的 RTL 設(shè)計(jì)補(bǔ)碼原理與 Verilog 實(shí)現(xiàn)2.1 補(bǔ)碼原理為什么 abs 不是簡(jiǎn)單地把符號(hào)位抹掉ABS 模塊在數(shù)字電路里最常見的應(yīng)用是處理有符號(hào)數(shù)而有符號(hào)數(shù)在 FPGA 里幾乎一律用補(bǔ)碼表示。補(bǔ)碼的核心邏輯是正數(shù)的補(bǔ)碼等于原碼負(fù)數(shù)的補(bǔ)碼等于按位取反再加 1。所以計(jì)算絕對(duì)值時(shí)很多新手第一反應(yīng)是“把符號(hào)位去掉不就行了”這在原碼思維下成立在補(bǔ)碼體系里卻會(huì)出錯(cuò)。舉個(gè)例子8 位有符號(hào)數(shù) -5 的補(bǔ)碼是 11111011如果直接把最高位當(dāng)成符號(hào)位抹掉變成 01111011得到的是 123而不是 5。正確做法是判斷符號(hào)位如果為 0直接輸出原值如果為 1則按位取反再加 1也就是 11111011 取反得到 00000100再加 1 得到 00000101這才是 5。這里有一個(gè)邊界問題值得單獨(dú)拎出來8 位補(bǔ)碼能表示的范圍是 -128 到 127其中 -128 的補(bǔ)碼是 10000000。按照取反加一的規(guī)則它取反得到 01111111加 1 得到 10000000結(jié)果還是 -128而不是 128。這是因?yàn)?128 已經(jīng)超出了 8 位有符號(hào)數(shù)的表示范圍。這個(gè)點(diǎn)在課設(shè)答辯時(shí)經(jīng)常被問也是仿真里最容易暴露的隱藏 bug。常見的處理方式有兩種一是把輸出位寬擴(kuò)展為 9 位讓 abs(-128) 變成 0_10000000也就是 128二是保留 8 位輸出同時(shí)加一個(gè)溢出標(biāo)志位由外部電路做進(jìn)一步處理。這份資源里采用的做法是第二種因?yàn)樗闹饕繕?biāo)是把“兩位數(shù)實(shí)驗(yàn)”和“FPGA 實(shí)現(xiàn)”銜接起來溢出標(biāo)志在后續(xù)擴(kuò)展里更靈活。2.2 Verilog 實(shí)現(xiàn)數(shù)據(jù)流描述與行為級(jí)描述怎么選ABS 模塊的 RTL 實(shí)現(xiàn)并不復(fù)雜但描述方式會(huì)影響綜合后的電路結(jié)構(gòu)。這份資源里主模塊用的是行為級(jí)描述配合三態(tài)判斷和條件賦值整體邏輯非常直觀// abs_module.v module abs_module #( parameter DATA_WIDTH 8 // 數(shù)據(jù)位寬可通過例化時(shí)修改 )( input wire [DATA_WIDTH-1:0] data_in, // 有符號(hào)數(shù)輸入補(bǔ)碼 output reg [DATA_WIDTH-1:0] abs_out, // 絕對(duì)值輸出 output reg overflow // 溢出標(biāo)志1 表示超出可表示范圍 ); always (*) begin if (data_in[DATA_WIDTH-1] 1b0) begin // 符號(hào)位為 0正數(shù) abs_out data_in; overflow 1b0; end else if (data_in {1b1, {(DATA_WIDTH-1){1b0}}}) begin // 特判補(bǔ)碼最小值僅最高位為 1其余全 0 abs_out data_in; // 輸出原值不可表示的 128 補(bǔ)碼形式 overflow 1b1; // 置溢出標(biāo)志 end else begin abs_out ~data_in 1b1; // 負(fù)數(shù)取反加一 overflow 1b0; end end endmodule這段代碼里有兩個(gè)地方值得注意。第一個(gè)是always (*)組合邏輯塊沒有時(shí)鐘參與所以 abs_out 會(huì)隨著 data_in 的變化立刻變化這在純組合邏輯驗(yàn)證里非常直觀。第二個(gè)是特判分支里的那個(gè)全 1 加零的組合寫法{1b1, {(DATA_WIDTH-1){1b0}}}它用拼接語法構(gòu)造了一個(gè)最高位為 1、其余位全 0 的數(shù)在參數(shù)化位寬下不需要手工數(shù)零改 DATA_WIDTH 時(shí)不用動(dòng)內(nèi)部邏輯。數(shù)據(jù)流描述也可以實(shí)現(xiàn)同樣的功能比如寫成assign abs_out (data_in[DATA_WIDTH-1]) ? (~data_in 1b1) : data_in;但這種寫法無法在同一個(gè)表達(dá)式里同時(shí)處理溢出標(biāo)志而且不利于在仿真里加斷言。行為級(jí)描述雖然綜合出來可能多幾級(jí) LUT但對(duì)于大二實(shí)驗(yàn)的教學(xué)場(chǎng)景可讀性和可測(cè)試性遠(yuǎn)比那一點(diǎn)點(diǎn)資源消耗重要。2.3 參數(shù)化設(shè)計(jì)與 Testbench 的接口約定這份資源把 DATA_WIDTH 做成了 parameter意味著你可以在頂層例化它時(shí)直接改位寬。比如你想驗(yàn)證 16 位輸入只需要在頂層寫abs_module #(.DATA_WIDTH(16)) u_abs (...)。但要注意溢出判斷的分支條件寫的是“僅最高位為 1 且其余全 0”這個(gè)模式在任意位寬下都成立因?yàn)檠a(bǔ)碼表示的最小負(fù)數(shù)永遠(yuǎn)是 1000...000這是補(bǔ)碼編碼的固有性質(zhì)。所以參數(shù)化不會(huì)引入邏輯錯(cuò)誤只需要在 testbench 里同步修改輸入的位寬。另外資源里給 abs_out 和 overflow 都加了輸出端口這讓模塊不是一次性的“玩具電路”而是可以掛到總線或嵌入到更大的數(shù)據(jù)通路里。比如在后續(xù)做帶符號(hào)運(yùn)算單元時(shí)abs 可以作為預(yù)處理模塊overflow 信號(hào)接出去觸發(fā)飽和處理。這個(gè)設(shè)計(jì)取舍在實(shí)驗(yàn)報(bào)告里說明會(huì)很加分。3. Vivado 仿真testbench 編寫與波形驗(yàn)證的四個(gè)關(guān)鍵點(diǎn)3.1 testbench 結(jié)構(gòu)從復(fù)位到邊界值全覆蓋拿到這段 RTL第一步不是急著上板而是在 Vivado 里跑仿真。abs 模塊是純組合邏輯理論上不需要時(shí)鐘但實(shí)際項(xiàng)目里它往往掛在時(shí)序邏輯后面所以 testbench 里依然要給出時(shí)鐘和復(fù)位模擬真實(shí)接入場(chǎng)景。這份資源里的 testbench 寫得相當(dāng)規(guī)整// tb_abs_module.v timescale 1ns / 1ps module tb_abs_module; parameter DATA_WIDTH 8; reg [DATA_WIDTH-1:0] tb_data_in; wire [DATA_WIDTH-1:0] tb_abs_out; wire tb_overflow; abs_module #(.DATA_WIDTH(DATA_WIDTH)) uut ( .data_in(tb_data_in), .abs_out(tb_abs_out), .overflow(tb_overflow) ); initial begin tb_data_in 8b0000_0000; // 0 #100; tb_data_in 8b0000_0001; // 1 #100; tb_data_in 8b0111_1111; // 127最大正數(shù) #100; tb_data_in 8b1111_1111; // -1 #100; tb_data_in 8b1000_0001; // -127 #100; tb_data_in 8b1000_0000; // -128邊界溢出 #100; $display(abs(-128) %d, overflow %b, $signed(tb_abs_out), tb_overflow); $display(Test finished.); $finish; end endmodule這里沒有時(shí)鐘和復(fù)位信號(hào)因?yàn)榧兘M合邏輯下電平一變輸出就應(yīng)該在當(dāng)前時(shí)間步內(nèi)更新。#100是延時(shí) 100ns作用是給仿真器足夠的時(shí)間步來傳播組合邏輯的值也讓波形圖上每個(gè)輸入狀態(tài)保持一段可讀的穩(wěn)定區(qū)間。最后的$display會(huì)打印出 abs(-128) 的十進(jìn)制數(shù)值方便在 Tcl Console 里直接核對(duì)。3.2 仿真流程Vivado 里從添加文件到看波形的完整操作在 Vivado 中跑這段仿真的步驟不復(fù)雜但每一步都有容易出錯(cuò)的地方。首先新建工程時(shí)器件型號(hào)要和你的開發(fā)板一致如果選錯(cuò)后面的管腳約束文件會(huì)報(bào)錯(cuò)。其次把 abs_module.v 和 tb_abs_module.v 都加入工程Vivado 會(huì)默認(rèn)把包含 initial 語句的文件識(shí)別為仿真文件但如果你的工程設(shè)置里 simulation sources 沒包含 testbench運(yùn)行仿真時(shí)會(huì)提示找不到頂層模塊。常見做法是在 Sources 窗口選中 tb 文件右鍵點(diǎn)擊 Set as Top把它設(shè)為仿真頂層。仿真運(yùn)行后波形窗口里默認(rèn)只能看到 tb_data_in、tb_abs_out 這些端口信號(hào)。如果你想看 abs_module 內(nèi)部某個(gè)中間值需要把那根線加入波形窗口或者直接在 Tcl Console 里輸入add_wave /tb_abs_module/uut/abs_out。這是很多新手卡住的地方仿真結(jié)果看著“不對(duì)”其實(shí)只是沒把所有關(guān)心的信號(hào)加進(jìn)來觀察。3.3 仿真用例設(shè)計(jì)為什么邊界值比隨機(jī)值更重要這份資源里的 testbench 只測(cè)了六個(gè)值卻涵蓋了正數(shù)、負(fù)數(shù)、最大正數(shù)、最小負(fù)數(shù)、溢出邊界和零。很多人寫 testbench 喜歡用$random生成大量隨機(jī)數(shù)據(jù)這在做復(fù)雜算法驗(yàn)證時(shí)有用但對(duì)于 abs 這種邏輯簡(jiǎn)單的模塊邊界值才是最容易暴露問題的。我的習(xí)慣是把邊界用例按表格列出來對(duì)照驗(yàn)證輸入十進(jìn)制輸入補(bǔ)碼期望輸出期望溢出標(biāo)志驗(yàn)證點(diǎn)00000_000000零值對(duì)稱性10000_000110最小正數(shù)1270111_11111270最大正數(shù)-11111_111110最小負(fù)數(shù)絕對(duì)值-1271000_00011270常規(guī)負(fù)數(shù)-1281000_0000-1281溢出邊界這張表可以直接貼進(jìn)實(shí)驗(yàn)報(bào)告比純文字描述清楚得多。仿真跑完仔細(xì)核對(duì)這六個(gè)用例就能確認(rèn) RTL 邏輯沒有大的方向性錯(cuò)誤。3.4 仿真中的常見誤用電平激勵(lì)和時(shí)序激勵(lì)搞混組合邏輯的 testbench 只需要給電平不需要(posedge clk)但如果你把 abs 模塊接到一個(gè)帶時(shí)鐘使能的寄存器組后面就必須考慮時(shí)序問題。比如你用一個(gè) 8 位寄存器存 data_in在每個(gè)時(shí)鐘上升沿更新那么 testbench 里就要提供時(shí)鐘且 abs_out 的變化會(huì)比寄存器輸出晚半個(gè)周期的組合延遲這在波形圖上能看到一個(gè)小的毛刺或延遲屬于正?,F(xiàn)象不代表電路錯(cuò)誤。另一個(gè)容易誤用的是$signed()的使用。$display打印 abs_out 時(shí)如果不加$signed()會(huì)把 8 位二進(jìn)制數(shù)當(dāng)作無符號(hào)數(shù)打印那么 11111111 會(huì)被打印成 255而不是 -1。這也是為什么我在上面代碼里特意寫了$signed(tb_abs_out)否則你很可能對(duì)著一串看似錯(cuò)誤的結(jié)果懷疑模塊寫錯(cuò)了。這個(gè)細(xì)節(jié)在排查問題時(shí)能省掉大量時(shí)間。4. 綜合實(shí)現(xiàn)與上板管腳約束、時(shí)序收斂與比特流下載4.1 XDC 管腳約束別讓撥碼開關(guān)的引腳映射變成玄學(xué)仿真通過只是第一步真正讓數(shù)電實(shí)驗(yàn)“落地”的是把邏輯燒到 FPGA 里。絕大多數(shù)開發(fā)板是通過撥碼開關(guān)輸入、LED 顯示輸出所以需要把 abs_module 的端口映射到具體的物理引腳。這份資源自帶一份 XDC 約束文件核心內(nèi)容大致如下## abs_module 引腳約束示例 set_property PACKAGE_PIN U1 [get_ports data_in[0]] set_property PACKAGE_PIN U2 [get_ports data_in[1]] set_property PACKAGE_PIN U3 [get_ports data_in[2]] set_property PACKAGE_PIN U4 [get_ports data_in[3]] set_property PACKAGE_PIN U5 [get_ports data_in[4]] set_property PACKAGE_PIN U6 [get_ports data_in[5]] set_property PACKAGE_PIN U7 [get_ports data_in[6]] set_property PACKAGE_PIN U8 [get_ports data_in[7]] set_property IOSTANDARD LVCMOS33 [get_ports data_in*] set_property IOSTANDARD LVCMOS33 [get_ports abs_out*] set_property PACKAGE_PIN U9 [get_ports abs_out[0]] set_property PACKAGE_PIN U10 [get_ports abs_out[1]] set_property PACKAGE_PIN U11 [get_ports abs_out[2]] set_property PACKAGE_PIN U12 [get_ports abs_out[3]] set_property PACKAGE_PIN U13 [get_ports abs_out[4]] set_property PACKAGE_PIN U14 [get_ports abs_out[5]] set_property PACKAGE_PIN U15 [get_ports abs_out[6]] set_property PACKAGE_PIN U16 [get_ports abs_out[7]]XDC 文件里有三個(gè)必須同時(shí)滿足的約束缺一個(gè)都可能出問題。PACKAGE_PIN 指定了引腳號(hào)IOSTANDARD 指定了電平標(biāo)準(zhǔn)一般開發(fā)板用的是 LVCMOS33。如果你的開發(fā)板是 1.8V 或 2.5V 邏輯這里要改成對(duì)應(yīng)的標(biāo)準(zhǔn)否則即使綜合能通過上板后引腳電平不匹配數(shù)據(jù)讀進(jìn)來全是亂的。另一個(gè)容易被忽略的是撥碼開關(guān)的默認(rèn)狀態(tài)——很多開發(fā)板的撥碼開關(guān)撥到 ON 時(shí)引腳接地OFF 時(shí)默認(rèn)高電平如果你的設(shè)計(jì)期望高電平為邏輯 1方向反了會(huì)導(dǎo)致輸入全反。這種情況在仿真里看不出來屬于純上板問題。4.2 綜合與實(shí)現(xiàn)的流程Vivado 里跑完三步還需要看什么Vivado 的流程是綜合Synthesis→ 實(shí)現(xiàn)Implementation→ 生成比特流Generate Bitstream三步缺一不可。綜合是把 Verilog 變成門級(jí)網(wǎng)表實(shí)現(xiàn)是完成布局布線最后把網(wǎng)表映射成 FPGA 內(nèi)部的查找表和觸發(fā)器配置文件。在這三步里最容易坑到新人的是綜合后的時(shí)序報(bào)告。abs_module 是純組合邏輯不存在跨時(shí)鐘域問題但 Vivado 依然會(huì)給出 WNSWorst Negative Slack和一個(gè)定時(shí)約束要求。如果代碼里誤加了時(shí)鐘邏輯或者 data_in 路徑上堆了大量組合邏輯導(dǎo)致延遲超標(biāo)綜合后會(huì)出現(xiàn) Timing constraints not met 的告警。這里記住一個(gè)判斷方法純組合邏輯模塊的時(shí)序告警基本可以忽略只要沒有 Fatal Error 或 Ungrouped LUT 報(bào)錯(cuò)但如果模塊內(nèi)部帶了狀態(tài)機(jī)或時(shí)鐘分頻邏輯時(shí)序不收斂就必須處理。處理手段通常是加流水線寄存器或者改組合邏輯結(jié)構(gòu)而不是盲目調(diào)高時(shí)鐘頻率。Vivado 默認(rèn)的時(shí)序目標(biāo)一般是從時(shí)鐘約束里讀出來的如果你沒有在 XDC 里 create_clock仿真和上板都會(huì)按照默認(rèn)值來這通常沒問題但實(shí)驗(yàn)報(bào)告里最好寫明默認(rèn)時(shí)鐘周期和你的余量判斷。4.3 上板下載Run Bitstream 與硬件管理器實(shí)操綜合實(shí)現(xiàn)完成后點(diǎn)擊 Generate BitstreamVivado 會(huì)生成 .bit 文件。接下來打開 Hardware Manager連接開發(fā)板選擇對(duì)應(yīng)的 FPGA 器件點(diǎn)擊 Program Device加載比特流并燒寫。這一步需要在 XDC 里先完成引腳約束并成功跑完實(shí)現(xiàn)否則 Program Device 時(shí)會(huì)出現(xiàn) Unconstrained port 的報(bào)錯(cuò)。上板后把 8 位撥碼開關(guān)分別撥到不同組合觀察 LED 輸出。一個(gè)常見問題是 LED 變化極快肉眼根本捕捉不到正確結(jié)果。這是因?yàn)?abs_module 是組合邏輯輸入一變輸出立刻變而撥碼開關(guān)是手動(dòng)操作對(duì)于人眼來說信號(hào)切換是瞬時(shí)的。所以我通常會(huì)建議在頂層再加一個(gè)按鍵按鍵按下時(shí)用寄存器鎖存當(dāng)前輸入再估值輸出這樣可以在穩(wěn)定的狀態(tài)下核對(duì)結(jié)果。這也是這份資源里 abs 模塊只做組合邏輯的原因——它的接口足夠干凈方便外部例化時(shí)自由添加寄存器層級(jí)。5. 排查手冊(cè)仿真過了上板卻翻車的三個(gè)典型坑5.1 坑一撥碼開關(guān)方向反了整個(gè)輸入全反轉(zhuǎn)現(xiàn)象仿真用例全部通過上板后把撥碼開關(guān)撥到對(duì)應(yīng)位置LED 顯示完全錯(cuò)亂比如輸入 1 顯示 255輸入 2 顯示 254。原因開發(fā)板撥碼開關(guān)的電氣邏輯和你設(shè)計(jì)里假設(shè)的邏輯剛好相反。很多開發(fā)板的撥碼開關(guān) ON 位置是拉到地OFF 位置才是高電平而 RTL 里把高電平當(dāng)成 1所以撥到 ON 時(shí)讀到的是 0整個(gè) 8 位輸入變成了按位取反的結(jié)果。解決先用萬用表或者查開發(fā)板原理圖確認(rèn)撥碼開關(guān)在高電平和低電平下的實(shí)際位置。然后有兩種改法一種是在 XDC 里給 data_in 加set_property PULLUP [get_ports data_in*]配合外部上拉電阻把開關(guān)默認(rèn)狀態(tài)改成高另一種更簡(jiǎn)單的是直接在 RTL 里把輸入取反一次比如寫wire [7:0] data_in_adj ~data_in;。我一般傾向于先確認(rèn)硬件極性再來改 RTL因?yàn)槟愀?RTL 會(huì)讓仿真和上板的輸入定義不一致后續(xù)加用例容易混淆。5.2 坑二abs(-128) 顯示成 128 或 -128但實(shí)驗(yàn)要求是飽和輸出現(xiàn)象把撥碼開關(guān)撥到 10000000-128LED 顯示結(jié)果在 127 和 128 之間跳躍有時(shí)是 128有時(shí)是 128 的補(bǔ)碼形式不穩(wěn)定。原因這里其實(shí)是兩個(gè)問題疊加。第一abs 模塊對(duì) -128 的處理本來就是輸出原值并置溢出標(biāo)志這從電路邏輯上沒錯(cuò)。第二你的 LED 只有 8 位把它當(dāng)成無符號(hào)數(shù)顯示時(shí) 10000000 顯示為 128但如果用負(fù)數(shù)方式解讀它又是 -128這個(gè)歧義來自位寬不足以同時(shí)表示 128 這個(gè)正數(shù)和 -128 這個(gè)負(fù)數(shù)。解決如果實(shí)驗(yàn)題目要求的是“絕對(duì)值輸出”那最穩(wěn)妥的方案是把這個(gè)特殊情況定義為未定義或溢出狀態(tài)并在實(shí)驗(yàn)報(bào)告里明確說明。如果題目要求的是飽和到最大正數(shù) 127那需要在 abs 模塊里把特判分支的 abs_out 改成 8b0111_1111。這兩種做法都合理但必須和題目的驗(yàn)收標(biāo)準(zhǔn)對(duì)齊。我經(jīng)歷過一次課設(shè)答辯功能都實(shí)現(xiàn)了就因?yàn)闆]講清楚 -128 的處理方式被扣了分所以這個(gè)點(diǎn)在報(bào)告里務(wù)必寫明白。5.3 坑三仿真波形有毛刺上板后偶發(fā)錯(cuò)誤但很難復(fù)現(xiàn)現(xiàn)象ModelSim 或 Vivado Simulator 里看到 abs_out 在輸入切換瞬間出現(xiàn)一個(gè)極窄的毛刺時(shí)間極短仿真器默認(rèn)不顯示但放大后能看到。上板后這種毛刺導(dǎo)致后續(xù)電路偶發(fā)采到錯(cuò)誤值。原因組合邏輯的路徑延遲差異導(dǎo)致暫態(tài)競(jìng)爭(zhēng)。比如 data_in 從 00000001 切換到 11111111 時(shí)每一位到達(dá) always 塊內(nèi)部的時(shí)間不同abs_out 在那一瞬間可能輸出一個(gè)中間值。這種毛刺在純組合輸出接 LED 時(shí)肉眼看不出但一旦接到采樣寄存器就可能被采到。解決最直接的方法是不要在組合邏輯輸出端直接做采樣而是在 abs_out 后面加一級(jí)寄存器用時(shí)鐘同步采樣。常見的做法是修改頂層把 abs_out 鎖存到 reg 里再用 LED 輸出。這樣毛刺被時(shí)鐘沿隔離不會(huì)傳到后面電路。仿真里驗(yàn)證毛刺是否消失可以用 Vivado 的find -timescale結(jié)合光標(biāo)放大波形的毛刺區(qū)間來確認(rèn)。從那以后我設(shè)計(jì) abs 這類組合模塊時(shí)都會(huì)預(yù)留一個(gè)可選的同步輸出寄存器成本不高但能讓上板問題和仿真問題徹底解耦。6. 進(jìn)階驗(yàn)證用 ILA 在線調(diào)試替代盲調(diào)把 abs 模塊封裝成可復(fù)用 IP比撥碼開關(guān)點(diǎn)燈更有效的驗(yàn)證方法是使用 Vivado 的集成邏輯分析儀 ILA。ILA 可以嵌入到你的設(shè)計(jì)里實(shí)時(shí)抓取 abs_module 內(nèi)部的 data_in、abs_out、overflow 信號(hào)并且可以設(shè)置觸發(fā)條件。比如你設(shè)置觸發(fā)條件為data_in 8b10000000ILA 會(huì)一直捕捉數(shù)據(jù)直到滿足條件時(shí)把前后若干周期的波形抓下來通過 Hardware Manager 回傳到 Vivado 里查看。這樣不用靠肉眼對(duì)著撥碼開關(guān)猜能直接確認(rèn)上板后 abs_out 的實(shí)際值是不是和仿真一致。我的習(xí)慣是任何帶邊界條件的數(shù)電實(shí)驗(yàn)上板調(diào)試第一件事就是插一個(gè) ILA 核先抓一組邊界輸入確認(rèn)內(nèi)部信號(hào)與仿真無差別再去檢查外圍硬件。另一個(gè)進(jìn)階方向是把 abs_module 封裝成自定義 IP。Vivado Tools 菜單里的 Create and Package New IP 可以把當(dāng)前工程里的 abs_module 打包成一個(gè)帶 AXI 接口的 IP之后在 Block Design 里例化它通過 AXI 總線讀寫 data_in 和 abs_out這樣 abs 就能被嵌入式軟核調(diào)用變成一個(gè)真正可復(fù)用的模塊。這個(gè)操作本質(zhì)上只是在 abs_module 外面套了一層 AXI 寄存器橋內(nèi)部的補(bǔ)碼邏輯不需要改動(dòng)。做完這些驗(yàn)證我個(gè)人的習(xí)慣是每次寫完一個(gè)組合邏輯模塊強(qiáng)制自己走一遍“全用例回歸仿真 RTL 斷言 ILA 在線抓信號(hào)”三條路徑確認(rèn)無誤后再提交代碼。這樣能提前排除大部分上板翻車點(diǎn)也希望這份資源能幫你在數(shù)電實(shí)驗(yàn)的路上少走一些彎路。本文還有配套的精品資源點(diǎn)擊獲取