Tcl脚本管理Vivado工程:实现FPGA开发的可复现、可协作与自动化
1. 从混乱到秩序为什么我们需要Tcl脚本管理Vivado工程如果你在FPGA开发领域摸爬滚打超过一年大概率经历过这样的场景项目中期客户要求回退到两周前的某个版本进行验证。你打开Vivado试图回忆当时到底修改了哪些IP核的参数约束文件是哪个版本Block Design里又动了哪根线。更糟糕的是团队里另一位工程师在他自己的Vivado工程里也做了修改你们俩的工程文件.xpr一合并直接冲突到无法打开。这种依赖图形界面GUI和二进制工程文件进行协作的方式在稍微复杂一点的硬件项目中几乎注定会陷入混乱。这正是“编写Tcl脚本创建整个Vivado工程并通过Git进行管理”这个实践要解决的核心痛点。它不是一个炫技的操作而是FPGA团队工程化、可持续协作的基石。简单来说它的目标是把Vivado工程从一堆黑盒的、不可读的、易冲突的文件转变为一组清晰、可版本控制、可一键重现的脚本。想象一下无论何时何地你只需要一条命令就能从源代码HDL代码、约束文件、IP配置和脚本完整地重建出与当时完全一致的Vivado工程包括所有IP配置、Block Design连接、甚至综合与实现的策略。这带来的不仅是版本的可追溯性更是协作的确定性和效率的质变。核心价值在于三个词可复现、可协作、可自动化。可复现保证了任何历史版本都能被精确重建消除了“在我机器上是好的”这类问题。可协作使得多人可以通过Git高效管理工程变更像管理软件代码一样进行分支、合并、Code Review。可自动化则为持续集成CI铺平了道路你可以让服务器自动检查每次提交的代码是否能成功创建工程、通过综合甚至进行一些基础的时序分析。2. Tcl脚本Vivado工程的“源代码”在深入具体操作前我们必须理解一个根本性的观念转变Tcl脚本才是Vivado工程的“源代码”而.xpr工程文件只是“编译产物”。Vivado本身就是一个内置了Tcl解释器的强大环境其所有GUI操作底层都是在执行Tcl命令。当你点击“Create Project”时Vivado在后台生成并执行了一系列Tcl命令。2.1 Tcl在Vivado中的核心作用TclTool Command Language在Vivado中扮演着绝对核心的角色。它不仅是自动化的工具更是工程状态的描述语言。通过Tcl你可以精确描述工程结构定义项目名称、路径、器件型号、语言标准。声明设计源码添加或移除Verilog/VHDL文件并指定它们的库归属。管理IP核创建、配置、生成输出产品并将其集成到工程中。构建Block Design以命令方式添加IP、连接端口、创建接口实现图形化设计的脚本化。施加约束读取或直接写入XDC约束文件。控制设计流程执行综合、实现、生成比特流并应用各种策略。一个最基本的工程创建脚本骨架如下所示# 设置项目变量 set project_name “my_fpga_project” set board_part “xilinx.com:vc709:part0:1.9” # 以VC709开发板为例 set design_files [list \ “../src/top.v” \ “../src/clock_gen.v” \ ] set constr_files [list “../constr/timing.xdc”] # 1. 创建工程 create_project -force $project_name ./$project_name -part [get_parts -filter “PART_NAME~xc7vx690t*”] set_property board_part $board_part [current_project] # 2. 添加设计源文件 add_files -norecurse $design_files update_compile_order -fileset sources_1 # 3. 添加约束文件 add_files -fileset constrs_1 -norecurse $constr_files # 4. 设置顶层模块 set_property top top [current_fileset] puts “INFO: Project $project_name created successfully.”这个脚本已经包含了创建一个可综合工程的最小命令集。-force参数确保如果目录已存在则覆盖之这在自动化脚本中很常用。update_compile_order命令至关重要它让Vivado根据当前文件集更新编译顺序避免出现找不到模块的警告。2.2 从GUI操作中“录制”脚本对于初学者最快捷的学习方式是利用Vivado的Tcl录制功能。在Vivado GUI中点击菜单栏的“Tools” - “Run Tcl Script…”旁边就有一个“Record”按钮。点击它然后你进行的所有GUI操作创建工程、添加文件、配置IP等都会被实时翻译成Tcl命令并输出在“Tcl Console”或你指定的日志文件中。例如你通过GUI创建并配置一个MicroBlaze处理器核。录制下来的脚本会包含create_bd_cell,apply_bd_automation,connect_bd_net等一系列命令。你可以将这些命令保存下来稍作整理比如将硬编码的实例名替换为变量就得到了构建该Block Design的脚本。这是将已有图形化工程转化为脚本化工程的捷径。注意录制的脚本通常包含大量绝对路径和自动生成的名称如processing_system7_0。为了脚本的通用性你需要将其参数化用变量替代具体的路径和实例名这是脚本能否被版本化管理的关键一步。3. 构建一个完整、健壮的工程创建脚本一个用于生产环境的Tcl脚本远不止是创建工程和添加文件。它需要考虑到工程目录结构的组织、依赖管理、错误处理以及灵活性。下面我们拆解一个更完善的脚本应该包含的模块。3.1 工程目录结构的标准化首先定义一个清晰的目录结构这是所有后续工作的基础。我推荐的目录结构如下my_project/ ├── build/ # 脚本运行后生成的工程目录加入.gitignore ├── scripts/ # 存放所有Tcl脚本 │ ├── create_project.tcl # 主工程创建脚本 │ ├── create_bd.tcl # 创建Block Design的脚本 │ └── config/ # 配置文件如器件型号列表 ├── src/ # RTL源代码 │ ├── hdl/ │ └── ip/ # 自定义IP仓库 ├── constr/ # 约束文件 (.xdc) │ ├── timing.xdc │ └── physical.xdc ├── sim/ # 仿真文件 ├── docs/ # 文档 └── README.md # 项目说明在create_project.tcl的开头我们就应该定义这些路径变量确保所有路径都是相对于脚本位置的从而提高可移植性。# 获取脚本所在目录作为项目根目录 set script_dir [file normalize [file dirname [info script]]] set project_root $script_dir/.. # 定义子目录路径变量 set src_dir “$project_root/src/hdl” set ip_repo_dir “$project_root/src/ip” set constr_dir “$project_root/constr” set build_dir “$project_root/build” # 创建构建目录 file mkdir $build_dir3.2 分模块管理复杂工程对于包含Block Design和多个IP核的复杂工程强烈建议将脚本模块化。主脚本 (create_project.tcl)负责搭建框架创建工程、设置器件、添加基础的RTL和约束。然后它通过source命令调用子脚本。# 主脚本主体部分 create_project -force $project_name $build_dir/$project_name -part $target_part set_property board_part $board_part [current_project] # 添加RTL源码 add_files -norecurse [glob -nocomplain -directory $src_dir *.v *.vhd *.sv] update_compile_order -fileset sources_1 # 添加约束 add_files -fileset constrs_1 -norecurse [glob -nocomplain -directory $constr_dir *.xdc] # 设置IP仓库路径如果需要使用自定义IP set_property ip_repo_paths [list $ip_repo_dir] [current_project] update_ip_catalog # 调用子脚本创建Block Design source ./scripts/create_bd.tcl # 设置包含BD的顶层文件为顶层 set_property top top_wrapper [current_fileset]子脚本 (create_bd.tcl)专注于构建Block Design。它应该从创建或清理BD开始。# 创建或重置Block Design create_bd_design “system_bd” update_compile_order -fileset sources_1 # 添加Zynq Processing System IP create_bd_cell -type ip -vlnv xilinx.com:ip:processing_system7:5.5 ps7_0 apply_bd_automation -rule xilinx.com:bd_rule:processing_system7 -config {make_external “FIXED_IO, DDR” apply_board_preset “1” Master “Disable” Slave “Disable”} [get_bd_cells ps7_0] # 配置PS-PL接口例如启用GP0接口 set_property -dict [list CONFIG.PCW_USE_M_AXI_GP0 {1}] [get_bd_cells ps7_0] # 添加AXI互联IP、其他外设IP... # ... # 连接IP connect_bd_intf_net [get_bd_intf_pins ps7_0/M_AXI_GP0] [get_bd_intf_pins axi_interconnect_0/S00_AXI] # 验证并保存BD设计 validate_bd_design save_bd_design这种模块化的好处是职责清晰。当只需要修改Block Design时你只需关注create_bd.tcl当需要调整工程设置时则修改主脚本。这也更利于Git进行差异比较。3.3 错误处理与脚本健壮性一个健壮的脚本不能假设一切顺利。我们需要加入错误处理让脚本在出错时给出明确信息而不是默默崩溃。# 使用 catch 命令捕获命令执行中的错误 if { [catch { create_project -force $project_name $build_dir/$project_name -part $target_part } errmsg] } { puts “ERROR: Failed to create project: $errmsg” exit 1 # 非零退出码表示失败 } else { puts “INFO: Project created successfully.” } # 检查文件是否存在再添加 set required_file “$src_dir/top.v” if { ![file exists $required_file] } { puts “ERROR: Required source file $required_file not found!” exit 1 } add_files -norecurse $required_file在团队协作中你还可以在脚本开头检查Vivado版本确保所有人环境一致。# 检查Vivado版本 set required_version “2023.2” set current_version [version -short] if { $current_version ! $required_version } { puts “WARNING: This script is tested with Vivado $required_version. You are using $current_version. Proceed with caution.” }4. 与Git的深度集成将工程真正纳入版本控制仅仅有Tcl脚本还不够如何与Git配合才是实现可协作性的关键。这里的原则是版本库中只存储“源材料”和“配方”不存储“成品”。4.1 .gitignore文件的正确配置这是最重要的一步。必须确保Vivado在运行过程中生成的所有临时文件、工程文件、编译产物都不被提交到Git仓库中。一个典型的.gitignore文件内容如下# Vivado工程构建目录 /build/ *.jou *.log *.str *.xpr *.xml *.srcs/ *.gen/ *.runs/ *.cache/ *.hw/ *.sim/ *.ip_user_files/ *.tmp/ # 不需要版本化的IP生成产物 */ip/*/synth/ */ip/*/sim/ */ip/*/bd/ # 不需要版本化的Block Design输出 */bd/*/synth/ */bd/*/sim/ */bd/*/ip/ # 本地用户设置 *.user这样配置后你的Git仓库里将只包含scripts/目录下的所有Tcl脚本。src/目录下的RTL源代码和IP仓库源文件.xci文件。constr/目录下的约束文件。项目文档和README。当团队成员克隆仓库后他只需要运行scripts/create_project.tcl就能在本地build/目录下生成完整的、与仓库状态一致的Vivado工程。4.2 管理IP核与Block Design的源文件IP核.xci文件和Block Design.bd文件是FPGA设计的重要组成部分它们也必须被版本化管理。对于IP核Vivado的IP核源文件是.xci文件。这个文件很小只包含了IP的配置参数。你需要将.xci文件保存在src/ip/这样的目录下并提交到Git。脚本中通过add_files添加.xci文件Vivado会在首次运行时根据它生成所有必要的输出产品如HDL wrapper、仿真模型等这些生成物应被.gitignore忽略。# 添加IP核源文件 add_files -norecurse $ip_repo_dir/my_clk_gen.xci对于Block Design虽然我们使用Tcl脚本创建BD但有时我们也希望保存一个.bd文件作为参考或备份。你可以使用write_bd_tcl命令将当前的BD导出为一个独立的、可重放的Tcl脚本这个脚本比.bd文件更利于版本管理。或者你也可以将.bd文件本身纳入版本控制但要注意.bd是XML格式合并冲突时解决起来比Tcl脚本困难。4.3 基于Git分支的开发流程脚本化工程使得基于Git分支的硬件开发流程成为可能这彻底改变了团队协作模式。功能分支开发当需要添加一个新功能或修改一个外设时工程师从main分支创建一个新分支如feature/add_uart。在该分支上他修改RTL代码并可能更新create_bd.tcl脚本以在BD中添加新的UART IP并连接。提交与推送他将RTL修改和Tcl脚本的修改一并提交到该功能分支并推送到远程仓库。发起合并请求功能完成后他发起一个合并请求Pull Request。此时其他团队成员可以在线审查他的代码修改和脚本修改。他们可以清晰地看到Block Design是如何被改变的这比对比两个二进制.bd文件直观无数倍。自动化验证在合并请求环节可以触发CI/CD流水线例如使用GitLab CI或Jenkins。流水线自动执行以下操作拉取该分支代码。在干净的容器环境中启动Vivado。运行create_project.tcl脚本尝试重建整个工程。运行综合synth_design检查是否有语法错误或逻辑错误。可选运行简单的静态时序分析检查。 如果任何一步失败合并请求就会自动标记为失败阻止有问题的代码合并入主分支。合并与同步审查和自动化验证通过后分支被合并到main。所有其他成员拉取最新的main分支重新运行脚本即可获得一个包含了新UART功能的完整工程。这种流程将软件工程中成熟的最佳实践引入了硬件开发极大地提升了代码质量、团队协作效率和项目的可维护性。5. 进阶技巧与实战中的“坑”掌握了基础方法后一些进阶技巧和实战中遇到的“坑”能让你和你的团队走得更稳。5.1 参数化与配置管理不要将器件型号、时钟频率等参数硬编码在脚本里。应该使用单独的配置文件如project_config.tcl或通过命令行参数传入。# project_config.tcl set project_name “my_project” set target_part “xc7z020clg400-1” set board_part “digilentinc.com:zybo-z7-20:part0:1.0” set top_module “top_wrapper” # 在主脚本中引用 source ./scripts/config/project_config.tcl或者通过命令行传递参数在vivado -mode tcl -source之外使用-tclargsvivado -mode batch -source scripts/create_project.tcl -tclargs “xc7z020clg400-1” “my_project”在Tcl脚本中通过$argv变量获取这些参数。5.2 处理IP核版本升级与锁定IP核升级可能带来接口变化导致脚本失败。一种稳妥的做法是锁定IP版本。在生成IP时在Tcl命令中指定明确的版本号。create_ip -name clk_wiz -vendor xilinx.com -library ip -version 6.0 -module_name clk_wiz_0同时将项目所用到的所有IP的版本信息记录在一个ip_versions.txt文件中并纳入版本控制作为项目依赖的一份清单。5.3 常见问题排查脚本执行顺序问题Vivado Tcl命令有时有隐式依赖。例如必须在add_files添加了IP的.xci文件后才能update_ip_catalog。最安全的做法是严格按照“创建工程 - 添加源文件/IP - 更新编译顺序/IP目录 - 构建BD - 设置顶层”这个基本流程。路径问题这是最常见的错误。始终使用[file normalize]和相对路径基于[info script]来构造绝对路径避免因工作目录不同导致的脚本失败。BD验证失败validate_bd_design命令报错时仔细查看错误信息。常见原因包括接口不匹配、时钟未连接、复位信号未连接等。GUI操作时Vivado有时会自动处理但脚本中必须显式完成所有连接。Git合并冲突当多人修改同一个Tcl脚本时合并冲突不可避免。解决Tcl脚本的冲突比解决二进制文件冲突简单得多。你需要理解双方修改的意图手动合并冲突部分。这再次体现了脚本化相对于二进制工程文件的巨大优势。5.4 将流程扩展到综合与实现工程创建脚本可以进一步扩展将整个编译流程也自动化。你可以编写一个run_impl.tcl脚本在创建工程后自动调用综合、实现、生成比特流并输出报告。# run_impl.tcl launch_runs synth_1 -jobs 4 wait_on_run synth_1 if {[get_property PROGRESS [get_runs synth_1]] ! “100%”} { error “Synthesis failed!” } launch_runs impl_1 -jobs 4 wait_on_run impl_1 if {[get_property PROGRESS [get_runs impl_1]] ! “100%”} { error “Implementation failed!” } launch_runs impl_1 -to_step write_bitstream -jobs 4 wait_on_run impl_1然后在CI流水线中你不仅可以检查工程创建还可以检查综合是否无错甚至检查时序是否收敛。这为“持续验证”提供了可能。从我个人的经验来看从GUI驱动转向脚本驱动和版本控制初期会有一个学习曲线和习惯改变的成本可能会觉得有些繁琐。但一旦团队跨过这个门槛其带来的长期收益是巨大的。它解决了FPGA项目中最令人头疼的版本混乱和协作低效问题。当你需要回溯三个月前的某个bug或者新同事第一天就能搭建起完整的开发环境并重现历史任何一个版本时你会觉得所有前期的投入都是值得的。这不仅仅是技术上的改变更是团队工程文化和开发范式的一次升级。

