上一篇:IntelliJ IDEA下开发FPGA-CSDN博客
Type:Quartus
一、安装插件
在应用商店先安装Digtal IDE插件
安装后,把其他相关的Verilog插件禁用,避免可能的冲突。重启后,可能会弹出下面提示
这是插件默认要求的工具链,在扩展设置中可以选择其他几个。这里遵循默认,进入Vivado网址,下载
安装教程可参考Vivado 安装教程 - Digital Lab 2024
安装完成后,可以在VS Code的设置里,找到扩展选项,再找到安装路径设置,注意斜杠的方向
不过在使用时,经常会报下面错误,应该是VS Code的日常bug吧
二、基本使用
1,网表
在编写的模块中,点击Netlist就可以生成网表
在开启右边设置里的渲染动画时,点击每条支路都会有流光动画
2,文档
点击右上角,可以打开文档,里面有该模块的所有总结信息
生成的代码文档足够详细,甚至包含注释的信息
3、代码检查
在语法解析完成的情况下,对代码的检查可谓是相当给力,点击下方的提示可自动转到
而且对System Verilog文件也能进行正常检查,不必担忧中文注释
基于这个代码检查,编写模块文件和测试文件都可以使用表达能力更好的System Verilog
4,git插件
至于前篇提到的内置git不是很好用,在一些插件的帮助下,有了质的提升
5,文件纲要
6,代码格式化
右键屏幕,点击格式化文档,即可自动对齐代码
三、自动流程
1,创建任务
由于FPGA芯片为Intel类型,因此综合(或仿真)需要用到Quartus(或ModelSim)。如想达到如IntelliJ IDEA中非常方便的运行目标,需要配置任务
①创建模板
选择模板创建
选择Others
然后会自动生成配置文件
语法很简单,label是该任务的名称,type是任务的类型,command就是该任务的执行命令,也就是名为label的任务使用shell执行command。这些内容在上篇IntelliJ IDEA的配置运行目标中可以见到,只不过这里更为自由
②运行任务
此时执行任务试试
可以看到输出台上
③配置为生成任务(构建)
点击配置默认生成任务,在点击之前创建的任务即可(这里已经选择过了)
在模板里可以看到,json里添加了新的两个选项(标签)
④使用快捷键
由于VS Code的运行任务不像IntelliJ IDEA之间在上方显示,每次运行时都需要点击【运行】→【运行任务】→【xxx任务】。因此可以把需要执行的任务配置为默认生成任务,然后通过Ctrl + Shift + B快速执行
⑤任务启动插件
当然,这样依旧并不是很方便,比如现在有综合、仿真等任务,来回切换就有些不够清晰直观。为解决这个问题,首先要想的便是插件,可以看到丰富的插件
这里就随便选择一个,比如Task Manager,安装之后左边会出现一个小图标,点击之后就会显示所有任务
光标悬停在任务上面,就会出现绿色的执行键,点击执行就会出现下面图标,简单好用
2,综合任务
①编写脚本
综合
基于上面内容,现在我们可以轻松仿照上篇中关于综合任务的配置。其基本思路是,编写一个ps1脚本,让任务目标使用shell终端来执行ps1脚本,ps1脚本里则是各种终端命令。
任务 → shell脚本 → 工具命令
现在先写一个ps1脚本,与前篇的一样(前篇有详细讲解和代码)
为了让脚本更加通用,工作目录可以通过参数的方式传入,项目名从工作目录里提取
param( [Parameter(Mandatory=$true)] # 强制要求参数 [string]$WORK_DIR # 声明参数变量 ) # 设置程序路径 $QUARTUS_BIN = "E:\Tools\Develop\Embedded\intelFPGA\quartus\bin64" # 从路径提取目录名 $PROJECT_NAME = (Get-Item $WORK_DIR).Name # 初始化环境 $env:PATH = "$QUARTUS_BIN;$env:PATH" Set-Location $WORK_DIR # 执行流程 $commands = @( "quartus_map --read_settings_files=on --write_settings_files=off $PROJECT_NAME -c $PROJECT_NAME", "quartus_fit --read_settings_files=off --write_settings_files=off $PROJECT_NAME -c $PROJECT_NAME", "quartus_asm --read_settings_files=off --write_settings_files=off $PROJECT_NAME -c $PROJECT_NAME", "quartus_sta $PROJECT_NAME -c $PROJECT_NAME", "quartus_eda --read_settings_files=off --write_settings_files=off $PROJECT_NAME -c $PROJECT_NAME" ) foreach ($cmd in $commands) { Write-Host "Run: $cmd" -ForegroundColor Cyan Invoke-Expression $cmd if ($LASTEXITCODE -ne 0) { Write-Host "[Error] $LASTEXITCODE" -ForegroundColor Red exit $LASTEXITCODE } }
任务脚本需要改一下,名称为sythesis,命令为脚本名称。args里是存放各种参数,逗号相当于空格,起分隔作用
{ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", "tasks": [ { "label": "synthesis", "type": "shell", "command": "powershell.exe", // 显式调用 PowerShell "args": [ "-ExecutionPolicy", "Bypass", // 绕过权限限制 "-File", "${workspaceFolder}/usr/scripts/powershell/synthesis.ps1", // 脚本路径 "${workspaceFolder}"// 传入工作目录 ], "options": { "cwd": "${workspaceFolder}" // 设置工作目录为项目根目录 }, // 配置为生成任务 "problemMatcher": [], "group": { "kind": "build", "isDefault": true }, } ] }
执行效果如下
添加报告
为了综合后能显示一些重要信息,可以在上面的ps1脚本末尾添加一些提取信息的命令,通过正则表达式匹配信息。比如可以从output_files目录下的xxx.sta.rpt文件里提取Fmax
# 提取 Fmax 值 $reportPath = Join-Path $WORK_DIR "output_files\$PROJECT_NAME.sta.rpt" if (Test-Path $reportPath) { $content = Get-Content $reportPath -Raw $pattern = '(?s); \w+\s+\d+mV\s+(?<temp>[0-9-]+C)\s+Model Fmax Summary.*?; (?<fmax>\d+\.\d+) MHz' $matches = [regex]::Matches($content, $pattern) if ($matches.Count -gt 0) { $data = @() foreach ($match in $matches) { $temp = $match.Groups["temp"].Value -replace "C", "" $fmax = $match.Groups["fmax"].Value $data += [PSCustomObject]@{ temp = $temp Fmax = "$fmax MHz" } } # 修正列宽计算方式 $tempWidth = [Math]::Max(4, ($data.temp | ForEach-Object { $_.ToString().Length } | Measure-Object -Maximum).Maximum) $fmaxWidth = [Math]::Max(5, ($data.Fmax | ForEach-Object { $_.ToString().Length } | Measure-Object -Maximum).Maximum) # 输出彩色表格 Write-Host "--------------------------------------------------" Write-Host "Fmax Report for $PROJECT_NAME" -ForegroundColor Yellow Write-Host "--------------------------------------------------" # 标题行 Write-Host ("{0,-$tempWidth} {1,-$fmaxWidth}" -f "temp", "Fmax") -ForegroundColor Cyan Write-Host ("{0,-$tempWidth} {1,-$fmaxWidth}" -f "----", "-----------") -ForegroundColor DarkGray # 数据行 foreach ($item in $data) { # 温度用绿色,Fmax用蓝色 Write-Host ("{0,-$tempWidth}" -f $item.temp) -NoNewline -ForegroundColor Green Write-Host (" {0,-$fmaxWidth}" -f $item.Fmax) -ForegroundColor Blue } } else { Write-Host "Failed to find Fmax values in report" -ForegroundColor Red } } else { Write-Host "Report file not found: $reportPath" -ForegroundColor Red }
②查看报告
生成的报告都在output_files目录,其中fit.map和map.rpt是我们需要关注的,在fit.map中,大概两百多行处就是资源消耗的总结报告
由于没有什么rpt文件相关的插件,如果想要更详细的信息,那么还是到Quartus里查看比较好
资源消耗报告
不过既然想做一站式开发,频繁切换开发环境并不好。因此我们可以同前面一样,在脚本后面加一些命令来解析rpt文件获取一些重要信息,比如掌管资源消耗的xxx.fit.rpt文件
需注意,这些脚本的路径都是基于项目路径,只要项目路径传输正确并且output_files能正确在项目路径里生成(这一条是由Quartus自动生成的)
# 提取 资源消耗 $reportPath = Join-Path $WORK_DIR "output_files\$PROJECT_NAME.fit.rpt" if (-not (Test-Path $reportPath)) { Write-Host "Report file not found: $reportPath" -ForegroundColor Red exit 1 } # 读取报告内容 $content = Get-Content $reportPath -Raw # 提取顶层实体名 if ($content -match '; Top-level Entity Name\s+;\s*(?<entity>\S+)\s*;') { $topEntity = $matches.entity } else { $topEntity = "Unknown" } # 定义要提取的关键资源项 $resourcePatterns = @{ "Total Logic Elements" = '; Total logic elements\s+;\s+(.*?)\s*;' "Total Registers" = '; Total registers\*\s+;\s+(.*?)\s*;' "I/O Pins" = '; I/O pins\s+;\s+(.*?)\s*;' "Used LABs" = '; Total LABs:\s+partially or completely used\s+;\s+(.*?)\s*;' "Global Clocks" = '; Global clocks\s+;\s+(.*?)\s*;' "Peak Interconnect Usage" = '; Peak interconnect usage.*?\s+;\s+(.*?)\s*;' "Max Fan-out" = '; Maximum fan-out\s+;\s+(.*?)\s*;' "Memory (M9Ks)" = '; M9Ks\s+;\s+(.*?)\s*;' "PLLs" = '; PLLs\s+;\s+(.*?)\s*;' "Average Interconnect Usage" = '; Average interconnect usage.*?\s+;\s+(.*?)\s*;' } # 提取数据 $resourceData = @{} foreach ($key in $resourcePatterns.Keys) { if ($content -match $resourcePatterns[$key]) { $resourceData[$key] = $matches[1].Trim() } else { $resourceData[$key] = "N/A" } } # 创建表格数据 $reportData = @() foreach ($key in $resourceData.Keys) { $reportData += [PSCustomObject]@{ Resource = $key Value = $resourceData[$key] } } # 自动调整表格列宽 $maxResourceLength = ($reportData.Resource | Measure-Object -Maximum -Property Length).Maximum $maxValueLength = ($reportData.Value | Measure-Object -Maximum -Property Length).Maximum # 输出彩色表格 Write-Host "--------------------------------------------------" Write-Host "Fitter Resource Usage Report for [$topEntity]" -ForegroundColor Yellow Write-Host "--------------------------------------------------" # 标题行 Write-Host ("{0,-$maxResourceLength} {1,-$maxValueLength}" -f "Resource", "Value") -ForegroundColor Cyan Write-Host ("{0,-$maxResourceLength} {1,-$maxValueLength}" -f "--------------------------------", "--------------------------------") -ForegroundColor DarkGray foreach ($item in $reportData) { # 根据资源类型设置颜色 $valueColor = if ($item.Value -match "\d+%\s*\)") { "Cyan" } else { "Magenta" } Write-Host ("{0,-$maxResourceLength}" -f $item.Resource) -NoNewline -ForegroundColor Green Write-Host (" {0,-$maxValueLength}" -f $item.Value) -ForegroundColor $valueColor } Write-Host "--------------------------------------------------"
效果如下,需要什么效果就用正则表达式提取相应信息,生成想要的格式
3,仿真任务
与前篇一致,插件仿真默认使用的是iverilog,那么对sv文件的支持就相当差了,点击爬虫没有反应,但对v文件有反应。
①ModelSim_GUI
那么也如前面,创建一个仿真任务,在原任务下面即可,逗号不要忘记添加
{ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", "tasks": [ { "label": "synthesis", "type": "shell", "command": "powershell.exe", // 显式调用 PowerShell "args": [ "-ExecutionPolicy", "Bypass", // 绕过权限限制 "-File", "${workspaceFolder}/usr/scripts/powershell/synthesis.ps1", // 脚本路径 "${workspaceFolder}"// 传入工作目录 ], "options": { "cwd": "${workspaceFolder}" // 设置工作目录为项目根目录 }, // 配置为生成任务 "problemMatcher": [], "group": { "kind": "build", "isDefault": true }, }, { "label": "modelsim_gui", "type": "shell", "command": "quartus_sh", "args": [ "-t", "E:/tools/develop/embedded/intelfpga/quartus/common/tcl/internal/nativelink/qnativesim.tcl", // TCL 脚本路径 "--rtl_sim", "${workspaceFolderBasename}", "${workspaceFolderBasename}" ], "options": { "cwd": "${workspaceFolder}", // 设置工作目录 // 添加环境变量 "env": { "PATH": "E:/Tools/Develop/Embedded/intelFPGA/quartus/bin64;${env:PATH}" } }, "problemMatcher": [] // 无错误匹配器 } ] }
执行效果如下
②ModelSim_CMD
此时是让ModelSim以命令行的形式运行,原流程是quartus_sh自动读取tcl脚本,然后生成对应的do脚本,再让vsim以命令行的形式执行这个do脚本。如果改写tcl脚本的话,有些麻烦,于是准备使用ps1脚本,让其直接读取qsf文件中仿真的配置,然后生成do脚本,再让vsim执行do脚本。
那么这个ps1脚本可以先使用正则表达式匹配到关键信息,再利用模板生成特定的do脚本
param( [string]$ProjectFloder, [string]$QsfPath, [string]$DoScript, [string]$VsimFloderPath, [string]$SimMode = "RTL" ) # 设置环境变量 $env:PATH = "$VsimFloderPath;$env:PATH" # 强制覆写设置(移除了备份逻辑) if (Test-Path $DoScript) { Write-Host "Force overwriting existing DO script [$DoScript]" Remove-Item $DoScript -Force } # 2. 解析QSF文件 $Config = @{ SimTool = "" # ModelSim-Altera (SystemVerilog) TopEntity = "" # 顶层模块名 TopEntityFile = "" ActiveTestbench = "" TestbenchFile = "" } # 解析状态跟踪 $currentSection = $null Get-Content $QsfPath | ForEach-Object { # 捕获section声明 if ($_ -match '-section_id\s+([^\s]+)') { $currentSection = $matches[1] } switch -regex ($_) { # 关键参数解析 'set_global_assignment\s+-name\s+EDA_SIMULATION_TOOL\s+"(.+)"' { $Config.SimTool = $matches[1] Write-Host "[SimTool]: $($matches[1])" -ForegroundColor Green } # 捕获顶层模块名 'set_global_assignment\s+-name\s+TOP_LEVEL_ENTITY\s+(.+)' { $Config.TopEntity = $matches[1] Write-Host "[TopEntity]: $($matches[1])" -ForegroundColor Green } # 捕获顶层模块文件名 'set_global_assignment\s+-name\s+(VERILOG_FILE|SYSTEMVERILOG_FILE)\s+(.+)' { if($($Config.TopEntityFile) -eq "") { $filePath = $matches[2] $fileName = [IO.Path]::GetFileNameWithoutExtension($filePath) if ($fileName -eq $Config.TopEntity) { $Config.TopEntityFile = $filePath Write-Host "[TopEntityFile]: $filePath" -ForegroundColor Green } } } # 捕获激活的测试平台 'set_global_assignment\s+-name\s+EDA_NATIVELINK_SIMULATION_TEST_BENCH\s+"?(.+?)"?\s' { if ($currentSection -eq 'eda_simulation') { $Config.ActiveTestbench = $matches[1] Write-Host "[ActiveTestbench]: $($matches[1])" -ForegroundColor Green } else { Write-Host "[Warning] Ignoring non-testbench section: $currentSection -> $($matches[1])" -ForegroundColor Red } } # 捕获测试平台文件名 'set_global_assignment\s+-name\s+EDA_TEST_BENCH_FILE\s+(.+)\s+-section_id\s+(.+)' { $filePath = $matches[1] $fileName = [IO.Path]::GetFileNameWithoutExtension($filePath) $testbenchName = $matches[2] # 测试激励文件名与测试激励名称应匹配 if(-not $fileName -eq $testbenchName){ throw "[Error]: Testbench file name mismatch: $fileName != $testbenchName" } # 匹配测试激励文件 if($testbenchName -eq $($Config.ActiveTestbench)) { $Config.TestbenchFile = $matches[1] Write-Host "[TestbenchFile]: $($matches[1])" -ForegroundColor Green } } } } # 对Config里面的内容做一次检查 if($($Config.TopEntityFile) -eq ""){ throw "[Error]: No top entity file found in QSF file" } Write-Host "" # 3. 确定测试平台文件 if (-not [string]::IsNullOrEmpty($Config.ActiveTestbench)) { } else { throw "No active testbench found in QSF file" } # 4. 生成DO脚本 $DoContent = @( "transcript on", "if {[file exists rtl_work]} {", " vdel -lib rtl_work -all", "}", "vlib rtl_work", "vmap work rtl_work", "" ) # 生成仿真命令 $fileDirectory = [IO.Path]::GetDirectoryName($($Config.TopEntityFile)) $logCmd1 = "vlog -sv -work work +incdir+$ProjectFloder/$fileDirectory {$ProjectFloder/$($Config.TopEntityFile)}" $fileDirectory = [IO.Path]::GetDirectoryName($($Config.TestbenchFile)) $logCmd2 = "vlog -sv -work work +incdir+$ProjectFloder/$fileDirectory {$ProjectFloder/$($Config.TestbenchFile)}" $simCmd ="vsim -t 1ps -L altera_ver -L lpm_ver -L sgate_ver -L altera_mf_ver -L altera_lnsim_ver -L cycloneive_ver -L rtl_work -L work -voptargs=`"+acc`" $($Config.ActiveTestbench)" $DoContent += @( "", $logCmd1, $logCmd2, $simCmd, "add wave *", "view structure", "view signals", "run -all", "quit" ) # 5. 安全写入文件 try { $DoContent = $DoContent -join "`r`n" $DoContent | Out-File $DoScript -Encoding ASCII -Force Write-Host "DO script generated: $DoScript" -ForegroundColor Blue # 执行仿真 if ($Config.SimTool -match "ModelSim") { Write-Host "Launching ModelSim..." -ForegroundColor Blue Start-Process vsim.exe -ArgumentList "-c -do `"$DoScript`"" -Wait -NoNewWindow } } catch { if (Test-Path $DoBackupPath) { Move-Item $DoBackupPath $DoScript -Force Write-Host "Restored original DO script" } throw $_ }
同前面一样,再创建一个仿真任务
{ "label": "modelsim_cmd", "type": "shell", "command": "powershell.exe", // 显式调用 PowerShell "args": [ "-ExecutionPolicy", "Bypass", // 绕过权限限制 "-File", "${workspaceFolder}/usr/scripts/powershell/modelsim_cmd.ps1", // 脚本路径 "${workspaceFolder}",// 传入工作目录 "${workspaceFolder}/${workspaceFolderBasename}.qsf",// 传入qsf文件 "${workspaceFolder}/simulation/modelsim/modelsim.do",// 设置do脚本的路径(自己随便设,不一定要在simulation路径) "E:/Tools/Develop/Embedded/intelFPGA/ModelSim/modelsim_ase/win32aloem"// 传入vsim路径 ], "options": { "cwd": "${workspaceFolder}/simulation/modelsim", // 设置工作目录 }, }
启动任务后,ModelSim就以命令行的形式调用,正常进行仿真
需要说明的是,这种方式只生成了do脚本,与之配套的库之类的还是依赖Quartus的tcl脚本生成,也就意味着运行do脚本的目录需要放到simulation目录里,而且前面必须至少执行过一次modelsim_gui任务(确保运行过tcl脚本在simulation目录里生成相应的库)
③查看波形
查看波形已经没必要再Surfer里去查看,因为插件自带一个查看vcd文件的功能。想要生成波形,测试激励文件里必须加上下面这句
initial begin $dumpfile("xxx.vcd");// 填写对应的名称 $dumpvars; end
点击生成的vcd文件后,会出现这样的一个波形图,此时空空如也。点击右上角
选择需要的信号
左边就会出现你需要的信号,右键信号会有一些配置选项
此时,需要点击信号(变亮就行,点亮后波形也更亮),再点击红色箭头指向的位置,移动到最左边,不然波形不一定能看得到(如果太短的话)
调整到合适的位置之后,箭头所指方向,有三种不同的显示方式,旁边还有数值的显示方式。想要调节,那么左边的信号必须被点亮
第一种是数字型,第二种是离散化,第三种是模拟
其他仿真波形,“?”是高阻态,即z。“x”是未知值,紫色是数据帧
4,qsf文件的基础分析
qsf文件对于Quartus相当于CMakeLists或者makefile对于C项目的管理,而目前的自动化流程是在Quartus综合的基础上构建的,因此需要简单理解一些基础的命令来更快的辅助开发。同时为了避免过度修改导致工程出现问题,要么手动备份,要么使用版本控制。
在qsf文件中我们可以看到这样的写法,“#”就是行注释,里面的命令不区分顺序,并且可以出现空行,命令要顶格写且选项参数遵循间要有空格(同时也方面ps1脚本解析)
这些命令不需要强记,因为这里我只要会修改即可,而且仅从“-name”后面的内容就可以看出它大概是干什么的了。比如下面,显然,上面是芯片族,下面是具体芯片型号
set_global_assignment -name FAMILY "Cyclone IV E" set_global_assignment -name DEVICE EP4CE6E22C8
①引脚绑定
qsf文件里面的命令并不一定是整齐排列的,为了便于我们管理,可以主动整理。比如引脚信号,在不确定引脚不常改变的情况下,可以先整理一下引脚,备份引脚便于下次新建工程时或者切换模块时快速恢复引脚
配置引脚时是需要使用Quartus里的引脚绑定窗口,这里只是提供一种备份的思路
②资源文件
如果文件都放在同一目录下,在Quartus里添加文件并不是一件方便的事。我们可以找到类似于下面这样的命令,把它们放到一起,前面可以加上一些注释来表明。-name后面左边的是文件类型,右边是对应的文件
SYSTEMVERILOG_FILE说明它是.sv文件,VERILOG_FILE自然就是.v文件,SDC_FILE是时序约束文件
set_global_assignment -name SYSTEMVERILOG_FILE xxx.sv
set_global_assignment -name VERILOG_FILE xxx.v
测试激励文件与前面类似,不过它只有一种类型EDA_TEST_BENCH_FILE,后面紧跟着文件名(相对路径,相对于项目根目录),-section_id后面跟着的是名称,需要确保测试模块名称、仿真文件名称和这里的名称相同
③顶层文件
这里的顶层文件指的不仅有模块顶层文件,还有测试模块文件。TOP_LEVEL_ENTITY描述的是模块顶层文件,EDA_NATIVELINK_SIMULATION_TEST_BENCH描述的是选中的仿真文件。
需要注意的是它们都是名称而非文件,只不过模块名称与文件名称需要相同罢了
# ======================================================================== # Entity # ======================================================================== set_global_assignment -name TOP_LEVEL_ENTITY regs set_global_assignment -name EDA_NATIVELINK_SIMULATION_TEST_BENCH regs_tb -section_id eda_simulation
此外,由于前面脚本解析的原因,需要确保指定顶层模块的命令放在前面,至少在资源文件定义前面
④测试激励文件绑定
针对于一个仿真测试模块,需要三条命令来约束
连着不容易看懂,可以把它们每三个按空格隔开
前面如果添加了这是激励文件,这里也要同步添加名称,这里的配置不常改,只要把三个复制一下然后改一下名称即可