仿實(shí)戰(zhàn):原語庫編譯與波形調(diào)試)
1. 為什么要在易靈思Efinity里折騰Modelsim聯(lián)仿接觸易靈思EfinixFPGA的朋友大概率都是從Trion系列或者鈦金系列比如Ti60 F225入的坑。Efinity是官方配套的IDE界面清爽、編譯速度快尤其是對多Die架構(gòu)的Laguna系列支持得比較順手。但真到了要驗(yàn)證邏輯功能的時(shí)候很多人就卡在仿真這一步了——Efinity自帶的仿真能力比較基礎(chǔ)想看波形、想跑testbench、想驗(yàn)證時(shí)序還是得把Modelsim拉進(jìn)來做聯(lián)仿。這個(gè)組合的實(shí)際價(jià)值在于你可以在Efinity里完成綜合和布局布線同時(shí)用Modelsim做功能仿真和時(shí)序仿真兩邊各干各擅長的事。特別是做UART收發(fā)、SPI通信、圖像處理流水線這類需要反復(fù)調(diào)試波形的項(xiàng)目聯(lián)仿能幫你省下大量上板調(diào)試的時(shí)間。我見過太多人上板之后發(fā)現(xiàn)數(shù)據(jù)不對拿示波器抓半天最后發(fā)現(xiàn)是仿真階段就能暴露的時(shí)序問題。這篇文章適合誰看如果你已經(jīng)裝好了Efinity和Modelsim但不知道怎么把這兩個(gè)工具串起來或者你之前一直用Vivado自帶的仿真器轉(zhuǎn)到易靈思平臺后找不到北又或者你正在做Ti60 F225上的項(xiàng)目需要驗(yàn)證原語Primitive級別的行為——那這篇內(nèi)容基本能覆蓋你80%的需求。我會(huì)從原語編譯、庫文件生成、聯(lián)仿配置、波形調(diào)試到常見報(bào)錯(cuò)排查一步步拆開講。需要提前說明的是Efinity的版本迭代比較快不同版本在聯(lián)仿配置上有些許差異。我手頭用的是Efinity 2023.2配合Modelsim SE-64 2020.4這個(gè)組合實(shí)測比較穩(wěn)。如果你用的是更新的版本操作路徑大同小異注意看界面提示就行。2. 聯(lián)仿前的環(huán)境準(zhǔn)備與核心概念梳理2.1 Efinity仿真流程的底層邏輯Efinity的仿真流程和Xilinx、Intel那套有本質(zhì)區(qū)別。Vivado是綜合、實(shí)現(xiàn)、仿真都在一個(gè)工程里閉環(huán)Modelsim通過編譯Xilinx的仿真庫來調(diào)用。Efinity則是把仿真拆成了兩個(gè)階段功能仿真RTL Simulation和時(shí)序仿真Post-Route Simulation而且它不自己維護(hù)仿真庫而是把設(shè)計(jì)網(wǎng)表和原語模型導(dǎo)出成標(biāo)準(zhǔn)的Verilog/VHDL文件交給第三方仿真器去編譯。這就意味著你要在Modelsim里跑Efinity的設(shè)計(jì)必須先把Efinity的原語仿真庫編譯進(jìn)去。這些原語包括IO緩沖、PLL、BRAM、DSP等硬核的仿真模型。Efinity安裝目錄下其實(shí)自帶了這些原語的仿真源文件但格式和路徑需要你手動(dòng)指定。我一開始也納悶為什么Efinity不直接生成一個(gè)編譯好的庫后來想明白了易靈思的芯片系列多Trion、鈦金、Laguna各有各的原語集而且不同速度等級、不同封裝的原語參數(shù)也不一樣。如果預(yù)編譯成庫體積會(huì)非常大而且容易和用戶自己的Modelsim版本沖突。所以官方選擇讓用戶在需要的時(shí)候自己編譯靈活但稍微麻煩一點(diǎn)。2.2 必須搞清楚的三個(gè)目錄在動(dòng)手之前你得先找到Efinity安裝目錄下的這幾個(gè)關(guān)鍵路徑。以Windows默認(rèn)安裝為例原語仿真源文件目錄Efinity_Install/sim/lib/下面按器件系列分文件夾比如trion、titanium、laguna。每個(gè)文件夾里是.v文件就是原語的仿真模型。仿真庫編譯腳本Efinity_Install/sim/下通常有compile_simlib之類的腳本或說明文件告訴你用Modelsim的vlog/vcom命令怎么編譯。工程導(dǎo)出目錄Efinity工程在綜合或布局布線后會(huì)在工程目錄下生成sim/文件夾里面包含functional/和timing/兩個(gè)子目錄分別對應(yīng)功能仿真和時(shí)序仿真的網(wǎng)表文件。注意不同版本的Efinitysim/lib的路徑可能略有不同。如果找不到直接在安裝目錄下搜索*_primitive*.v或者efx_*.v一般就能定位到。2.3 Modelsim版本選擇與避坑Modelsim的版本選擇上我強(qiáng)烈建議用SE-64 2020.4或更新的版本。原因有兩個(gè)一是Efinity導(dǎo)出的網(wǎng)表里可能用到SystemVerilog的一些語法老版本Modelsim支持不好二是64位版本在處理大容量FPGA設(shè)計(jì)時(shí)內(nèi)存占用更合理不容易崩。至于Modelsim DE比如2022.2功能上沒問題但有些朋友反饋在編譯Efinity原語庫時(shí)會(huì)報(bào)一些奇怪的warning。如果你手頭只有DE版本也可以先用著遇到問題再換SE。安裝Modelsim的時(shí)候有個(gè)細(xì)節(jié)不要裝到帶中文或空格的路徑下。我吃過這個(gè)虧Efinity調(diào)用Modelsim的腳本對路徑里的空格處理有問題會(huì)導(dǎo)致編譯中斷。老老實(shí)實(shí)裝到C:\modeltech64_2020.4這種純英文無空格路徑下省心。3. 原語庫編譯把Efinity的硬核模型喂給Modelsim3.1 手動(dòng)編譯原語庫的完整步驟Efinity官方?jīng)]有提供一鍵編譯腳本所以這一步得手動(dòng)來。別怕命令不多但順序和參數(shù)要對。首先在Modelsim里創(chuàng)建一個(gè)新的庫專門放Efinity的原語vlib efinity_prim vmap efinity_prim efinity_prim然后進(jìn)入Efinity安裝目錄下的原語源文件目錄。假設(shè)你用的是鈦金系列Ti60路徑大概是cd Efinity_Install/sim/lib/titanium接著用vlog命令編譯所有.v文件。這里有個(gè)技巧不要一個(gè)一個(gè)編譯用通配符一次性編但要注意文件之間的依賴順序。Efinity的原語文件命名有規(guī)律一般efx_開頭的是基礎(chǔ)原語titanium_開頭的是器件相關(guān)原語。建議先編基礎(chǔ)原語再編器件原語vlog -work efinity_prim -sv efx_*.v vlog -work efinity_prim -sv titanium_*.v-sv參數(shù)是告訴Modelsim按SystemVerilog標(biāo)準(zhǔn)解析Efinity的有些原語模型用了SV語法。如果你不加這個(gè)參數(shù)可能會(huì)報(bào)語法錯(cuò)誤。編譯完成后在Modelsim的Library窗口里應(yīng)該能看到efinity_prim庫里面有一堆模塊。你可以用vdir efinity_prim命令確認(rèn)一下編譯結(jié)果。3.2 編譯過程中的常見報(bào)錯(cuò)與處理報(bào)錯(cuò)一Module efx_pll is not defined這個(gè)通常是因?yàn)樵Z文件沒有全部編譯進(jìn)去。檢查一下是不是漏了某個(gè)子文件夾或者編譯順序不對導(dǎo)致依賴缺失。解決辦法是把所有.v文件列個(gè)清單按字母順序編一遍確保沒有遺漏。報(bào)錯(cuò)二near logic: syntax error這是典型的SV語法支持問題。確認(rèn)你的vlog命令加了-sv參數(shù)。如果加了還報(bào)錯(cuò)可能是Modelsim版本太老不支持某些SV特性。升級到2020.4以上基本能解決。報(bào)錯(cuò)三Cannot open include file efx_define.vhEfinity的原語文件里會(huì)include一些頭文件。你需要把原語目錄下的.vh文件路徑加到vlog的搜索路徑里vlog -work efinity_prim -sv incdirEfinity_Install/sim/lib/titanium efx_*.vincdir后面跟頭文件所在目錄多個(gè)目錄用連接。3.3 驗(yàn)證原語庫是否可用編譯完別急著跑仿真先做個(gè)簡單驗(yàn)證。在Modelsim里新建一個(gè)臨時(shí)工程寫個(gè)最簡單的testbench例化一個(gè)Efinity的原語比如efx_pll然后vsim一下。如果Modelsim能正常加載這個(gè)模塊不報(bào)“module not found”說明庫編譯成功了。我一般會(huì)寫個(gè)這樣的測試module test_prim; efx_pll #( .CLKIN_PERIOD(10.0), .CLKOUT0_DIVIDE(1) ) u_pll ( .clkin(clk), .clkout0(clk_out) ); // 簡單時(shí)鐘激勵(lì) reg clk 0; always #5 clk ~clk; endmodule如果vsim test_prim能跑起來波形里能看到clk_out跟著clk變化那就說明原語庫沒問題了。4. Efinity工程導(dǎo)出與Modelsim聯(lián)仿配置4.1 從Efinity導(dǎo)出仿真文件Efinity工程在綜合Synthesis完成后就可以導(dǎo)出功能仿真文件了。操作路徑是Project - Generate Simulation Files - Functional Simulation。如果是布局布線Place Route之后可以導(dǎo)出時(shí)序仿真文件Project - Generate Simulation Files - Timing Simulation。導(dǎo)出完成后工程目錄下會(huì)多出一個(gè)sim文件夾結(jié)構(gòu)大概是sim/ ├── functional/ │ ├── project_name.v │ ├── project_name_tb.v │ └── ... └── timing/ ├── project_name_post_route.v ├── project_name_post_route.sdf └── ...功能仿真用的是RTL網(wǎng)表時(shí)序仿真用的是布局布線后的網(wǎng)表加上SDF延時(shí)文件。新手建議先從功能仿真入手確認(rèn)邏輯正確后再跑時(shí)序仿真。4.2 在Modelsim里建立聯(lián)仿工程打開Modelsim新建一個(gè)工程路徑指向Efinity工程目錄下的sim/functional。然后把以下文件添加到工程里Efinity導(dǎo)出的設(shè)計(jì)網(wǎng)表文件project_name.v你的testbench文件如果Efinity自動(dòng)生成了_tb.v可以直接用也可以自己寫之前編譯好的efinity_prim庫添加庫的時(shí)候在Modelsim的Start Simulation對話框里切換到Libraries標(biāo)簽頁把efinity_prim庫加進(jìn)去。這樣Modelsim在仿真時(shí)就能找到原語模型了。4.3 仿真腳本的編寫與自動(dòng)化每次手動(dòng)點(diǎn)來點(diǎn)去太麻煩我習(xí)慣寫個(gè).do腳本一鍵完成編譯和仿真。下面是一個(gè)典型的聯(lián)仿腳本# 創(chuàng)建work庫 vlib work vmap work work # 編譯原語庫如果還沒編譯過 # vlib efinity_prim # vmap efinity_prim efinity_prim # vlog -work efinity_prim -sv incdir$env(EFINITY_HOME)/sim/lib/titanium $env(EFINITY_HOME)/sim/lib/titanium/efx_*.v # 編譯設(shè)計(jì)文件和testbench vlog -sv -work work incdir../functional ../functional/project_name.v vlog -sv -work work ../functional/project_name_tb.v # 啟動(dòng)仿真 vsim -t 1ps -L efinity_prim -L work work.project_name_tb # 添加波形 add wave -r /* # 運(yùn)行仿真 run -all把這個(gè)腳本保存為run_sim.do在Modelsim的Transcript窗口里執(zhí)行do run_sim.do就能自動(dòng)跑完整個(gè)流程。腳本里的$env(EFINITY_HOME)是環(huán)境變量指向Efinity安裝目錄。如果你沒設(shè)這個(gè)變量直接寫絕對路徑也行。提示腳本里的-L efinity_prim參數(shù)很關(guān)鍵它告訴Modelsim在仿真時(shí)去efinity_prim庫里找原語模塊。漏了這個(gè)參數(shù)仿真會(huì)報(bào)“module not found”。5. 波形調(diào)試實(shí)戰(zhàn)從UART接收仿真看聯(lián)仿效果5.1 UART接收模塊的testbench設(shè)計(jì)拿一個(gè)實(shí)際的UART接收模塊來演示。假設(shè)波特率115200時(shí)鐘50MHz數(shù)據(jù)位8位無校驗(yàn)1位停止位。testbench的核心任務(wù)是模擬串行數(shù)據(jù)輸入然后檢查接收模塊輸出的并行數(shù)據(jù)是否正確。timescale 1ns/1ps module uart_rx_tb; reg clk 0; reg rst_n 0; reg rx 1; // 空閑時(shí)高電平 wire [7:0] data_out; wire data_valid; // 50MHz時(shí)鐘 always #10 clk ~clk; // 例化UART接收模塊 uart_rx #( .CLK_FREQ(50_000_000), .BAUD_RATE(115200) ) u_dut ( .clk(clk), .rst_n(rst_n), .rx(rx), .data_out(data_out), .data_valid(data_valid) ); // 發(fā)送一個(gè)字節(jié)的任務(wù) task send_byte(input [7:0] data); integer i; begin rx 0; // 起始位 #(8680); // 115200波特率下1位約8680ns for (i 0; i 8; i i 1) begin rx data[i]; #(8680); end rx 1; // 停止位 #(8680); end endtask initial begin // 復(fù)位 #100 rst_n 1; #1000; // 發(fā)送0x55 send_byte(8h55); #10000; // 發(fā)送0xAA send_byte(8hAA); #10000; $stop; end endmodule這個(gè)testbench里send_byte任務(wù)按照UART協(xié)議手動(dòng)翻轉(zhuǎn)rx信號。波特率115200對應(yīng)每位約8680ns1/115200 ≈ 8.68us這個(gè)計(jì)算要準(zhǔn)確否則接收模塊采樣會(huì)錯(cuò)位。5.2 波形窗口的關(guān)鍵觀察點(diǎn)仿真跑起來后波形窗口里重點(diǎn)看這幾個(gè)信號rx串行輸入應(yīng)該能看到起始位拉低、8位數(shù)據(jù)、停止位拉高的完整波形。data_out接收模塊輸出的并行數(shù)據(jù)在data_valid拉高的那一刻應(yīng)該等于發(fā)送的字節(jié)。data_valid數(shù)據(jù)有效標(biāo)志每接收完一個(gè)字節(jié)應(yīng)該產(chǎn)生一個(gè)時(shí)鐘周期的高脈沖。我實(shí)測下來最容易出問題的地方是采樣點(diǎn)偏移。UART接收模塊通常在起始位下降沿后延遲半個(gè)波特率周期開始采樣然后每隔一個(gè)波特率周期采樣一次。如果testbench里的位寬計(jì)算有偏差或者接收模塊的采樣邏輯有bug波形上就會(huì)看到data_out的值和發(fā)送值對不上。5.3 時(shí)序仿真中的SDF反標(biāo)與延遲觀察功能仿真通過后切換到sim/timing目錄做時(shí)序仿真。時(shí)序仿真需要把SDF文件反標(biāo)到網(wǎng)表上Modelsim的命令是vsim -t 1ps -L efinity_prim -L work -sdfmax /u_dut../timing/project_name_post_route.sdf work.project_name_tb-sdfmax表示用最大延時(shí)進(jìn)行仿真適合檢查建立時(shí)間違例。如果想看保持時(shí)間用-sdfmin。時(shí)序仿真跑起來后波形上會(huì)看到信號跳變不再是理想的零延時(shí)而是帶有各種毛刺和延遲。這時(shí)候重點(diǎn)觀察建立時(shí)間數(shù)據(jù)在時(shí)鐘沿到來之前是否已經(jīng)穩(wěn)定。保持時(shí)間數(shù)據(jù)在時(shí)鐘沿之后是否保持足夠長時(shí)間??鐣r(shí)鐘域如果有異步FIFO或握手信號看同步器輸出是否有亞穩(wěn)態(tài)傳播。我遇到過一種情況功能仿真完全正確但時(shí)序仿真里data_valid偶爾會(huì)多出一個(gè)周期的高脈沖。查了半天發(fā)現(xiàn)是組合邏輯的競爭冒險(xiǎn)在時(shí)序仿真里被放大了。這種問題在功能仿真階段很難發(fā)現(xiàn)必須跑時(shí)序仿真才能暴露。6. 常見問題排查與避坑經(jīng)驗(yàn)實(shí)錄6.1 聯(lián)仿報(bào)錯(cuò)速查表報(bào)錯(cuò)信息可能原因解決方法Module efx_xxx is not defined原語庫未編譯或未鏈接編譯efinity_prim庫vsim時(shí)加-L efinity_primnear logic: syntax errorModelsim未啟用SV支持vlog命令加-sv參數(shù)Cannot open include file頭文件路徑未指定vlog加incdirpathSDF file not found時(shí)序仿真未生成SDF確認(rèn)Efinity已執(zhí)行布局布線并導(dǎo)出時(shí)序仿真文件Failed to access library efinity_prim庫映射丟失重新執(zhí)行vmap efinity_prim efinity_prim波形全是紅線X態(tài)復(fù)位未釋放或原語未初始化檢查testbench復(fù)位邏輯確認(rèn)原語庫編譯正確6.2 波形紅線的排查思路Modelsim波形里出現(xiàn)紅線X態(tài)是最常見的調(diào)試場景。在Efinity聯(lián)仿中紅線通常來自三個(gè)源頭第一原語模型未正確初始化。比如PLL的鎖定信號在仿真開始時(shí)是X直到輸入時(shí)鐘穩(wěn)定后才拉高。這是正常行為不用慌。但如果一直不拉高就要檢查PLL的輸入時(shí)鐘頻率是否在允許范圍內(nèi)。第二復(fù)位信號未正確釋放。很多模塊的寄存器在復(fù)位釋放前是X態(tài)。檢查testbench里rst_n的時(shí)序確保在仿真開始后足夠長時(shí)間才拉高。第三跨時(shí)鐘域信號未同步。如果設(shè)計(jì)里有異步信號直接跨時(shí)鐘域仿真時(shí)會(huì)出現(xiàn)X態(tài)傳播。解決辦法是加兩級同步器或者在testbench里對異步輸入做隨機(jī)化處理。6.3 聯(lián)仿效率優(yōu)化的幾個(gè)技巧技巧一只編譯需要的原語。Efinity的原語庫文件很多全編譯一次要幾分鐘。如果你只用到了PLL和BRAM可以只編譯efx_pll.v和efx_bram.v其他不編。這樣能省不少時(shí)間。技巧二用vopt加速仿真。Modelsim的vopt命令可以對設(shè)計(jì)做優(yōu)化大幅提升仿真速度。在vsim之前加一步vopt acc work.project_name_tb -o project_name_opt vsim -t 1ps -L efinity_prim work.project_name_optacc參數(shù)保留信號可見性方便看波形。如果不加優(yōu)化后有些信號會(huì)被裁掉波形里看不到。技巧三分段仿真。如果testbench跑的時(shí)間很長可以分段跑。先跑前10us看復(fù)位和初始化再跑后面的數(shù)據(jù)收發(fā)。用run 10us代替run -all避免一次跑太久卡死。6.4 多Die器件Laguna聯(lián)仿的特殊注意點(diǎn)如果你用的是Laguna系列多Die FPGA聯(lián)仿時(shí)有個(gè)坑跨Die信號在仿真模型里會(huì)有額外的延遲。Efinity導(dǎo)出的時(shí)序網(wǎng)表里跨Die的布線延時(shí)會(huì)被建模成額外的延時(shí)單元。功能仿真時(shí)看不出來但時(shí)序仿真時(shí)跨Die路徑的延遲可能比單Die大好幾倍。我的建議是多Die設(shè)計(jì)在布局布線時(shí)盡量把相關(guān)邏輯約束到同一個(gè)Die里。如果必須跨Die在時(shí)序仿真階段重點(diǎn)檢查跨Die路徑的建立時(shí)間。Efinity的Timing Analyzer里可以看跨Die路徑的詳細(xì)報(bào)告結(jié)合Modelsim的時(shí)序仿真波形一起分析。7. 從聯(lián)仿到上板驗(yàn)證閉環(huán)的最后一公里聯(lián)仿跑通不代表上板一定沒問題但聯(lián)仿能幫你排除掉90%的邏輯錯(cuò)誤。我一般會(huì)在聯(lián)仿通過后做這幾件事再上板第一檢查約束文件。Efinity的約束文件.fdc里時(shí)鐘約束、IO約束、時(shí)序例外都要寫全。聯(lián)仿不檢查約束但上板后約束不對會(huì)導(dǎo)致時(shí)序違例。第二用SignalTap類似的工具做在線調(diào)試。Efinity有Debugger功能可以插入邏輯分析儀核抓取上板后的實(shí)際波形。把聯(lián)仿時(shí)的波形和上板抓的波形對比能快速定位差異。第三做溫度測試。如果是Ti60 F225這類工業(yè)級芯片上板后跑一段時(shí)間用手摸一下芯片溫度。如果燙手檢查一下時(shí)鐘頻率和邏輯翻轉(zhuǎn)率可能需要加散熱片或者降頻。我個(gè)人在實(shí)際操作中的體會(huì)是聯(lián)仿階段多花一小時(shí)上板調(diào)試能省一天。尤其是UART、SPI這種低速接口聯(lián)仿里把各種邊界情況起始位毛刺、停止位提前、數(shù)據(jù)位翻轉(zhuǎn)都測一遍上板基本一次過。反倒是那些覺得“這么簡單肯定沒問題”的模塊上板后最容易出幺蛾子。最后分享一個(gè)小技巧Efinity導(dǎo)出的功能仿真網(wǎng)表里模塊名和信號名可能會(huì)被優(yōu)化掉一些。如果你在波形里找不到某個(gè)信號在Efinity的綜合設(shè)置里把Keep Hierarchy打開重新導(dǎo)出仿真文件這樣層次結(jié)構(gòu)會(huì)保留信號名也不會(huì)被改。代價(jià)是仿真速度稍微慢一點(diǎn)但調(diào)試起來方便很多。