FPGA第一次实验避坑指南:从环境搭建到上板调试
简介这份资源是面向高校软件工程、电子信息等专业学生的FPGA数字系统设计实验报告对应西电软工院选课程第1次实验适合刚接触FPGA开发、需要完成层次化工程创建作业或复习ISE工具链的学习者。压缩包内仅1个docx文档约267KB内容围绕两个实体MY_AND2与MY_OR2的RTL描述展开并用结构化方式描述顶层模块AND_OR涵盖实验原理、实现步骤、测试结果与实验总结四部分同时梳理了FPGA、RTL描述、VHDL、ISE集成环境、HDL编辑器、逻辑门及层次结构描述等核心知识点。报告完整呈现了从新建工程、编写逻辑门代码、语法检查到生成原理图与仿真的流程可帮助读者理解模块调用与信号连接的基本方法快速上手VHDL编程与ISE HDL编辑器的使用。目前已有344人学习下载适合作为实验参考与排错对照。1. 从一份课程作业文档说起FPGA 第一次实验到底在练什么很多同学拿到“XDU软工院选FPGA第1次.docx”这类课程文档时第一反应是找代码、找截图、找能直接交的东西。但真正决定你后面几次实验顺不顺的恰恰是第一次实验里那些看起来最基础的动作工具链装没装对、工程目录怎么建、约束文件写没写、仿真和上板的边界在哪里。我带过几届做 FPGA 课程设计的学生翻车最多的不是逻辑写错而是第一步就把器件型号选错、把 testbench 当顶层、把引脚约束漏掉最后波形出不来还以为是代码问题。这篇笔记就按“第一次实验通常要做什么”来拆把环境搭建、最小可跑工程、仿真验证、上板排查这几件事讲透。适合刚接触 FPGA 的软工方向同学也适合想快速把课程实验跑通、不想在环境上耗三天的人。2. 第一次实验的典型任务拆解从组合逻辑到最小可跑工程2.1 为什么第一次实验几乎都从组合逻辑开始FPGA 课程第一次实验的任务通常不会太复杂常见的是二选一多路器、译码器、半加器、全加器或者简单的 LED 控制。这不是老师偷懒而是因为组合逻辑没有时钟、没有状态输入一变输出就变最适合用来验证“你的工具链是不是通的”。如果你一上来就写状态机一旦波形不对你很难判断是逻辑问题、时钟问题还是约束问题。我一般会建议第一次实验就做两件事一个纯组合逻辑模块加一个带时钟的分频或计数器模块。组合逻辑用来验证仿真流程时序逻辑用来验证时钟约束和上板现象。这样一次实验能把“写代码—仿真—综合—实现—生成比特流—下载”整条链路走一遍后面再做复杂设计时你至少知道每一步该看什么。从选型角度说课程实验常用的器件无非是两类一类是厂商工具链自带的基础器件另一类是实验箱上的具体型号。第一次实验最重要的不是选多高级的芯片而是确认你装的工具版本和实验指导书里写的是不是一致。版本不一致带来的玄学问题比逻辑错误难查得多。2.2 工程目录怎么建后面才不用反复搬家很多人第一次建工程是直接在桌面新建一个文件夹然后把所有文件往里一扔。等到第三次实验要复用第一次的模块时发现路径里全是中文和空格工具直接报错。血泪经验是从第一次实验开始就按固定结构建目录后面能省掉大量重复劳动。我一般会这样组织fpga_lab/ ├── lab1/ │ ├── rtl/ # 设计源码 │ │ └── mux2.v │ ├── sim/ # 仿真文件 │ │ └── tb_mux2.v │ ├── constr/ # 约束文件 │ │ └── lab1.xdc │ └── prj/ # 工具生成的工程文件 └── common/ # 后面实验可复用的模块这个结构的好处是rtl 里只放可综合代码sim 里只放 testbenchconstr 里只放引脚和时钟约束。工具生成的工程文件单独放 prj不跟源码混在一起。后面换工具版本或者换电脑直接把 rtl、sim、constr 三个目录拷走就能重建工程。提示路径里不要出现中文、空格和特殊符号。这不是工具矫情而是很多综合脚本对路径的处理就是按 ASCII 来的一旦出问题报错信息还特别模糊。2.3 一个最小可跑模块二选一多路器加同步计数器下面这个例子把组合逻辑和时序逻辑放在同一个工程里第一次实验可以直接照着改。组合逻辑部分是一个二选一多路器时序逻辑部分是一个带使能的同步计数器。// rtl/mux2.v module mux2 ( input wire a, // 输入 a input wire b, // 输入 b input wire sel, // 选择信号0 选 a1 选 b output wire y // 输出 ); assign y sel ? b : a; endmodule// rtl/counter.v module counter #( parameter WIDTH 4 // 计数器位宽默认 4 位 )( input wire clk, // 时钟 input wire rst_n, // 低电平复位 input wire en, // 计数使能 output reg [WIDTH-1:0] cnt // 计数值 ); always (posedge clk or negedge rst_n) begin if (!rst_n) cnt {WIDTH{1b0}}; else if (en) cnt cnt 1b1; end endmodule这两个模块加起来不到二十行但覆盖了第一次实验最核心的两个点assign 连续赋值和 always 时序赋值。参数 WIDTH 用 parameter 定义后面做分频器或者数码管扫描时可以直接改位宽不用重写模块。2.4 约束文件里最容易漏掉的三行约束文件是第一次实验翻车最集中的地方。很多人代码仿真过了综合也过了一下载发现灯不亮八成是约束没写对。以常见的实验板为例约束文件里至少要有三部分时钟引脚、复位引脚、输出引脚。# constr/lab1.xdc # 时钟引脚假设板载 50MHz 晶振接在 E3 set_property PACKAGE_PIN E3 [get_ports clk] set_property IOSTANDARD LVCMOS33 [get_ports clk] # 复位引脚假设按键接在 C12低电平有效 set_property PACKAGE_PIN C12 [get_ports rst_n] set_property IOSTANDARD LVCMOS33 [get_ports rst_n] # 输出引脚假设 LED 接在 M14 set_property PACKAGE_PIN M14 [get_ports cnt[0]] set_property IOSTANDARD LVCMOS33 [get_ports cnt[0]]这里的关键是 PACKAGE_PIN 必须和实验板原理图一致IOSTANDARD 必须和引脚所在 bank 的电压一致。这两项写错综合能过实现能过但下载后就是没反应。我见过有人把 3.3V 的引脚写成 LVCMOS18结果 LED 微亮查了一下午。注意约束文件里的端口名必须和顶层模块的端口名完全一致大小写敏感。改过顶层端口名之后约束文件一定要同步改。3. 仿真验证第一次实验里最值得花时间的一步3.1 testbench 不是走形式是帮你省下上板调试的时间很多同学觉得仿真麻烦直接综合上板看现象。第一次实验逻辑简单可能侥幸能过但到了后面做状态机或者接口协议时没有仿真你会调到怀疑人生。testbench 的本质是给你一个可控的输入环境让你能在几纳秒内看到信号变化而不是靠按键和 LED 去猜。第一次实验的 testbench 不需要写得多复杂把输入激励给全、把关键信号打出来就够了。下面这个 testbench 同时测了多路器和计数器。// sim/tb_lab1.v timescale 1ns / 1ps module tb_lab1; reg a, b, sel; wire y; reg clk, rst_n, en; wire [3:0] cnt; // 例化多路器 mux2 u_mux ( .a (a), .b (b), .sel (sel), .y (y) ); // 例化计数器 counter #(.WIDTH(4)) u_counter ( .clk (clk), .rst_n (rst_n), .en (en), .cnt (cnt) ); // 生成时钟周期 20ns initial begin clk 0; forever #10 clk ~clk; end initial begin // 初始化 a 0; b 1; sel 0; rst_n 0; en 0; #100; rst_n 1; en 1; // 测多路器 #20 sel 1; #20 sel 0; #20 a 1; b 0; // 让计数器跑一段时间 #200 en 0; #40 en 1; #100 $finish; end // 打印波形方便在终端看 initial begin $monitor(time%0t sel%b y%b cnt%d, $time, sel, y, cnt); end endmodule这段 testbench 里有几个细节值得说。第一时钟用 forever 生成周期 20ns 对应 50MHz和板载晶振一致。第二复位先拉低再拉高模拟上电复位过程。第三en 信号中途拉低再拉高用来验证使能是否真的起作用。$monitor 会把每次信号变化打出来仿真跑完你就能看到 cnt 是不是在 en 为高时才加。3.2 仿真波形里该看什么不该看什么仿真跑起来之后波形窗口里信号很多但第一次实验只需要盯三个地方复位后 cnt 是不是 0、en 为高时 cnt 是不是每个时钟沿加一、sel 变化时 y 是不是跟着变。如果这三条都对说明逻辑没问题可以进综合。不该看的是那些跟第一次实验无关的信号比如综合工具插入的缓冲、时钟树上的中间节点。这些信号在 RTL 仿真里本来就不该出现看了只会干扰判断。如果仿真结果不对排查顺序是先看 testbench 的激励给对没有再看模块端口连接有没有接错最后才怀疑逻辑本身。我见过有人把 testbench 里的 rst_n 初始值写成 1结果计数器一直在复位状态查了半天代码。3.3 综合和实现阶段该盯哪些报告仿真过了之后综合和实现阶段最容易忽略的是报告。第一次实验不需要把每个报告都读一遍但有两个报告必须看综合后的资源利用率报告和实现后的时序报告。资源利用率报告告诉你这个设计用了多少 LUT、多少寄存器。第一次实验的设计很小如果报告里显示用了上千个 LUT那说明你代码写错了大概率是把某个信号综合成了锁存器或者大位宽比较器。时序报告看的是建立时间和保持时间有没有违例。第一次实验时钟频率低一般不会违例但如果约束文件里时钟周期写错了比如把 50MHz 写成 500MHz时序报告会一片红。这时候不用改代码改约束就行。提示综合和实现阶段报的 warning 不要全部忽略。有些 warning 比如“信号被优化掉”或者“端口未连接”往往就是上板没现象的直接原因。4. 上板调试从比特流到现象中间隔着哪些坑4.1 下载前的检查清单生成比特流之后先别急着下载。我一般会按下面这个清单过一遍检查项常见问题确认方式器件型号选错封装或速度等级对照实验板丝印顶层模块选错顶层文件看综合报告里的顶层名引脚约束引脚号或电平标准写错对照原理图逐条核对时钟约束周期写错或漏写看时序报告里的时钟定义比特流文件生成的是否为最新版本看文件时间戳这个清单看起来简单但每一条我都见过有人踩坑。尤其是器件型号有些实验板用的是同一系列不同封装工具里选错之后综合能过下载直接报错。4.2 下载后没现象按这个顺序排查下载成功但现象不对排查顺序应该是先看时钟有没有起来再看复位有没有释放最后看输出引脚有没有配错。时钟有没有起来最直接的办法是用示波器或者逻辑分析仪量晶振引脚。如果没有仪器可以写一个最简单的分频器把时钟分到肉眼能看见的频率去驱动 LED。如果 LED 闪说明时钟没问题如果不闪说明时钟或者约束有问题。复位有没有释放可以看复位按键的电平。有些实验板的按键是常闭的按下才是高电平如果你按低电平有效来写复位就一直生效。输出引脚配错最常见的是把 LED 的引脚号写成了数码管的引脚号。这种错误仿真发现不了只能对照原理图查。4.3 第一次实验里最容易被忽略的复位设计复位在第一次实验里看起来很简单但它是后面所有实验的基础。我一般会建议第一次实验就用异步复位、同步释放的结构而不是简单的异步复位。// 异步复位同步释放 module reset_sync ( input wire clk, input wire rst_async_n, output wire rst_sync_n ); reg rst_meta, rst_sync; always (posedge clk or negedge rst_async_n) begin if (!rst_async_n) begin rst_meta 1b0; rst_sync 1b0; end else begin rst_meta 1b1; rst_sync rst_meta; end end assign rst_sync_n rst_sync; endmodule这个模块的作用是防止复位释放时出现亚稳态。第一次实验可能用不到但后面做跨时钟域或者高速设计时这就是后悔药。早点养成习惯后面能少踩很多坑。5. 避坑与常见问题第一次实验翻车记录5.1 仿真过了但上板没现象现象testbench 里波形完全正确综合实现也没报错下载后 LED 不亮或者数码管不显示。原因九成是约束文件的问题。要么引脚号写错要么电平标准写错要么顶层端口名和约束里的名字不一致。解决先对照原理图核对引脚号和电平标准再检查约束文件里的端口名和顶层模块是否完全一致。如果都没问题用工具里的引脚规划视图看实际分配的引脚和约束是否一致。5.2 综合报错说模块找不到现象综合的时候报“module not found”或者“undefined module”。原因文件没有加到工程里或者文件路径里有中文和空格或者模块名和文件名不一致。解决检查工程的文件列表确认所有 rtl 文件都加进去了。路径改成纯英文无空格。模块名和文件名尽量保持一致虽然 Verilog 不强制但工具有时候会按文件名找模块。5.3 计数器不计数或者计数不对现象仿真里 cnt 一直是 0或者计数跳变没有规律。原因复位一直生效或者时钟没接上或者 en 信号一直是低。解决先看复位信号的电平确认复位已经释放。再看时钟有没有翻转可以在 testbench 里加 $monitor 打印时钟。最后看 en 信号确认在需要计数的时候是高电平。5.4 工具版本和指导书不一致导致报错现象按照指导书步骤操作但工具报错或者菜单找不到。原因指导书写的工具版本和你装的不一样不同版本之间菜单和流程有差异。解决优先按指导书版本装工具。如果装不了就按当前版本的流程走核心步骤是一样的建工程、加源码、加约束、综合、实现、生成比特流、下载。菜单名字变了但功能位置不会差太远。5.5 下载器识别不到设备现象工具里找不到下载器或者识别不到器件。原因驱动没装或者下载器接触不良或者实验板没上电。解决先确认实验板电源灯亮再检查下载器驱动。如果是 USB 下载器换一个 USB 口试试。有些实验板需要先上电再插下载器顺序反了也会识别不到。6. 把第一次实验变成可复用的模板几个进阶习惯第一次实验做完之后最有价值的动作不是交作业而是把它整理成一个可复用的模板。后面每次实验都可以从这个模板开始省掉重复建工程、重复写约束的时间。我一般会保留三样东西一个干净的工程目录结构、一份通用的约束文件模板、一个基础的 testbench 模板。约束文件模板里把时钟、复位、常用外设的约束都写好后面实验只需要改引脚号和端口名。testbench 模板里把时钟生成、复位激励、$monitor 打印都写好后面只需要改例化和激励。还有一个习惯是给每个模块写一个简单的说明注释包括端口含义、参数含义、时序要求。第一次实验的模块很简单不写注释也能看懂但到了后面模块多了没有注释的代码就是黑匣子。我见过有人过了一个月回头看自己的代码完全想不起来当时为什么那么写。验证方法上除了仿真和上板还可以用工具自带的网表查看器看综合后的电路结构。第一次实验的组合逻辑综合出来应该就是一个 LUT时序逻辑综合出来应该是 LUT 加寄存器。如果综合出来的结构和你预期的不一样比如多路器综合出了锁存器那说明代码里有组合逻辑环路或者不完整的条件分支。最后一个技巧是关于时钟的。第一次实验用的时钟频率低随便写写都能跑。但后面做高速设计时时钟约束和时序收敛就是核心问题。我一般会建议从第一次实验开始就养成写时钟约束的习惯哪怕频率很低也要写清楚。这样到了后面你至少知道时序报告该怎么看不会一上来就被一堆违例吓住。我自己带学生做课程设计这些年最大的教训就是第一次实验省下来的时间后面都会加倍还回去。环境没搭好、约束没写对、仿真没跑透到了第三次第四次实验就会集中爆发。反过来第一次实验多花两个小时把流程走顺后面每次实验都能省半天。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Java接口从基础语法到工程实践:默认方法、抽象类与多实现冲突全解析