相关新闻

最长公共子序列(LCS)算法详解:从动态规划原理到文本比对实战

最长公共子序列(LCS)算法详解:从动态规划原理到文本比对实战

1. 项目概述:从“找茬”游戏到算法核心如果你玩过“找茬”游戏,或者对比过两版合同、两份代码的差异,那你已经在直觉上运用了“最长公共子序列”的核心思想。它要解决的,就是在一堆看似杂乱的信息中,精准定位出那些“顺…

2026/10/8 15:00:56 阅读更多 →
Godot 4.0新2D地图编辑器:半小时搭建星露谷风格农场场景

Godot 4.0新2D地图编辑器:半小时搭建星露谷风格农场场景

1. 项目概述与核心价值如果你和我一样,是个独立游戏开发者或者对用Godot引擎做点小玩意儿感兴趣,那你肯定对《星露谷物语》那种精致、治愈的像素风农场场景心动过。但一想到要手动摆放成千上万个瓦片(Tile)来构建地形、道路、河流…

2026/10/8 6:13:50 阅读更多 →
深入解析PX4 ECL EKF:从卡尔曼滤波原理到多传感器融合实践

深入解析PX4 ECL EKF:从卡尔曼滤波原理到多传感器融合实践

1. 从“黑盒”到“白盒”:为什么我们需要深究PX4 ECL EKF的推导 如果你正在使用PX4飞控进行无人机开发,或者对组合导航算法有浓厚的兴趣,那么“扩展卡尔曼滤波”这几个字对你来说一定不陌生。在PX4的导航系统中,ECL EKF&#xff0…

