计算机组成原理CPU课程实验:从单周期到流水线的Verilog实现与调试
简介这份资源是东南大学计算机组成原理课程实验设计的完整交付物面向正在学习CPU设计、需要完成课程大作业或准备体系结构相关实验的本科生与自学者。内容围绕一个单地址指令格式的简单CPU展开涵盖控制单元、内部寄存器、ALU与指令集四大部分主存规格为256×16位指令字16比特、操作码与地址段各8比特支持直接寻址与立即数寻址并配有验证程序。压缩包共734个文件、约56.18MB以VHDL与Verilog源码、coe与mif存储器初始化文件、xdc约束、tcl与bat脚本、仿真日志及Word版实验报告为主完整覆盖设计、综合、仿真到验证的全流程。已有332人学习下载。读者可据此获得可直接运行的工程源码、可复用的测试激励与排错思路以及结构清晰的实验报告模板便于对照理解CPU数据通路与控制逻辑快速搭建并验证自己的设计。1. 从零搭一个能跑通指令的 CPU这门课到底在练什么很多人第一次听到“计算机组成原理 CPU 课程实验”脑子里浮现的是数电课那堆 74 系列芯片和面包板。但真正做过一轮完整设计的人会告诉你这件事的核心根本不是焊电路而是用硬件描述语言从零描述出一个能取指、译码、执行、写回的单周期或流水线处理器再让它跑通一段真实的指令序列。标题里说的“源码及报告”落到实操层面就是两样东西一套可综合的 RTL 代码加上一份能解释每条指令数据通路为什么这样连的设计文档。这门实验通常出现在大三上学期前置课程是数字逻辑和汇编语言。它解决的是一个很具体的痛点你学了那么多指令格式、寻址方式、流水线冒险但这些东西在纸面上是散的只有当你亲手把 PC、IR、ALU、寄存器堆、存储器用 Verilog 连起来看着仿真波形里那条add指令真的把两个寄存器的值加到了目标寄存器知识才算落地。适合谁做适合已经能写基本 Verilog 模块、看得懂时序图、并且愿意花十几个晚上调波形的人。如果你连always (posedge clk)和非阻塞赋值都还没写顺建议先把数字逻辑的实验补完再进来否则会在数据通路上卡很久。2. 指令集选型与数据通路先定边界再画线2.1 为什么大多数课程实验从 MIPS 子集切入选指令集是第一个分叉口。常见做法是取 MIPS 的一个子集比如只保留 R 型算术逻辑指令、lw/sw访存指令、beq分支指令和j跳转指令。理由很实际MIPS 指令长度固定 32 位格式规整译码逻辑不需要处理变长指令的边界对齐问题对第一次做 CPU 的人来说认知负担最小。相比之下x86 那种变长指令集在取指阶段就要处理指令长度预测放到课程实验里属于自找麻烦。我一般会建议把指令集边界写死在一张表里先不写代码把每条指令的格式、操作数来源、结果去向、对 PC 的影响列清楚。这张表后面会直接变成译码器的 case 分支和控制信号真值表。下面是一个最小子集的参考指令格式操作对 PC 的影响addR型rd rs rtPC4subR型rd rs - rtPC4andR型rd rs rtPC4orR型rd rs | rtPC4lwI型rt Mem[rsimm]PC4swI型Mem[rsimm] rtPC4beqI型if(rsrt) PCPC4imm2条件跳转jJ型PC {PC[31:28], addr, 2b00}无条件跳转这张表定下来之后数据通路的宽度和复用关系基本就确定了。比如lw和sw都需要 ALU 算地址所以 ALU 的一个输入必须能选寄存器值或立即数beq需要比较两个寄存器是否相等可以复用 ALU 做减法看零标志也可以单独做一个比较器。这些选择没有绝对对错但会影响后面控制信号的复杂度。2.2 单周期数据通路的模块划分与连接顺序单周期是大多数人的起点因为一个时钟周期完成一条指令没有流水线冒险控制逻辑是纯组合电路。模块划分上我习惯分成这几个文件pc.v、imem.v指令存储器、regfile.v寄存器堆、alu.v、dmem.v数据存储器、control.v主译码器、datapath.v顶层连线。这样分的好处是每个模块可以单独仿真出问题的时候定位范围小。连接顺序上有个血泪经验先把 PC 和指令存储器的取指通路连起来用一个固定的 PC 值读出指令确认imem的地址映射和读出数据正确。然后再接译码器和寄存器堆最后接 ALU 和访存。很多人一上来就把所有模块连完再仿真结果波形里全是红色不定态根本不知道从哪查起。下面是一个寄存器堆的实现片段带写优先和读端口组合输出module regfile ( input clk, input we, // 写使能 input [4:0] waddr, // 写地址 input [31:0] wdata, // 写数据 input [4:0] raddr1, // 读地址1 input [4:0] raddr2, // 读地址2 output [31:0] rdata1, output [31:0] rdata2 ); reg [31:0] regs [0:31]; // 读端口组合输出$0 恒为 0 assign rdata1 (raddr1 5d0) ? 32d0 : regs[raddr1]; assign rdata2 (raddr2 5d0) ? 32d0 : regs[raddr2]; // 写端口同步写入写优先 always (posedge clk) begin if (we waddr ! 5d0) regs[waddr] wdata; end endmodule这段代码里有两个参数需要留意。第一$0寄存器必须硬连到 0不能靠初始化因为综合工具不会给寄存器堆上电赋初值。第二写地址为 0 时禁止写入否则会破坏零寄存器的语义。读端口用组合逻辑是常见做法因为单周期里读和用发生在同一个周期内如果读也做成时序就得额外等一个周期数据通路时序会对不上。2.3 控制信号真值表的推导方法控制信号是单周期设计的核心。主译码器输入是指令的 opcode 和 funct 字段输出是一组多路选择器选择和写使能信号。推导方法很机械对每条指令问三个问题——ALU 的第二个操作数来自寄存器还是立即数写回的数据来自 ALU 还是存储器PC 的下一个值是 PC4、分支目标还是跳转目标把这三个问题的答案列成表控制信号就出来了。比如lw需要 ALUSrc1立即数、MemtoReg1写回来自存储器、RegWrite1、MemRead1add需要 ALUSrc0、MemtoReg0、RegWrite1、MemRead0。分支指令需要 Branch1 并且 ALU 做减法跳转指令需要 Jump1。提示控制信号表建议先用 Excel 或手写列全再翻译成 Verilog 的 case 语句。直接写代码容易漏掉某个信号在特定指令下的取值导致仿真时出现莫名其妙的写回错误。3. 从单周期到流水线把吞吐率提上去的代价3.1 五级流水线的阶段划分与寄存器插入单周期跑通之后下一步通常是改成五级流水线取指 IF、译码 ID、执行 EX、访存 MEM、写回 WB。吞吐率从每周期一条指令变成理想情况下每周期一条但代价是引入了三类冒险结构冒险、数据冒险、控制冒险。阶段划分的关键是在每两级之间插入流水线寄存器。IF/ID 寄存器保存 PC4 和取到的指令ID/EX 保存译码后的控制信号、寄存器读出的两个值和立即数EX/MEM 保存 ALU 结果和写回目标寄存器号MEM/WB 保存访存读出的数据或 ALU 结果。这些寄存器的位宽要仔细算漏掉一个控制信号就会导致后面某一级行为异常。下面是一个 IF/ID 寄存器的示例module if_id ( input clk, input rst, input stall, // 暂停信号用于冒险处理 input [31:0] pc_plus4_in, input [31:0] instr_in, output reg [31:0] pc_plus4_out, output reg [31:0] instr_out ); always (posedge clk or posedge rst) begin if (rst) begin pc_plus4_out 32d0; instr_out 32d0; end else if (!stall) begin pc_plus4_out pc_plus4_in; instr_out instr_in; end // stall 为高时保持原值插入气泡 end endmodule这里的stall信号是后面处理数据冒险时插入气泡用的。注意复位时把指令清成 0而 0 在 MIPS 里对应nopsll $0,$0,0这样流水线启动阶段不会执行无效指令。3.2 数据冒险的前递与暂停判断数据冒险是流水线里最磨人的部分。典型场景是相邻两条指令前一条写寄存器后一条读同一个寄存器。比如add $t0, $t1, $t2 # 在 WB 阶段写 $t0 sub $t3, $t0, $t4 # 在 ID 阶段读 $t0如果什么都不做sub读到的$t0是旧值。解决办法有两种前递和暂停。前递是把 EX/MEM 或 MEM/WB 阶段的结果直接送到 ALU 输入端绕过寄存器堆。暂停是让流水线停一拍等写回完成再读。前递的判断逻辑是如果 EX/MEM 阶段的目标寄存器号等于 ID/EX 阶段的源寄存器号且 EX/MEM 的 RegWrite 有效就把 EX/MEM 的 ALU 结果前递到 ALU 的 A 输入端。同理处理 MEM/WB 阶段。暂停只在lw后面紧跟使用该数据的指令时需要因为lw的数据要到 MEM 阶段结束才可用前递来不及。注意前递优先级要处理好。如果 EX/MEM 和 MEM/WB 同时命中同一个源寄存器应该选 EX/MEM 的结果因为它更新。这个优先级搞反了会出现偶发的数据错误波形上很难看出来。3.3 控制冒险与分支预测的简化处理控制冒险来自分支和跳转指令。在流水线里分支是否跳转要到 EX 阶段甚至 MEM 阶段才知道但此时 IF 阶段已经取了下一条指令。如果分支跳转取错的指令要冲刷掉。课程实验里常见的简化做法是“预测不跳转”也就是默认继续取 PC4 的指令。等分支结果出来如果确实跳转就把 IF/ID 和 ID/EX 寄存器清空同时把 PC 改成分支目标。这样每条跳转指令会损失两个周期。另一种做法是在 ID 阶段就算分支减少冲刷的级数但需要在 ID 阶段加一个比较器。冲刷逻辑实现起来就是在分支跳转时给 IF/ID 和 ID/EX 的复位端一个脉冲或者用多路选择器把指令强制选成nop。我一般用后者因为复位信号在流水线里到处拉容易出毛刺。4. 仿真验证与上板调试怎么确认它真的对了4.1 用测试指令序列覆盖每条指令的数据通路仿真验证的第一步是写一段测试程序覆盖所有实现的指令。这段程序通常用汇编写然后用交叉汇编器转成机器码初始化到指令存储器里。测试序列要包含R 型指令的三种运算、lw和sw的地址计算、beq的跳转和不跳转两种情况、j的跳转。一个常见的测试序列是这样的# 测试 add/sub/and/or add $t0, $zero, $zero # $t0 0 addi $t1, $zero, 10 # 假设实现了 addi addi $t2, $zero, 20 add $t3, $t1, $t2 # $t3 30 sub $t4, $t2, $t1 # $t4 10 and $t5, $t1, $t2 # $t5 0 or $t6, $t1, $t2 # $t6 30 # 测试 sw/lw sw $t3, 0($zero) # Mem[0] 30 lw $t7, 0($zero) # $t7 30 # 测试 beq beq $t3, $t7, label # 应该跳转 addi $t0, $zero, 99 # 不应执行 label: addi $t0, $zero, 1 # $t0 1仿真时重点看几个波形PC 是否按预期变化、寄存器堆的写使能和写地址是否正确、ALU 的输出是否和预期值一致、存储器读写是否发生在正确的地址上。如果某条指令结果不对先看它的控制信号再看数据通路上的多路选择器选择端。4.2 上板调试时先跑通单条指令再跑程序如果实验要求上 FPGA 验证调试策略和仿真不一样。仿真里你可以随便看内部信号上板之后能看的只有 LED、数码管或者串口输出。我一般会先在顶层加一个调试模块把 PC 的值或者某个寄存器的值引到数码管上单步执行或者低速运行确认取指和译码没问题。然后跑一个最简单的程序比如只做add然后写到一个固定的寄存器观察数码管变化。确认单条指令通了再跑完整的测试程序。上板最容易翻车的地方是时钟分频和复位。如果时钟太快数码管刷新看不出来如果复位没处理好PC 可能停在不定态。常见做法是用一个分频计数器把板载时钟降到 1Hz 左右手动按复位键启动。4.3 报告里该写什么数据通路图和控制信号表报告不是代码的堆砌。评审的人想看的是你对数据通路的理解。我建议报告里至少包含一张完整的数据通路图标出所有多路选择器和控制信号一张控制信号真值表列出每条指令下每个信号的取值一段关键指令的波形分析说明每个周期里数据怎么流动以及仿真结果和上板结果的对比。数据通路图不要用工具自动生成的那种密密麻麻的网表手画或者用绘图工具画清楚模块和连线就行。控制信号表要和代码里的 case 语句一一对应这样别人看报告就能复现你的设计。5. 避坑与常见问题那些让波形全红的细节5.1 寄存器堆写优先导致仿真和综合行为不一致现象仿真时add指令写回的值立刻能被下一条指令读到但上板后同样的程序结果不对。原因仿真器对寄存器堆的读写冲突处理可能和综合工具不同。如果读端口是组合逻辑写是时序在同一个时钟边沿读写同一地址时仿真器可能返回新值而实际硬件返回旧值。解决在寄存器堆里显式实现写优先或者在前递逻辑里覆盖这种情况。更稳妥的做法是读端口也用时序但这样单周期时序要重新算。5.2 立即数符号扩展漏掉高位现象lw指令用负偏移量时地址算错访问到非法区域。原因立即数在译码时没有做符号扩展高位补了 0。比如lw $t0, -4($t1)的立即数是0xFFFC如果不扩展成0xFFFFFFFCALU 算出来的地址就偏了。解决在译码阶段对 I 型指令的立即数做符号扩展用{{16{instr[15]}}, instr[15:0]}这种写法。5.3 分支目标地址计算漏掉左移两位现象beq跳转后 PC 落到了错误的位置程序跑飞。原因MIPS 的分支偏移量是以字为单位的实际字节偏移要左移两位。如果直接拿立即数加到 PC4 上地址就错了。解决分支目标 PC4 (立即数 2)。跳转指令的目标地址也要注意J 型指令的地址字段是 26 位要拼上 PC 的高 4 位再左移两位。5.4 流水线前递条件漏判导致偶发错误现象大部分程序跑得对但特定指令序列下结果偶尔错。原因前递逻辑只判断了 EX/MEM 阶段漏了 MEM/WB 阶段或者优先级搞反了。解决把两个阶段的前递条件都写上并且 EX/MEM 优先。调试时可以在前递多路选择器上打波形看命中时选择端是否正确。5.5 复位信号异步释放导致状态机跑飞现象上板后按复位键有时候正常启动有时候直接卡死。原因复位信号是异步的释放时没有和时钟同步导致流水线寄存器在不同周期退出复位。解决加一个复位同步器把异步复位信号打两拍再送到各模块。这个坑在仿真里基本看不出来因为仿真器的复位释放是理想的。6. 把设计推到能跑基准程序一个可复用的验证套路做到能跑通手写测试序列之后下一步值得做的是让 CPU 跑一段稍微像样的程序比如计算斐波那契数列或者做冒泡排序。这一步的价值在于它会暴露你在小测试里看不到的问题循环分支的反复跳转、连续访存的地址递增、寄存器复用带来的冒险组合。我一般会用一个固定的验证套路先用高级语言写一段算法手动翻译成目标汇编再用交叉汇编器生成机器码初始化到指令存储器。然后跑仿真把最终结果和高级语言算出来的值对比。如果对不上就在循环体里设一个断点看每一轮迭代后寄存器的值。这个套路里有个技巧在汇编里预留几个固定的寄存器作为“输出寄存器”程序结束时把结果写进去仿真时只看这几个寄存器的值。这样不用逐条指令查波形效率高很多。比如冒泡排序可以把排序后的数组首地址和长度写到$s0和$s1仿真结束直接看存储器内容。另一个值得做的进阶是给 CPU 加一个简单的异常处理或者中断入口。虽然课程实验不一定要求但加上之后你会被迫理清流水线冲刷、EPC 保存、异常向量跳转这一套逻辑对理解真实处理器的行为很有帮助。实现上就是在 EX 阶段检测异常条件触发时把 PC 改成异常向量地址同时把当前 PC 存到 EPC 寄存器。最后说一个我自己的习惯每改一次 RTL先跑一遍最小测试序列确认没有回归错误再去跑大程序。很多人改完流水线直接跑冒泡排序结果错了不知道是新引入的 bug 还是原来就有的。最小测试序列就是你的后悔药花三十秒跑一遍能省半小时的波形排查。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

数据结构图论编程题全攻略:从建图到Dijkstra与拓扑排序

数据结构图论编程题全攻略:从建图到Dijkstra与拓扑排序

最近在刷某高校《数据结构》慕课的编程练习题,正好做到第八讲。这一讲在整门课里属于“承上启下”的位置——前面的线性表、栈队列、树都打完了基础,后面还有查找排序等着,而第八讲的内容通常落在图论或搜索这一大块,是很多同学的…

2026/10/15 22:47:14 阅读更多 →
AI Agent设计模式实战:从ReAct到多智能体的可靠落地

AI Agent设计模式实战:从ReAct到多智能体的可靠落地

做AI Agent和写业务接口完全是两种思维。业务接口是输入固定、输出固定,最坏情况你也能预判;Agent不是,它要在不可控的模型输出之上构建可控的业务逻辑。Ynuo Agent(依诺)是我在一个内部项目里负责的自主代理代号&…

2026/10/15 22:47:14 阅读更多 →
SSM+Vue就业信息统计系统毕业设计:从数据库设计到答辩全流程解析

SSM+Vue就业信息统计系统毕业设计:从数据库设计到答辩全流程解析

1. 项目解读与核心功能拆解先说一句实在话,每年到这个时候,SSMVue的选题就跟韭菜似的,一茬接一茬。就业信息统计系统这个题目,在毕业设计里是个典型的中等难度项目——你说它难吧,它没有复杂的算法和分布式架构&#x…

2026/10/15 22:47:14 阅读更多 →

最新新闻

ComfyUI 在 Python 3.13 下报 No module named ‘insightface‘ 装不上?手把手完整修复教程

ComfyUI 在 Python 3.13 下报 No module named ‘insightface‘ 装不上?手把手完整修复教程

ComfyUI 在 Python 3.13 下报 No module named insightface 装不上?手把手完整修复教程 【免费下载链接】ComfyUI The most powerful and modular diffusion model GUI, api and backend with a graph/nodes interface. The fastest local inference engine in the …

2026/10/16 1:32:39 阅读更多 →
VR-reversal 实战指南:用 MPV 插件把 3D 视频转 2D 观看、环视缩放并导出 2D 视频

VR-reversal 实战指南:用 MPV 插件把 3D 视频转 2D 观看、环视缩放并导出 2D 视频

音视频VR视频 【免费下载链接】VR-reversal VR-Reversal - Player for conversion of 3D video to 2D with optional saving of head tracking data and rendering out of 2D copies. 项目地址: https://gitcode.com/gh_mirrors/vr/VR-reversal 点击查看 免费下载 …

2026/10/16 1:32:39 阅读更多 →
改进QSGS四参数随机生长法构建三维多孔介质代码

改进QSGS四参数随机生长法构建三维多孔介质代码

①改进的QSGS四参数随机生长法构建三维多孔介质代码。②QSGS四参数随机生长法构建三维多孔介质代码。代码中包含26/8连通去除孤立孔隙(可定向提取xyz方向的连通孔隙),图像平滑,三维展示,切片展示。非均匀性&#xff08…

2026/10/16 1:32:39 阅读更多 →
GetQzonehistory:基于 Python 的 QQ 空间数据备份工具,把历史说说全量导出为 Excel

GetQzonehistory:基于 Python 的 QQ 空间数据备份工具,把历史说说全量导出为 Excel

GetQzonehistory:基于 Python 的 QQ 空间数据备份工具,把历史说说全量导出为 Excel 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 翻看旧说说时,图片…

2026/10/16 1:32:39 阅读更多 →
pdfplumber 页面渲染与可视化调试完整指南:4 个场景配好 PageImage 不踩坑

pdfplumber 页面渲染与可视化调试完整指南:4 个场景配好 PageImage 不踩坑

pdfplumber 页面渲染与可视化调试完整指南:4 个场景配好 PageImage 不踩坑 【免费下载链接】pdfplumber Plumb a PDF for detailed information about each char, rectangle, line, et cetera — and easily extract text and tables. 项目地址: https://gitcode.…

2026/10/16 1:32:39 阅读更多 →
BeanDefinition 合并

BeanDefinition 合并

0 前言1.BeanDefinition有父子关系,有个属性parentName,可设置BeanDefinition的父子关系 2.为啥要设置BeanDefinition的父子关系?因为子类可继承父类属性,抽出共性 3.BeanDefinition的合并过程就是将父子关系的 BeanDefinition 属…

2026/10/16 1:31:39 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

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

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练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/15 9:49:14 阅读更多 →