
1. 项目缘起为什么需要关注片内时钟OSC在FPGA开发中时钟是一切时序逻辑的基石。对于高云半导体Gowin的FPGA器件其内部集成了一个或多个片内时钟振荡器On-Chip Oscillator 简称OSC。很多刚接触高云FPGA的工程师尤其是从其他平台如Xilinx、Altera转过来的朋友可能会习惯性地直接使用外部晶振提供的时钟而忽略了这颗内置的“心脏”。这其实是一个不小的资源浪费甚至可能让设计走弯路。我最初接触高云FPGA时在一个对成本极其敏感、板子空间又非常有限的小型化项目中就遇到了这个问题。客户要求尽可能减少BOM成本和PCB面积。当时我的第一反应是选一颗小巧的外部有源晶振。但在仔细阅读高云GW1N系列的数据手册和用户指南后我发现器件内部已经提供了一个精度尚可的时钟源。如果这个片内OSC能够满足我的时序要求那么省下的不仅仅是一颗晶振、几个电容和PCB面积更是整个电源系统的设计复杂度和潜在的噪声源。然而调用这个片内OSC的过程并不像在代码里写一句clk 1‘b1那么简单。它涉及到IP核的例化、约束文件的编写、以及最关键的性能评估。网上关于高云工具的详细教程相对较少官方文档虽然全面但针对具体场景的“避坑指南”却需要自己摸索。这次我就把自己从“知道有这个东西”到“在项目中稳定可靠地用上它”的完整过程、核心原理和踩过的坑系统地梳理出来。2. 高云片内OSC核心原理与性能边界解析在动手调用之前我们必须先搞清楚我们手里的“武器”到底是什么水平它的优势在哪短板又在哪。盲目使用只会带来后期的时序灾难。2.1 OSC的硬件结构与工作原理高云FPGA内部的OSC本质上是一个基于环形振荡器Ring Oscillator或RC振荡电路的时钟发生器。它与我们熟悉的外部石英晶体振荡器原理截然不同。外部晶振利用石英晶体的压电效应产生极其稳定的频率其精度通常±10~50ppm和温度稳定性非常好但需要外部元件。片内OSC利用门电路的延迟构成振荡环路。其频率主要取决于半导体工艺、工作电压VCC和结温Tj。这是一个模拟电路模块被集成在FPGA的模拟区域。你可以把它想象成一个“自由奔跑的运动员”他的速度频率会受到“体能”电压、“天气”温度和“状态”工艺偏差的影响。而外部晶振更像一个“节拍器”非常精准但需要外部设备。高云不同系列的FPGA其OSC参数不同。例如在GW1N系列中常见的OSC频率有24MHz、48MHz等选项。在GW2A系列中可能提供更高或更低的频率选项。第一步也是最重要的一步就是找到你所使用芯片型号对应的《数据手册》Datasheet查看“Clock Resources”或“On-Chip Oscillator”章节确认其标称频率和精度参数。2.2 关键性能参数与设计考量数据手册里通常会给出几个关键参数理解它们意味着什么直接决定了这个OSC能否用在你的设计中标称频率Nominal Frequency例如 24MHz。这是OSC在典型条件下的中心频率。精度Accuracy通常以百分比%表示例如 ±5%。这是一个综合最差情况下的值。它包含了以下所有因素的影响工艺偏差Process Variation芯片制造过程中不可避免的差异不同芯片之间的OSC频率天生就有差别。电压灵敏度Voltage Sensitivity电源电压波动会直接影响振荡频率。数据手册会给出一个比例如 ±0.5% / 每0.1V VCC变化。温度灵敏度Temperature Sensitivity芯片结温变化的影响。例如在商业级温度范围0°C ~ 85°C内频率可能漂移 ±2%。启动时间Start-up Time从上电或使能到输出稳定时钟所需的时间。通常比外部晶振慢可能在几毫秒到十几毫秒量级。注意±5%的精度意味着在最坏的工艺、电压和温度组合下你实际得到的时钟频率可能在 22.8MHz 到 25.2MHz 之间以24MHz为例。如果你的设计中有UART通信依赖精确的波特率、定时器要求精确计时或需要与外部芯片进行同步通信如SPI、I2C有最高时钟要求就必须严格评估这个波动范围是否可以被接受。我的经验是对于FPGA内部的纯逻辑处理、状态机控制、LED闪烁、按键消抖等对绝对频率不敏感的应用片内OSC是绝佳选择。但对于需要与外界进行精确时序交互的场合比如生成一个标准的25MHz VGA像素时钟或者115200波特率的UART直接使用原生OSC风险很大往往需要配合内部的PLL进行频率综合和稳定。3. 实战调用从IP核配置到约束文件理论分析清楚后我们进入实操环节。高云FPGA的开发环境Gowin云源软件以下简称云源软件提供了图形化的IP核生成工具调用OSC主要靠它。3.1 使用ClkGen IP核配置OSC高云将时钟管理功能包括OSC和PLL都集成在ClkGen这个IP核里。调用步骤如下打开IP核生成器在云源软件中打开或创建工程后在左侧设计管理窗口的“IP Core Generator”上右键选择“Customize IP”。选择ClkGen在弹出的IP列表中找到“CLOCK”分类下的“ClkGen”双击或点击“Customize”。配置OSC参数时钟源选择Clock Source在“Input Clock Source”中选择“Internal OSC”。这是最关键的一步。OSC频率选择下方会出现“OSC Frequency”下拉菜单从中选择你芯片支持的频率如24MHz。输出时钟配置在“Output Clocks”标签页你可以定义由这个OSC直接驱动输出的时钟信号。例如你可以将clkout0设置为与输入相同的24MHz或者如果你启用了内部的分频器可以设置一个更低的分频时钟。但注意这里的简单分频无法改善精度。生成IP核配置完成后点击“Generate”按钮。软件会生成一个.v文件Verilog模块和一个.cst文件约束模板。生成的Verilog模块例化名通常类似Gowin_CLKDIV或Gowin_OSC具体看版本里面会有一个输出端口比如clkout这就是我们需要的时钟信号。3.2 在顶层设计中例化与连接假设生成的IP核文件名为clk_gen_osc.v模块名为clk_gen_osc。我们在顶层模块中需要像例化其他模块一样例化它并将其输出的时钟连接到你的逻辑时钟网络上。module top ( input wire rst_n, // 外部复位低有效 output wire led ); // 1. 声明IP核输出的时钟和可能的锁定信号 wire clk_24m; // OSC产生的24MHz时钟 wire clk_lock; // 通常OSC没有锁定信号但IP核可能提供需看实际生成代码 // 2. 例化ClkGen IP核OSC配置 clk_gen_osc u_clk_gen_osc ( .clkout(clk_24m), // 输出时钟连接到clk_24m网络 // .lock(clk_lock) // 如果有锁定信号则连接 ); // 3. 将clk_24m作为主时钟驱动你的逻辑 reg [23:0] counter; always (posedge clk_24m or negedge rst_n) begin if (!rst_n) begin counter 24‘d0; end else begin counter counter 1‘b1; end end assign led counter[23]; // 用计数器最高位驱动LED实现约0.7Hz闪烁24MHz/2^24 endmodule3.3 编写正确的物理约束.cst文件这是很多新手会忽略但至关重要的一步。仅仅在代码里例化了时钟模块综合工具并不知道clk_24m这个网络应该被当作全局时钟网络来布局布线。我们必须通过约束文件来告知工具。云源软件在生成IP核时通常会同时生成一个约束模板文件.cst。你需要将其中的内容或根据其格式手动添加到你的主约束文件中。一个最核心的约束是“虚拟时钟约束”。因为OSC产生的时钟并没有通过外部引脚输入我们需要为其创建一个虚拟的时钟定义并指定其频率。在你的.cst文件中需要添加如下语句语法可能随版本略有更新请以软件提示为准// 定义一个名为CLK24M的虚拟时钟周期为41.667ns (对应24MHz) create_clock -name CLK24M -period 41.667 [get_ports {clk_24m}]为什么是get_ports虽然clk_24m是内部信号但在约束语境下我们把它视为一个“源端口”。这条约束告诉时序分析引擎“请把clk_24m这个网络当作一个周期为41.667ns的时钟源来进行时序分析和优化。”如果设计中有多个时钟或者使用了OSC后再通过PLL产生其他时钟约束会变得更复杂需要建立时钟分组和约束关系。但对于简单的OSC直连逻辑上述虚拟时钟约束是必须的。踩坑记录我曾忘记添加这个虚拟时钟约束结果布局布线后的时序报告一片混乱建立时间Setup Time和保持时间Hold Time违规非常多。工具因为不知道clk_24m是时钟没有将其分配到专用的全局时钟资源Global Clock Buffer BUFG上导致时钟偏斜Skew极大设计无法稳定工作。加上约束后工具自动将时钟信号布到了全局网络上时序立刻达标。4. 验证、测试与性能评估方法代码写了约束加了编译通过了这还不算完。我们必须验证OSC是否真的在工作并且评估其在实际环境中的性能。4.1 上板基础验证逻辑功能测试最直接的验证方法就是用一个简单的计数器分频后驱动LED闪烁如上文的代码示例。下载到板子上观察LED是否以预期的频率闪烁。例如24MHz时钟24位计数器LED翻转频率是24MHz / 2^25 ≈ 0.715 Hz大约1.4秒亮灭一次。用手表或手机秒表大致计时如果闪烁节奏基本稳定说明OSC已经成功启动并在工作。4.2 精度测量使用逻辑分析仪或示波器要定量评估精度就需要测量工具。有两种常用方法输出测量法在FPGA代码中将OSC时钟直接或经过一个可靠的分频器如2分频引到一个空闲的IO引脚上。使用数字示波器或频率计测量该引脚的输出频率。注意直接输出高频时钟可能会因为IO驱动能力、负载等问题引入额外误差测量较低的分频后时钟如1MHz会更稳定。间接测量法利用OSC时钟驱动一个精确的定时器例如控制一个IO引脚每秒产生一个固定宽度的脉冲。用示波器测量这个脉冲的周期反推时钟频率。这种方法更接近实际应用场景。测量时务必考虑环境在室温25°C、标准供电电压如3.3V下测量一个“标称值”。然后尝试轻微改变供电电压在器件允许范围内或者用电吹风/冷风枪温和地改变板卡温度观察频率变化情况直观感受电压和温度灵敏度。4.3 内部资源与功耗查看使用云源软件的综合实现报告可以查看OSC IP核占用了哪些资源。通常OSC本身是模拟模块不占用数字逻辑资源LUT、FF但它会占用一个全局时钟缓冲器BUFG资源。高云FPGA的全局时钟网络数量有限通常几个到十几个使用OSC就会占用其中一个。在资源紧张的设计中需要统筹规划。功耗方面片内OSC的功耗通常远低于一个外部有源晶振及其驱动电路。在电池供电或低功耗设计中这也是一个优势。可以在软件中通过功耗分析工具进行估算。5. 进阶应用OSC与PLL的协同工作模式单独使用OSC往往无法满足复杂的时钟需求。更常见的模式是“OSC PLL”的组合拳。用OSC作为PLL的参考时钟输入利用PLL的频率合成、倍频、分频、相位调整功能产生一个或多个更稳定、频率更精确的时钟。5.1 为什么需要PLL提升频率稳定性PLL本身是一个闭环控制系统它通过对比反馈时钟和参考时钟的相位差来调整输出能够在一定程度上“追踪”和“稳定”参考时钟。虽然不能改变OSC固有的绝对精度但可以输出一个抖动Jitter更小、更干净的时钟信号对于高速SerDes或ADC采样等应用至关重要。生成非整数频率OSC频率是固定的如24MHz。如果你需要33.333MHz常见于视频处理或125MHz常见于以太网的时钟直接分频无法得到。通过PLL的倍频M和分频D系数可以灵活产生这些频率。多时钟域管理一个OSC通过一个PLL可以同时产生多个不同频率、不同相位的时钟为不同速度的外设或逻辑模块提供时钟源。5.2 在ClkGen IP中配置OSCPLL配置流程与单独配置OSC类似只是在ClkGen IP中时钟源依然选择“Internal OSC”并指定频率。在下方或另一个标签页中使能PLL功能。配置PLL的输入时钟来源为“内部OSC的输出”。详细配置PLL的输出时钟参数每个输出通道的倍频系数、分频系数、相位偏移等。软件会自动计算并显示生成的频率是否在PLL的有效工作范围内。生成后你会得到一个同时包含OSC和PLL功能的IP核输出端口可能就是PLL稳定后的多个时钟。5.3 此模式下的约束要点当使用OSC驱动PLL时约束的重点转移到了PLL的输出时钟上。你需要为PLL产生的每一个输出时钟创建虚拟时钟约束。// 假设OSC为24MHzPLL产生clk_out148MHz clk_out212MHz create_clock -name CLK48M -period 20.833 [get_ports {pll_clk_out1}] create_clock -name CLK12M -period 83.333 [get_ports {pll_clk_out2}]同时如果PLL有锁定信号locked这个信号非常重要。它指示PLL的输出时钟何时达到稳定状态。你的设计逻辑应该在系统上电复位后等待locked信号变高才能开始使用PLL输出的时钟否则可能因为时钟不稳定而导致逻辑错误。always (posedge clk_24m or negedge sys_rst_n) begin if (!sys_rst_n) begin system_enable 1‘b0; end else begin // 等待PLL锁定后才使能后续逻辑 system_enable pll_locked; end end // 后续的关键逻辑用 system_enable 作为使能条件 always (posedge pll_clk_out1) begin if (system_enable) begin // ... 你的核心逻辑 end end6. 常见问题排查与设计建议在实际项目中调用片内OSC我遇到过不少问题这里总结几个典型的6.1 问题编译无错但下载后设计不工作LED不亮。排查思路检查约束首先确认是否为OSC输出的时钟信号添加了正确的虚拟时钟约束create_clock。这是最高发的原因。检查复位确保你的复位逻辑正确。OSC的启动需要时间上电后应立即给整个设计一个足够长的复位脉冲毫秒级确保OSC稳定后再释放复位。可以在代码中用一个大的计数器实现上电延时复位。测量时钟用示波器测量连接到IO引脚上的时钟信号如果引出了的话看是否有波形。如果没有可能是OSC IP核未正确例化或使能。查看报告查看云源软件编译后的“时钟网络报告”确认clk_24m或你的时钟网络名是否被正确识别为时钟并分配到了全局时钟线上。6.2 问题通信接口如UART误码率高。原因分析这几乎肯定是时钟精度问题。UART的波特率由系统时钟分频得到。如果OSC频率漂移了±5%那么生成的波特率误差也可能接近±5%。而UART通信通常能容忍的波特率误差在2-3%以内。解决方案使用PLL如前所述用PLL并不能提高绝对精度但能提供更稳定的时钟减少短期抖动有时能改善情况。软件校准如果微小的误差是固定的主要由工艺偏差导致可以在软件中微调分频系数。例如实测时钟为24.5MHz那么计算115200波特率的分频系数时就使用24.5M而不是24M。换用外部晶振对于要求严格的通信协议最可靠的办法还是使用外部高精度晶振。6.3 设计建议何时用何时不用推荐使用片内OSC的场景对成本、面积、功耗极度敏感的低功耗消费类电子产品。功能简单仅需时钟驱动内部状态机、计数器、PWM等无对外精确时序接口。作为冗余或备份时钟源当外部时钟失效时启用。在原型验证阶段快速搭建测试环境省去焊接晶振的麻烦。不建议单独使用片内OSC的场景需要与外部世界进行精确时序交互UART, I2S, SPI, USB, Ethernet。需要产生特定频率的视频像素时钟如25MHz, 27MHz, 74.25MHz。高速数据采集或处理系统对时钟抖动要求高。工业级或汽车级应用环境温度变化大。最后一点个人体会高云的片内OSC是一个非常有价值的资源但它不是一个“免费用且万能”的时钟。把它用好的前提是充分理解其特性并通过严谨的约束和测试来驾驭它。在那些它擅长的领域它能为你省下真金白银和宝贵的板级空间而在它不擅长的领域强用只会带来调试的噩梦。我的习惯是在新项目评估时会优先考虑能否利用片内OSC并明确其性能边界做出权衡。这份从数据手册到代码、从约束到测试的完整流程希望能帮你把这块资源稳稳地用起来。