2026/10/7 23:00:08 阅读更多 →

最新新闻

自定义 robbyrussell 主题:打造高效 zsh 终端提示符

自定义 robbyrussell 主题:打造高效 zsh 终端提示符

默认的 robbyrussell 主题,算是 oh-my-zsh 里很多人入坑的第一个主题。绿色的用户名、蓝色的路径、括号里的 git 分支,简单干净,启动也快。我用它当主力主题用了很长一段时间,一直没换,原因就是它足够轻量,…

2026/10/9 2:36:39 阅读更多 →
Claude Code实战手册:从环境配置到高效工作流

Claude Code实战手册:从环境配置到高效工作流

做开发这几年,身边越来越多人开始把AI助手当成日常工具。我自己的主力环境一直在终端里,试过不少AI编程工具之后,Claude Code算是真正留下来陪我干活的那一个。它不是一个花哨的IDE插件,也不是网页对话框,而是直接在命…

2026/10/9 2:36:39 阅读更多 →
Academic Research Skills 完整指南:10 阶段论文流水线,30 秒装好跑通

Academic Research Skills 完整指南:10 阶段论文流水线,30 秒装好跑通

Academic Research Skills 完整指南:10 阶段论文流水线,30 秒装好跑通 【免费下载链接】academic-research-skills Academic Research Skills for Claude Code: research → write → review → revise → finalize 项目地址: https://gitcode.com/Git…