Java接口从基础语法到工程实践:默认方法、抽象类与多实现冲突全解析

面试问到Java接口(interface)的时候,很多人都有这种感觉:基础语法闭着眼睛能写,一旦被追问“接口里能不能放实例字段”“默认方法和抽象类的真实区别”“两个接口声明了同一个默认方法怎么办”,现场就卡壳。…

2026/10/15 6:00:39 阅读更多 →
VGGNet核心原理与工程实践:从3x3卷积堆叠到特征提取迁移学习

VGGNet核心原理与工程实践:从3x3卷积堆叠到特征提取迁移学习

VGGNet在2014年那届图像分类竞赛里拿了很好的成绩,很多人第一次听说它是因为“深”“规整”“好用”这三个词。放到今天看,VGGNet的价值早就超过了那一次比赛的名次,它用最简单的方式回答了当时最核心的问题:卷积网络到底应该怎么…

2026/10/15 6:00:39 阅读更多 →
多智能体协作系统架构设计:从Agent分工到软件开发自动化实战

多智能体协作系统架构设计:从Agent分工到软件开发自动化实战

1. 项目概述与核心需求解析1.1 为什么“开发团队”需要多智能体重构先说结论:单一智能体在真实开发场景里撑不过三个任务。试过的人都知道,你把一个需求丢给单Agent,它要么在第一步就把上下文窗口塞满,要么在一个错误路径上反复打…

