从仿真波形到硬件实测在Zynq-7000上跑通Xilinx DDS IP核的完整流程含资源消耗分析做便携测试设备里那块信号源板卡的时候板子主控选了Zynq-7000系列的xc7z020FPGA侧要出一路频率可调的正弦波频率范围要求从几十kHz覆盖到几MHz还得支持扫频。刚开始我老老实实在RTL里手写DDS相位累加器、查找表、截位策略全自己来结果发现想在输出波形干净度和频率步进精度之间找到平衡点调试成本远比我预想的高。后来换成Xilinx DDS Compiler IP核从仿真验证一路跑到硬件实测整个过程顺畅了很多也把资源消耗数据完整记录了下来。这篇文章就是这次实践的完整复盘适合正在做信号生成、通信基带、或者刚接触Xilinx IP核的FPGA工程师参考内容包括为什么选这个方案、IP核参数怎么配、仿真怎么验、上板怎么调以及最后那笔资源账怎么算。1. 为什么我放弃了手写DDS转向Xilinx DDS Compiler IP核1.1 手写DDS时我踩过的那些坑手写一个基础版DDS并不难核心就是一个N位相位累加器每个时钟周期累加一次频率控制字溢出自然产生周期性然后用累加器的高位去查正弦查找表。但在实际工程里问题往往出在“看起来能跑”和“真的能用”之间的那些细节上。第一个坑是相位截断。累加器位宽N很大比如32位但查找表地址位宽不可能也做32位只能截取高位。截断位数的多少直接决定杂散水平截多了波形难看截少了ROM资源爆炸。关键是你自己写代码的时候很难量化“到底截几位才够”只能一遍遍地看频谱上的杂散然后反复试。第二个坑是查找表实现方式。用ROM还是分布式RAM单端口还是双端口要不要做乒乓切换这些决定资源占用和最高工作频率可是手写的时候这些都得自己操心。第三个坑更隐蔽——输出端毛刺。相位累加器溢出那一瞬间查找表地址跳变会引入毛刺尤其当输出位宽较大、组合逻辑链较长时毛刺会被放大。为了消毛刺我不得不在输出级加寄存器于是整个模块的延迟和时序全变了又得重新调。说实话这些坑每个单拎出来都能解决但合在一起就是纯粹的时间黑洞。1.2 DDS工作的数学内核相位累加器与查找表不管手写还是用IP核DDS的原理都是一样的。核心公式是输出频率f_out与系统时钟f_clk、相位增量Δθ和相位累加器位宽N的关系f_out f_clk × Δθ / 2^N这个公式虽然是整个系统的基石但真正用起来有几个容易忽略的点。第一Δθ取整会引入频率误差误差上限是频率分辨率的一半第二相位累加器溢出不是错误恰恰是产生周期的机制它天然就是模2^N运算第三N越大频率分辨率越细但查找表不可能跟着做那么大所以相位截断是必然的。频率分辨率这个指标也很值钱。比如系统时钟100MHz如果相位累加器是16位分辨率只有100MHz / 65536 ≈ 1525.9Hz也就是说你调频率的最小步进大于1.5kHz在很多应用里根本不够用。把N提到32位分辨率就变成100MHz / 2^32 ≈ 0.0233Hz步进精细得多。我知道有人会质疑既然原理这么清楚自己写和用IP核有什么区别区别在于IP核把这些数学关系封装成了参数化配置你只需要告诉它“我要多少位相位累加器、多少位输出、多少个通道、目标SFDR是多少”它会自动帮你选内部结构和资源方案同时把接口整理成统一的标准时序。这种“把数学封装成参数”的省心在项目后期追时序、追资源的时候才体会得最明显。2. IP配置面板里的每个参数到底在决定什么2.1 我的开发环境与工程组织方式先交代一下这次实践的环境Vivado版本是2021.2器件型号是xc7z020clg400-1DDS Compiler IP核版本是6.0。Zynq-7000系列本质上就是一个ARM Cortex-A9双核处理器加一块7系列可编程逻辑所以DDS IP核在PL侧跑PS侧可以通过AXI总线去改写频率控制字实现动态变频。工程目录我习惯这样组织rtl/放自己写的逻辑ip/放生成的IP核constraints/放约束文件sim/放testbenchscripts/放Tcl脚本。这样做的好处是当工程换人接手或者换Vivado版本重建时只要顺着目录结构和Tcl脚本就能把工程复原不会出现“中间某个IP配置参数忘了当时怎么选的”这种尴尬情况。2.2 五个关键配置项的取舍逻辑打开DDS Compiler IP核配置界面第一眼会看到Operating Mode下拉框有Phase Generator、SIN/COS LUT only和Phase Generator and SIN/COS LUT三个选项。我在这个项目里选的是第三项因为不仅需要正弦波还同时需要余弦波做正交输出用后文会提到的方案直接生成IQ两路比后期再用Hilbert变换或者延迟线做正交要干净得多。第二项是Number of Channels这次选1如果做多通道波束赋形这类应用可以选多个通道DDS内部会用时间分片方式复用查找表资源占用不会线性增长但通道间会引入固定的流水延迟这个要心里有数。第三项是Phase Increment Programmability也就是相位增量可编程性。如果选Fixed频率就固定了只能在Vivado里配死选Programmable则IP核会预留s_axis_config接口每次配置时通过AXI4-Stream写入新的相位增量值。因为项目要求扫频这必须选Programmable。第四项是Phase Width相位累加器位宽。前面算过100MHz时钟下选择32位频率分辨率约0.0233Hz完全够用。相位宽度从16提到32资源增加并不明显所以建议尽量留够。第五项是Output Width输出数据位宽。这个参数直接决定幅度量化的精度大约输出每增加1位量化噪声功率下降约6dBSFDR大约提升6dBc。项目里我选了16位配合相位截断策略最终把SFDR控制在80dBc以上。最后还有一个容易忽略的选项叫Noise Shaping如果选择Phase Dithering会在相位累加结果里注入一个小随机扰动打破相位截断产生的周期性杂散。原理和音频DAC的抖动类似相当于把杂散频谱“摊平”成底噪代价是底噪会略微抬高但SFDR指标往往更好。对通信系统来说这种“把尖峰摊平”的行为通常是有利的。2.3 接口选型AXI4-Stream为什么比Native顺手DDS IP核有两种输出接口Native和AXI4-Stream。Native接口很简单直接输出波形数据和tvalid信号适合不需要和系统总线打交道的场合。AXI4-Stream则多了一套ready/valid握手协议初看觉得多此一举实际用下来优势不小。最直接的好处是如果后续要在输出端接一个自己写的数字下变频、FFT或者DAC驱动模块对方大概率也是AXI4-Stream接口直接用tvalid/tready握手就能串起来不用再写额外的格式转换逻辑。另外在调试阶段用System ILA抓信号时AXI4-Stream接口的tvalid信号本身就是很好的触发条件比在Native接口上另加调试逻辑方便得多。配置接口s_axis_config也一样我最终选了AXI4-Lite的变体写法在PS侧用一段简单的寄存器读写操作就能把频率控制字下发到DDS IP核。这意味着硬件调试时甚至不用动FPGA逻辑直接在SDK里写个循环改寄存器就能看到波形频率变化整个调试体验比纯逻辑方案顺滑很多。3. 仿真阶段从testbench到波形怎么确认DDS真的在干活3.1 testbench的写法与配置时序仿真阶段的核心任务只有一个确认IP核的配置时序没搞错输出波形频率、幅度符合预期。DDS IP核的数据手册里最关键的时序图是s_axis_config接口上的操作逻辑其实不复杂先把s_axis_config_tvalid拉高把相位增量写入s_axis_config_tdata然后等tready信号拉高一段时间后再把tvalid拉低。难点在于不同的配置选项下这个时序关系可能有细节差异最好直接照着手册波形写。我写testbench时的方式是先用阻塞赋值把复位信号拉低再拉高等100个时钟周期让IP核完成初始化再发起一次配置操作。配置的相位增量值按前面那个公式算100MHz时钟、32位累加器、想输出1MHz正弦波Δθ 1e6 × 2^32 / 100e6 ≈ 42949673。这个值在testbench里用一个parameter定义好仿真时如果输出频率不对最容易查的就是这个值是不是算错了。配置结束后观察m_axis_data_tvalid信号什么时候拉高。这个信号代表输出数据有效只有等它拉高后读出来的波形数据才有意义。我看到过有人在tvalid拉高之前就去采数据结果波形头上有几个周期的乱码还以为是IP核坏了其实是自己读早了。3.2 从波形里读频率、读幅度、读SFDR仿真通过的标准不只是“有条波形出来”而是要量化验证。频率验证最简单的方法是数周期100MHz采样时钟下输出1MHz正弦波一个周期应该刚好100个采样点。在Vivado Simulator里打开波形视图把游标对准两个相邻波峰测量两个波峰之间的时间差算一下频率是否接近1MHz。误差允许在频率分辨率范围内因为Δθ取整会引入最多0.0233Hz级别的小偏差但肉眼在波形图上根本看不出来。幅度验证的关键是理解输出数据是有符号定点数。DDS IP核在16位输出宽度下默认输出格式是二进制补码范围是-32768到32767对应的是-1.0到1.0左右的归一化幅度。如果你需要真实电压值就得在外部再乘一个幅度因子或者用IP核的幅度可编程功能。仿真时直接检查波形峰值的绝对值是否接近32767就能判断幅度是否满幅输出。至于SFDR仿真阶段最有效的验证方式是把输出数据导成文本文件扔到Matlab里做FFT然后找基波功率和第二大杂散之间的差值。这里要特别注意FFT点数至少要选65536点否则频谱分辨率不够杂散可能被频谱泄漏淹没看不出真实SFDR水平。实测下来在相位截断和幅度量化都配置合理的情况下16位输出的DDS IP核仿真SFDR超过80dBc是合理的。3.3 仿真通过不等于万事大吉的两点提醒第一仿真波形是理想时序下的结果它不会反映布线延迟、时钟抖动、电源噪声这些硬件因素。我这次在仿真里看到波形很干净但上板后实测波形的高频段多了一些毛刺后来排查发现是输出引脚驱动强度配置偏大导致振铃。这种情况在仿真里完全看不到所以“仿真老通过、上板就翻车”太常见了。第二仿真完成不等于IP核没有踩到文档里的配置限制。比如DDS Compiler IP核要求输出通道数和一些配置选项之间有固定组合关系如果在配置界面里选了一个不合法的组合Vivado在生成IP核时可能只给个warning仿真跑起来也没问题但在某个特定参数组合下硬件实测就会出怪问题。所以我建议在仿真前先花几分钟把配置界面的Summary页截图存到工程文档里后面排查问题的时候对照这个Summary会省很多时间。4. 硬件上板之后从PL引脚到示波器的全链路实测4.1 引脚约束与外部电路设计上板第一步是给输出信号分配物理引脚。Zynq-7000的PL支持多种IO标准这个项目里我用3.3V的LVCMOS33标准选了一个普通HR bank的引脚在XDC文件里写上引脚约束和io standard约束。需要注意的一点是DDS输出的是16位数字信号如果你只是想要一路模拟正弦波不能直接把16根线全引出去而是要做一步转换。常见的做法有两种接一个高速DAC芯片比如AD9708这类并行接口DAC把16位数字波形换成模拟电流再经运放转电压或者更简单粗暴的只取MSB或者某一两个高有效位经RC低通滤波后得到一个近似的正弦波。我这次因为板上的DAC还没到货为了不阻塞开发进度先用了第二套办法验证功能取输出数据的高8位推IO然后在FPGA板卡上的排针处加了一级一阶RC低通滤波器截止频率设计在3MHz左右。实测确实能看到平滑的正弦波只是受限于高8位的量化精度波形有明显阶梯感作为功能验证足够了等到DAC就位再切换到全并行输出。4.2 ILA抓波和VIO调频的实操细节硬件调试利器主要有三个ILA、VIO和System ILA。ILA负责在FPGA内部实时抓波形VIO则允许你在运行时动态改写内部寄存器的值对于DDS应用来说VIO就是用来在线改频率控制字的。我在这次调试里把ILA挂在DDS IP核的输出端采样时钟直接用系统时钟100MHz采样深度选16384触发信号设为m_axis_data_tvalid的上升沿。这样设置之后只要配置一次频率控制字ILA就会抓下整个波形数据段在硬件管理器里就能看到近似正弦的数据波形图。这里有个实操心得ILA的采样深度宁多勿少因为如果要分析一个完整周期的低频波形深度不够可能只抓到几个周期的片段分析起来很费劲。VIO的动态调频用法是在IP核的配置接口上用VIO代替PS端的AXI总线来提供频率控制字。注意这里的接口位宽要和IP核配置一致再把VIO的一个输出通道接到s_axis_config_tdata上另一个输出通道接tvalid。在硬件管理器里手动给tvalid一个脉冲tdata设置成目标频率对应的相位增量就能实时改变输出波形频率。这一步验证通过后再把VIO移除换成PS端的AXI读写逻辑整个链路就完整了。4.3 时钟域、复位移位与时序约束硬件实测过程中时序问题最容易在复位和跨时钟域处理上翻车。DDS IP核的复位信号要求和其他逻辑保持一致项目里我用的是异步复位同步释放方案这样既能保证复位信号到来时立刻生效又能在复位释放时避开时钟边沿防止亚稳态。热词里提到的“always对reg打几拍管用”在实际项目里要分清场景。如果一个单比特信号跨时钟域比如一个脉冲从慢时钟域进快时钟域打两拍两级同步器通常够用。但如果你把DDS输出的16位数据和tvalid信号从一个时钟域搬去另一个时钟域只打拍是不行的因为多比特数据的各bit到达时间可能不同步采样时会发生数据撕裂。正确做法是把数据写成异步FIFO或者在源时钟域先把数据锁存好再用目的时钟域的tvalid来采样。DDS IP核本身工作在单时钟域一般不涉及这个问题但一旦和系统的其他模块对接这个问题就绕不开。时序约束方面这次工程的时钟频率不高100MHz理论上是很容易满足的。但如果后续要把DDS工作频率推到300MHz甚至更高就要留意DDS IP核内部的查找表时序是否收敛以及输出数据寄存器到IO引脚的路径延迟。建议在工程综合后直接看一眼时序报告如果有时序违规优先考虑在输出端再加一级流水寄存器或者用不同速度等级的器件。5. 资源消耗账本LUT、FF、DSP48和BRAM分别被谁吃了5.1 三组配置下的资源占用实测数据资源消耗分析是项目落地的关键一环因为它直接关系到你的FPGA里还剩多少余量给其他逻辑。我在同一颗xc7z020上实测了三组DDS配置下的资源占用情况结果如下表配置项配置A基础版配置B本次项目版配置C多通道版Phase Width163232Output Width81616通道数114相位增量可编程否是是LUT56187412FF41142286DSP48002BRAM(36Kb)012这里要说明具体数值在不同Vivado版本、不同综合策略下会有差异但这个量级是有参考意义的。xc7z020总共有53200个LUT、106400个FF、220个DSP48和140个BRAM块所以即使是配置B也只占用了不到0.5%的资源非常轻量。当然这个轻量是建立在单通道、无大幅幅度调制的前提下的如果你的应用需要极高输出位宽或者大量通道资源消耗会明显上涨。5.2 资源消耗的归因分析与优化方向从数据上看占用大头的是LUT和FF而不是BRAM。原因是DDS IP核在输出位宽16位、相位累加器32位的配置下查找表的主要部分可以通过分布式RAM或者说LUT-based逻辑来实现只有当输出位宽进一步增大或者通道数增多时才会真正用到BRAM块。相位累加器本身在配置A和配置B之间增量明显从56个LUT涨到187个这是因为32位加法器加流水寄存器天然就需要这么多资源跟自己手写的实现差距不大IP核并没有在这方面浪费逻辑。如果资源紧张有三个优化方向可以考虑。第一如果能接受稍差一些的杂散表现就把Phase Width从32降到24LUT和FF数量都会下降第二在多通道场景下DDS IP核内部自动做时间分片本质上是用更高的内部时钟频率换取资源复用所以多通道版不像你想象的资源翻倍第三如果输出端不需要实时大幅度调制关掉幅度可编程功能能省掉DSP48的占用我实测配置B之所以DSP48是0就是因为没有启用幅度调制而配置C启用了多通道幅度调制DSP48数量就上来了。热词里的“xilinx logic cell 和slice”也适合放到这个环节说。7系列FPGA的基础逻辑单元实际上是以slice为单位的每个slice包含4个LUT和8个FF所以看资源报告时除了看LUT/FF数量还要看一下Slice数量因为它反映了逻辑的物理分布密度。DDS这种流水线很长的逻辑属于典型的LUT和FF密集但互联复杂度不高的设计通常在布局布线阶段时序收敛比较轻松。6. 回看这次跑通流程真正值得记住的排错经验6.1 问题排查清单整个从仿真到硬件实测的过程中我记录了一些典型的排错场景整理成下面的清单方便你对照排查问题现象根因分析解决办法tvalid信号始终为低配置时序不对s_axis_config_tvalid没有在tready拉高期间保持有效照着手册Waveform图重写配置操作tvalid在tready拉高前提前拉高并保持至少一个周期输出波形频率是期望值两倍相位增量计算错误把f_clk用成50MHz或者2^N用错重新核对公式f_out f_clk x Δθ / 2^N建议写个小计算脚本避免手算错误输出数据全为0输出位宽下配置了无符号输出但数据手册要求有符号格式时没转换或复位一直有效检查复位信号是否解除确认输出数据格式是二进制补码还是有符号数实测波形毛刺明显IO驱动强度设置过大信号反射严重在XDC里减小输出引脚Drive Strength或串接33Ω~50Ω电阻做阻抗匹配扫频时频率跳变不平滑每次改写相位增量后Instantaneous输出还是旧值导致短时间频偏改用IP核的Streaming模式或检查是否用了PINC vs POFF配置位的差异多通道输出时通道间串扰通道数配多但后级模块没按通道做区分所有通道都读了同一组输出检查AXI4-Stream输出端是否有通道标识信号tkeep或tuser按通道区分处理这里面最坑的是最后一条“频率跳变不平滑”它不像前几个问题那样直观。DDS IP核的配置接口在写入新的频率控制字后不是立刻生效的而是存在流水线延迟具体延迟Cycle数在数据手册里有表可查。如果你在扫频应用里不做任何处理两个频率之间的切换瞬间会有一段频率“拖尾”这在某些对频率纯度要求高的应用里是不能接受的。解决方案是在PS端软件里计算好提前量或者接受这个固有延迟并在系统级做校准。6.2 几个提高调试效率的小技巧调试效率在FPGA项目里往往比省几个LUT重要得多。第一个技巧是尽量用System ILA替代传统ILA核因为System ILA可以在Vivado的IP Integrator里直接插入到AXI总线上调试时不用手动例化一堆探测端口管理起来也方便得多。缺点是System ILA对AXI接口格式有要求纯Native接口的DDS输出用起来稍微麻烦一点。第二个技巧是善用Vivado的硬件管理器里导出的CSV功能。ILA抓到波形数据后可以把采样数据导出成CSV然后用Python或者Matlab直接做FFT分析比在波形视图里肉眼判断频率准确得多。我在这次实测中就是靠这个方式实时确认输出频谱是否干净、杂散是否满足预期整个过程比反复改testbench跑仿真快一大截。第三个技巧和热词里的“xilinx 7045 xadc功能”有关。Zynq-7000内部自带XADC硬核可以实时监测FPGA的核心电压和芯片温度。上板调试时我习惯把XADC的读数挂出来如果长时间跑高频信号发现输出波形畸变先看一眼温度和电源电压如果温度过高或者电压跌落就得先解决板级问题再回头调逻辑避免在错误的层次上浪费时间。第四个技巧是关于拆分的。DDS IP核虽然本身很简单但它几乎必然要和后级模块联动。第一次调试时不要直接跑一个完整的通讯链路而是只让DDS输出正弦波用ILA确认这个环节完全没问题后再接上后续的数字滤波器或者DAC驱动模块。每增加一个模块就重新抓一次波形确认信号链没被破坏。这种“分而治之”的调试节奏看起来慢实际效率反而最高。最后说一下我对这个流程的整体感受。从手写DDS的磕磕绊绊到换成IP核之后的相对顺畅最大的收获不是省了多少代码量而是明白了一个道理像DDS这种功能高度标准化、参数化程度很高的模块直接用官方IP是更理性的工程选择。自己手写虽然对原理理解有帮助但在项目交付压力下时间和稳定性成本都不可控。当然用IP核不代表可以放弃对原理的理解。恰恰相反只有把相位累加器、相位截断、SFDR这些概念吃透了才能正确理解每一个配置参数的含义也才能在波形出问题时快速定位到根因。如果现在让我重新做一次这个项目我会在动手前先把DDS Compiler IP核的Product Guide翻一遍尤其是那张配置时序图和资源利用表比在仿真和实测里反复试错省时间得多。
企业数字化 ERP 产品动态
相关推荐
RK3588核心板16GB+128GB高配实战:边缘AI多模型部署与存储优化 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 10:16:51
毫米波雷达与智能床垫结合:智慧养老夜间守护全流程解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 10:16:30
鸿蒙开发:了解Context 前言之前在封装图片滑动验证,还有当下的一个自适应背景颜色功能时,都需要获取到image.PixelMap对象,于是就使用了getMediaContent方法,代码如下:const resourceMgr: resourceManager.ResourceManager this.getUIConte… · 2026/9/24 10:49:51
用 C++ 写 Web 服务实战(二):路由进阶、中间件链与文件上传 用 C 写 Web 服务实战(二):路由进阶、中间件链与文件上传
上一篇我们搭起了一个能跑的 Web 应用骨架——监听端口、注册 JSON 路由、绑定静态目录、开启多事件循环、写了一个文件日志中间件。这篇在这个骨架之上继续往上盖:路由参… · 2026/9/24 10:49:45
nvm 速查表:Node.js 多版本安装、切换与镜像配置实战指南 文档知识库教程开发工具 【免费下载链接】reference 为开发人员分享快速参考备忘清单(速查表) 项目地址: https://gitcode.com/jaywcjlove/reference 点击查看 免费下载 本文基于开源仓库 reference 的 nvm 备忘清单 整理而成,聚焦 Node Version Manage… · 2026/9/24 10:49:45
Work Agent深度解读:AI如何完成长程复杂任务 AI交互形态正在经历一轮底层转变,从单次问答的对话窗口,逐步进化为可以自主推进多步骤工作的智能执行主体。早期大模型产品的核心交互形态是单轮问答,用户提出问题,模型即时返回一段文本结果;随着工具调用能力成熟&… · 2026/9/24 10:49:39
ITR客户服务流程实战:SLA分级、角色分工与升级机制落地指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 10:49:33
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程 简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13
1D-CNN时间序列建模实战:从Conv1d原理到工业落地 简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26
柔软的L:汉语语流中被忽视的舌肌张力控制 1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44