2026/10/9 2:36:39 阅读更多 →
OpenPencil JSX 渲染器:用代码声明式构建设计并导出为 JSX/Tailwind

OpenPencil JSX 渲染器:用代码声明式构建设计并导出为 JSX/Tailwind

前端桌面应用AI 应用MCP 服务 【免费下载链接】open-pencil AI-native design editor. Open-source Figma alternative. 项目地址: https://gitcode.com/gh_mirrors/op/open-pencil 点击查看 免费下载 本文围绕 OpenPencil(开源 Figma 替代方案、AI 原生…

2026/10/9 2:36:39 阅读更多 →
ClawWork LiveBench 视频编辑 Agent 实战:解读 “Support Green Energy“ 30 秒广告的素材清单与成片工作流

ClawWork LiveBench 视频编辑 Agent 实战:解读 “Support Green Energy“ 30 秒广告的素材清单与成片工作流

人工智能AI AgentAgent 评测模型评测工具调用后端前端 【免费下载链接】ClawWork "ClawWork: OpenClaw as Your AI Coworker - 💰 $15K earned in 11 Hours" 项目地址: https://gitcode.com/gh_mirrors/cl/ClawWork 点击查看 免费下载 本篇技…

2026/10/9 2:36:39 阅读更多 →
OpenAI API 中场战事:模型选型、批量任务与成本排查指南

OpenAI API 中场战事:模型选型、批量任务与成本排查指南

这次我们不看某个本地一键包,而是从一个技术人更容易感知的维度拆一个更大的题:OpenAI 的中场战事。GPT 迭代、推理模型、多模态、API 生态、批量任务和 Token 成本,这些东西正在决定 AI 应用落地的“下半场”怎么走。这篇文章不聊太多叙事层…

2026/10/9 2:35:39 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 10:10:36 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 13:34:55 阅读更多 →