2026/10/15 6:00:39 阅读更多 →

最新新闻

大模型上下文管理:四种模式让AI对话稳定又省钱

大模型上下文管理:四种模式让AI对话稳定又省钱

提到 context-mode,很多人第一反应是“这不就是给模型多喂点上下文嘛”。听起来简单,但真把一个对话式应用推到生产环境,你会发现这玩意儿根本不是一个概念,而是一套需要精细化设计的状态机。我在做某内部知识库问答系统时&#x…

2026/10/15 6:38:47 阅读更多 →
PyTorch工程化实战:数据管线、分布式训练与推理优化全指南

PyTorch工程化实战:数据管线、分布式训练与推理优化全指南

最近刚把一套基于 PyTorch 的视觉检测 Demo 从零写到交付,中间经历了数据加载慢、显存爆炸、分布式训练卡死、ONNX 导出结果对不上等一系列问题。说实话,PyTorch 写模型的上手成本确实低,但真正把它做进项目里,工程上的坑比想象中…

2026/10/15 6:38:47 阅读更多 →
acp-python:统一配置解析与任务编排的轻量级Python工具

acp-python:统一配置解析与任务编排的轻量级Python工具

如果你和我一样,经常在一堆临时脚本里来回倒腾配置文件、命令行参数和环境变量,那你大概率体会过那种“只有参数,没有框架”的痛。脚本一多,每个文件都要自己解析参数、加载配置、处理异常,重复劳动多到让人怀疑人生。…

2026/10/15 6:38:47 阅读更多 →
大模型对话失忆怎么办?上下文模式(context-mode)设计与工程实践

大模型对话失忆怎么办?上下文模式(context-mode)设计与工程实践

做了一段时间大模型应用开发,我发现大家问得最多的其实不是“怎么调 prompt”,而是“为什么我的机器人聊着聊着就失忆了”。前一秒还记住的需求,后一秒就忘得干干净净,甚至把 A 项目的要求套到 B 项目上。这种问题十有八九出在上下…

2026/10/15 6:38:47 阅读更多 →
KMP实战指南:代码共享、跨平台开发与Android面试全解析

KMP实战指南:代码共享、跨平台开发与Android面试全解析

说出来你可能不信,我第一次在简历上写“熟悉Kotlin Multiplatform”的时候,心里其实挺虚的。当时只是拿一个内部工具类项目练过手,连expect/actual的坑都没踩全。但正是那段“半懂不懂”的阶段,让我在后续的面试里被追问最多、也收…

2026/10/15 6:38:47 阅读更多 →
《操作系统真象还原》第一章环境部署实战:从引导扇区到Bochs调试

《操作系统真象还原》第一章环境部署实战:从引导扇区到Bochs调试

多少人都会觉得《操作系统真象还原》第一章“部署工作环境”不过是装个虚拟机、装个Linux、再装几个软件,两小时搞定的事。可实际写到后面章节才发现,当时对环境的理解有多粗糙,后面调引导代码时就有多被动。这一章表面是搭环境,实…

2026/10/15 6:37:47 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/14 7:53:34 阅读更多 →