AM62L EMIF时序寄存器配置实战:从DDR原理到嵌入式内存优化
1. 项目概述与核心挑战
在嵌入式系统开发,尤其是基于德州仪器(TI)AM62L Sitara™这类高性能异构处理器的项目中,内存子系统的稳定性和性能往往是决定项目成败的关键。处理器再强大,如果数据“喂”不饱,或者频繁出错,整个系统的表现就会大打折扣。而连接处理器与外部DDR内存的桥梁,正是外部存储器接口(EMIF)控制器。最近在为一个工业网关项目调试AM62L平台时,我花了大量时间与EMIF控制器的时序寄存器打交道,特别是EMIF_CTLCFG_DENALI_CTL_48到CTL_78这一大段寄存器组。这些寄存器密密麻麻的位域,对应着TRC、TRCD、TRP、TFAW等几十个关键时序参数,并且每个参数还针对不同的频率点(FC=0, 1, 2)有独立的配置项。手册上的描述虽然准确,但过于碎片化,缺乏一个从“为什么”到“怎么做”的系统性视角。
这篇文章,我就结合这次实际调优经历,把这些时序参数寄存器掰开揉碎了讲清楚。目标很明确:不只是告诉你某个寄存器在哪个地址,某个位域叫什么名字,而是要深入解析每个时序参数背后的物理意义、它对系统的影响、以及在实际配置中如何根据你的DDR颗粒手册来计算和填写这些值。无论是正在从事AM62L底层驱动开发的工程师,还是对DDR内存子系统感兴趣、希望理解其工作原理的朋友,这篇文章都将提供一份从理论到实践的详细路线图。我们会从DDR的基本操作时序讲起,逐步深入到AM62L EMIF控制器的寄存器映射与配置策略,最后分享一些调试过程中积累的实战经验和避坑指南。
2. DDR内存基础与关键时序参数解析
在动手配置寄存器之前,我们必须先理解我们正在配置的是什么。DDR SDRAM(双倍数据率同步动态随机存取存储器)的访问并非随心所欲,它遵循一套严格的时间序列协议。你可以把它想象成一个拥有复杂内部结构的仓库(Bank、Row、Column),存取货物(数据)需要一系列固定的步骤和等待时间。EMIF控制器的作用,就是作为处理器的“仓库管理员”,严格按照DDR颗粒规定的“工作手册”(时序参数)来发号施令。
2.1 核心时序参数家族
AM62L的EMIF控制器寄存器主要配置以下几大类时序参数,它们共同决定了内存访问的效率和正确性:
激活与预充电相关时序:这是访问一行数据的前提。
- TRCD (RAS to CAS Delay):从发送行激活命令(ACTIVATE)到发送读/写命令(READ/WRITE)之间必须等待的最小时钟周期数。可以理解为打开仓库某一排货架(激活行)后,需要稳定一下,才能开始在这一排里找具体货物(列访问)。
- TRP (Row Precharge Time):关闭当前打开的行(预充电)所需的最短时间。在访问另一行之前,必须先把当前行“关好门”。
- TRAS (Active to Precharge Delay):一行被激活后,必须保持打开状态的最短时间。可以理解为打开一排货架后,至少需要这么多时间来完成一次有效的存取操作,才能关闭它。
- TRC (Row Cycle Time):完成一次完整的行操作周期(激活 + 预充电)所需的最短时间。通常,
TRC = TRAS + TRP。这是限制行访问频率的关键参数。
同一Bank内与跨Bank操作时序:
- TRRD (Row to Row Delay):在同一Bank Group内,连续激活两行之间需要的最小间隔。如果连续访问的两行在不同的Bank Group,这个限制会更宽松(对应
TRRD_L)。 - TFAW (Four Activate Window):一个时间窗口内,最多只能发生四次行激活命令。这是一个平均功率和电流限制的约束。
- TRRD (Row to Row Delay):在同一Bank Group内,连续激活两行之间需要的最小间隔。如果连续访问的两行在不同的Bank Group,这个限制会更宽松(对应
读/写操作相关时序:
- TWR (Write Recovery Time):完成一次写操作后,必须等待多长时间才能对同一行发起预充电命令。这是为了确保数据被可靠地写入存储单元。
- TRTP (Read to Precharge Time):完成一次读操作后,必须等待多长时间才能对同一行发起预充电命令。
- TCCD (CAS to CAS Delay):连续两个读或写命令之间的最小间隔。在同一Bank Group内更严格(
TCCD_L)。
刷新与模式寄存器相关时序:
- TRFC (Refresh Cycle Time):执行一次刷新命令所需的时间。这是DDR颗粒中最大的延时参数之一,通常在几百个时钟周期。它直接影响了内存的可用带宽,尤其是在高密度颗粒上。
- TREF (Refresh Interval):发送两次自动刷新命令之间的时间间隔。通常由DDR规范(如64ms内刷新所有行)和时钟频率计算得出。
- TMRD (Mode Register Set Command Period):在发送模式寄存器设置(MRS)命令后,需要等待多长时间才能发送其他命令。
其他控制与时序:
- TCKE (CKE Minimum Pulse Width):时钟使能信号的最小脉冲宽度。
- TDAL (Data-in to Active/Precharge Delay):写数据到相关命令(如预充电)的延迟。
- BSTLEN (Burst Length):突发传输长度配置(BL2, BL4, BL8, BL16, BL32)。
2.2 频率点(FC)的概念
AM62L的EMIF控制器一个非常重要的设计是支持多频率点(Frequency Point, 简称FC)配置。在寄存器中,你会看到TRC_F0、TRC_F1、TRC_F2这样的字段。这并非指三个不同的物理时钟域,而是为了支持动态频率缩放(DFS)或动态电压频率缩放(DVFS)功能。
- FC=0, 1, 2:通常对应处理器运行的不同性能状态(OPP)下的DDR控制器工作频率。例如,FC0可能对应最低功耗频率点(如400MHz),FC1对应平衡频率点(如800MHz),FC2对应最高性能频率点(如1600MHz)。
- 为什么需要分FC配置?因为DDR的时序参数是以纳秒(ns)为单位的绝对时间。当时钟频率变化时,对应的时钟周期数(cycles)必须重新计算。例如,一个
TRCD = 15ns的参数,在400MHz(周期2.5ns)下需要ceil(15ns / 2.5ns) = 6个周期;在800MHz(周期1.25ns)下则需要ceil(15ns / 1.25ns) = 12个周期。控制器在切换频率前,会预先加载对应FC的时序寄存器组,确保频率切换后时序依然满足要求。 - 配置原则:你需要根据每个目标频率(FC),分别计算所有时序参数对应的周期数,并填入相应的
_F0、_F1、_F2寄存器字段中。通常,频率越高,所需的周期数值越大。
3. AM62L EMIF时序寄存器深度解析与配置实战
理解了背景知识,我们现在可以打开TRM(技术参考手册),直面这些寄存器了。我将选取几个最具代表性、也最容易出错的寄存器进行详细拆解,并给出具体的配置计算示例。
3.1 寄存器概览与寻址
AM62L的EMIF控制器寄存器位于一个统一的物理地址空间。以DDR16SS0实例为例,其基地址为0xF3080000。我们关注的时序控制寄存器组,从EMIF_CTLCFG_DENALI_CTL_48(偏移0xC0)开始。因此,CTL_48的完整物理地址是0xF3080000 + 0xC0 = 0xF30800C0。
这些寄存器都是32位宽,大部分位域是可读可写(R/W),复位值通常为0。在配置前,务必确认你的DDR颗粒型号、工作电压、以及计划运行的频率点。
3.2 核心时序寄存器详解与计算
3.2.1 EMIF_CTLCFG_DENALI_CTL_48 (Offset C0h) - TRC与TRRD配置
这个寄存器包含了TRC_F1、TRRD_L_F1和TRRD_F1。我们以TRC_F1为例进行深度解析。
- 位域:
TRC_F1占据bit[24:16],共9位,最大可表示511个周期,足以满足高速DDR的需求。 - 物理意义:
TRC是行周期时间。假设你的DDR4颗粒手册规定tRC = 46.1ns,你计划在FC1频率点运行在1200MHz(时钟周期约0.833ns)。 - 计算过程:
- 获取颗粒参数:
tRC_min = 46.1 ns(从颗粒Datasheet的AC Timing Characteristics表中查找)。 - 计算时钟周期:
tCK = 1 / 1200MHz ≈ 0.83333 ns。 - 计算所需周期数:
nTRC = ceil(tRC_min / tCK) = ceil(46.1 / 0.83333) = ceil(55.32) = 56。 - 考虑控制器余量:为了确保稳定性,通常会在计算值上增加1-2个周期的余量(Margin)。我们加1个周期:
nTRC_final = 56 + 1 = 57。 - 转换为十六进制:57的十六进制是
0x39。 - 写入寄存器:需要将
0x39写入TRC_F1字段(bit[24:16])。由于该字段在寄存器中并非最低位,需要进行移位操作。通常的配置代码会使用位域操作或直接计算整个寄存器的值。
- 获取颗粒参数:
注意:
ceil()是向上取整函数。必须向上取整,因为周期数是整数,且必须满足最小时间要求。向下取整会导致时序违规,可能引发随机数据错误或系统不稳定,这种错误极难调试。
TRRD_F1和TRRD_L_F1的计算方式类似,分别对应同Bank Group和不同Bank Group的行激活间隔。tRRD_S和tRRD_L的值同样从颗粒手册获取。
3.2.2 EMIF_CTLCFG_DENALI_CTL_49 (Offset C4h) - TWTR与TRAS_MIN配置
这个寄存器配置TWTR_L_F1、TWTR_F1和TRAS_MIN_F1。
- TWTR (Write to Read Turnaround):这是写操作后切换到读操作所需的延迟。
TWTR_L适用于同一Bank Group,TWTR适用于不同Bank Group。这个参数对混合读写工作负载的性能影响很大。 - TRAS_MIN:一行激活后最小保持打开的时间。一个常见的误区是认为
TRAS可以独立设置。实际上,TRAS_MIN、TRP和TRC之间存在约束关系:TRC ≥ TRAS_MIN + TRP。在配置时,需要确保你计算出的TRC周期数(如前例的57)大于等于TRAS_MIN周期数加上TRP周期数。通常,颗粒手册会给出tRAS和tRP的绝对值,我们需要分别计算并验证关系。
配置示例:颗粒tRAS_min = 32ns,tWTR_S = 3ns,tWTR_L = 9ns,频率1200MHz。
nTRAS_MIN = ceil(32 / 0.83333) = ceil(38.4) = 39,加1个余量 ->40。nWTR_L = ceil(9 / 0.83333) = ceil(10.8) = 11。nWTR = ceil(3 / 0.83333) = ceil(3.6) = 4。 需要检查:nTRC(57) ≥ nTRAS_MIN(40) + nTRP。假设nTRP算出来是18,那么40+18=58,大于57,这就不满足了!此时,你必须调整:要么增大nTRC(可能受限于颗粒最大值或性能),要么在满足tRAS_min的前提下,尝试用floor()计算nTRAS_MIN(但风险高),或者重新评估nTRP的计算。更常见的做法是优先保证TRC满足TRAS+TRP,因为TRC是更基础的周期。
3.2.3 EMIF_CTLCFG_DENALI_CTL_50 (Offset C8h) - TFAW与TRP配置
这个寄存器包含TCCD_L_F2、TFAW_F1和TRP_F1。
- TFAW (Four Activate Window):这是一个时间窗口约束,例如
tFAW = 21ns。在1200MHz下,nTFAW = ceil(21 / 0.83333) = 26。这个参数限制了行激活命令的突发密度,对随机访问性能有影响。 - TRP:前面已经提到,行预充电时间。计算方式同前。
- TCCD_L:同一Bank Group内CAS命令之间的延迟。对于DDR4,BL8模式时通常为4个时钟周期,但需以手册为准。它影响读/写命令的背靠背发送能力。
3.2.4 刷新相关寄存器:TRFC与TREF
刷新参数是配置中的重中之重,配置不当极易导致数据丢失或系统长时间运行后崩溃。
- EMIF_CTLCFG_DENALI_CTL_72 (Offset 120h) - TRFC_F0:
TRFC是刷新周期时间,值非常大。例如,一个8Gb DDR4颗粒的tRFC1可能高达350ns。在400MHz(2.5ns周期)下,nTRFC = ceil(350 / 2.5) = 140。这个值会直接写入寄存器的TRFC_F0字段(bit[25:16])。关键点:TRFC值巨大,会长时间阻塞内存总线,因此在高带宽实时应用中,需要关注刷新带来的性能抖动。 - EMIF_CTLCFG_DENALI_CTL_73 (Offset 124h) - TREF_F0:
TREF是刷新间隔。DDR规范要求64ms内完成所有行的刷新。如果颗粒有8192行,那么平均刷新间隔tREFI = 64ms / 8192 ≈ 7.8us。在400MHz下,周期数nTREF = tREFI / tCK = 7800ns / 2.5ns = 3120。这个值写入TREF_F0(bit[19:0])。控制器会根据这个周期值自动发起刷新命令。
重要心得:对于
TRFC和TREF,务必使用颗粒手册中与你所用密度和速度等级相对应的确切值。不同密度(4Gb, 8Gb, 16Gb)的tRFC差异巨大。直接套用其他项目的值或估算,是导致内存不稳定最常见的原因之一。
3.3 配置流程与代码示例
在实际项目中,我们不会手动计算每一个十六进制值。TI通常会提供基于Excel的配置工具(如DDR Register Configuration Tool)或脚本,输入颗粒参数和频率,自动生成寄存器值。但理解其原理对于调试至关重要。
假设我们使用一个DDR4颗粒,在FC1(1200MHz)下的部分关键参数计算如下表所示:
| 时序参数 | 颗粒值 (tXX_min) | 计算公式 (1200MHz) | 计算周期数 | 最终配置值 (周期) | 对应寄存器字段 |
|---|---|---|---|---|---|
| TRCD | 13.5 ns | ceil(13.5 / 0.83333) | 17 | 18 (+1余量) | TRCD_F1 |
| TRP | 13.5 ns | ceil(13.5 / 0.83333) | 17 | 18 (+1余量) | TRP_F1 |
| TRAS | 32 ns | ceil(32 / 0.83333) | 39 | 40 (+1余量) | TRAS_MIN_F1 |
| TRC | 46.1 ns | ceil(46.1 / 0.83333) | 56 | 57 (+1余量) | TRC_F1 |
| TRRD_S | 4.9 ns | ceil(4.9 / 0.83333) | 6 | 6 | TRRD_F1 |
| TRRD_L | 7.9 ns | ceil(7.9 / 0.83333) | 10 | 10 | TRRD_L_F1 |
| TFAW | 21 ns | ceil(21 / 0.83333) | 26 | 27 (+1余量) | TFAW_F1 |
| TWR | 15 ns | ceil(15 / 0.83333) | 18 | 19 (+1余量) | TWR_F1 |
在C代码或初始化脚本中,配置通常如下进行(以配置CTL_48和CTL_49为例):
// 假设寄存器基地址已定义为 EMIF_CTL_BASE volatile uint32_t *emif_ctl_reg = (uint32_t *)EMIF_CTL_BASE; // 配置 EMIF_CTLCFG_DENALI_CTL_48 (Offset 0xC0) // TRC_F1 = 57 (0x39), TRRD_L_F1 = 10 (0x0A), TRRD_F1 = 6 (0x06) uint32_t ctl48_value = (57 << 16) | (10 << 8) | (6 << 0); emif_ctl_reg[0xC0 / 4] = ctl48_value; // 因为寄存器是32位,偏移地址需要除以4 // 配置 EMIF_CTLCFG_DENALI_CTL_49 (Offset 0xC4) // TWTR_L_F1 = 11 (0x0B), TWTR_F1 = 4 (0x04), TRAS_MIN_F1 = 40 (0x28) uint32_t ctl49_value = (11 << 24) | (4 << 16) | (40 << 0); // 注意TRAS_MIN_F1是9位,这里假设值40在0-511范围内 emif_ctl_reg[0xC4 / 4] = ctl49_value;关键检查点:在写入所有时序寄存器后,必须验证TRC >= TRAS + TRP。根据上表:TRC=57,TRAS=40,TRP=18,40+18=58,57 < 58, 不满足!这说明我们的余量设置需要调整。在实际操作中,我们可能需要将TRC调整为58,或者在不违反颗粒最小值的条件下,将TRAS或TRP的计算值微调(例如,TRAS用39,不加余量)。这体现了手动核算的重要性。
4. 高级功能与特殊寄存器解析
除了基本时序,AM62L的EMIF控制器还提供了一些高级功能配置位,位于后续的寄存器中。
4.1 自动预充电与并发操作 (CTL_65)
EMIF_CTLCFG_DENALI_CTL_65寄存器包含几个重要的功能控制位:
- AP (Auto Precharge):置1使能控制器的自动预充电模式。在此模式下,读/写���令可以携带自动预充电标志,省去后续显式的预充电命令,有利于降低延迟,但可能会在某些访问模式下影响效率。
- CONCURRENTAP:如果DRAM颗粒支持,置1允许控制器在一个Bank处于自动预充电状态时,向其他Bank发送命令。这可以���升Bank级并行度。
- TRAS_LOCKOUT:如果DRAM支持,置1允许控制器在
TRAS_MIN到期前发出自动预充电命令。这可以优化时序,提前开始预充电,但需要颗粒支持。
实操建议:对于追求低延迟的应用,可以尝试使能
AP。CONCURRENTAP和TRAS_LOCKOUT取决于颗粒特性,最稳妥的方式是查阅颗粒手册确认支持后再使能,否则保持默认值0。
4.2 突发长度与地址镜像 (CTL_66 & CTL_68)
- CTL_66::BSTLEN:设置发送给DRAM的突发长度。必须与你在模式寄存器(MR)中配置的突发长度一致。例如,对于DDR4 BL8,应编程为
3。此配置错误将导致数据错位,系统根本无法启动。 - CTL_68::ADDRESS_MIRRORING:用于控制地址镜像,常用于解决PCB布线拓扑引起的信号完整性问题。如果你的板卡设计使用了地址线镜像来优化布线,需要在此处使能对应的片选(CS)位。
4.3 刷新深度优化与错误注入 (CTL_72 & CTL_70)
- CTL_72::CS_COMPARISON_FOR_REFRESH_DEPTH:这个位域定义了刷新逻辑在决定发送刷新命令时,会检查命令队列的深度。非零值可以限制刷新逻辑只查看队列的一部分,从而在某些高负载场景下更积极地插入刷新命令,避免因队列满而延迟刷新导致超时。对于实时性要求极高的系统,可以调整此参数。
- CTL_70::CA_PARITY_ERROR_INJECT:命令/地址奇偶校验错误注入。用于测试系统的CA奇偶校验错误检测和恢复机制。在生产代码中绝对不要启用。它通过选择要破坏的CA总线位来模拟错误。
5. 配置实战:从颗粒手册到寄存器值
让我们梳理一个完整的、可操作的配置流程。
5.1 第一步:收集信息
- 确定DDR颗粒型号:例如,美光MT40A512M16LY-075E。
- 获取颗粒数据手册:找到关键的AC时序参数表(通常以
tXX表示,单位ns或ps)。 - 确定系统目标频率:例如,FC0=400MHz, FC1=800MHz, FC2=1600MHz。并获取对应的时钟周期
tCK。 - 确定工作电压和时序表:DDR4通常有1.2V的时序表,确认你使用的是正确的电压档位下的参数。
5.2 第二步:参数计算与表格制作
创建一个Excel表格,列出所有需要配置的时序参数。对于每个频率点(FC),执行以下计算:
参数周期数 = ceil(颗粒tXX_min / tCK) + 时序余量(Margin)时序余量:这是一个经验值,用于补偿时钟抖动、电源噪声、SI(信号完整性)等因素。通常加1-2个周期。对于TRFC这种大值,加1个周期足矣。对于关键路径如tRCD、tRP,在信号质量好的板子上可以尝试不加余量以追求极限性能,但风险较高。
5.3 第三步:寄存器映射与验证
将计算出的周期数,映射到具体的寄存器位域。利用脚本或手动生成寄存器初始化数组。必须进行交叉验证:
TRC >= TRAS + TRPTFAW > 4 * TRRD(通常满足,但需检查)- 所有周期数不超过寄存器位域的最大值(如9位最大511)。
5.4 第四步:初始化序列与配置时机
AM62L的DDR初始化通常由Bootloader(如U-Boot)完成。配置这些时序寄存器是初始化序列的一部分,必须在DDR PHY训练之前完成。典型的顺序是:
- 配置PLL,设置DDR控制器时钟。
- 配置DDR控制器基本模式(如器件类型、数据宽度、Bank数量等)。
- 配置时序参数寄存器(即本文所述内容)。
- 执行DDR PHY训练(ZQ校准、读写电平、眼图训练等)。
- 使能内存控制器,内存进入可用状态。
6. 常见问题排查与调试心得
即使按照手册计算,DDR问题依然常见。以下是一些实战中遇到的坑和排查思路。
6.1 系统无法启动或随机崩溃
- 症状:上电后卡在Bootloader,或Linux内核启动过程中随机panic。
- 排查:
- 首要怀疑时序参数:尤其是
TRC、TRAS、TRP的关系不满足。使用调试器(如JTAG)在初始化阶段dump出配置的寄存器值,反向计算成纳秒,与颗粒手册最小值对比。 - 检查频率与参数匹配:确认为当前运行频率配置的FC寄存器组。如果你只在FC0配置了参数,但系统以FC1频率运行,必然崩溃。
- 检查突发长度(BSTLEN):是否与DDR颗粒模式寄存器(MR)配置的BL一致?不一致会导致所有数据错位。
- 检查刷新参数:
TREF设置过小会导致刷新过于频繁,性能下降;TRFC设置过小会导致刷新未完成就发起新操作,造成数据错误。TRFC设置过大一般没问题,但浪费了时间。
- 首要怀疑时序参数:尤其是
6.2 内存测试失败(如Memtest86+出现错误)
- 症状:系统能启动,但运行内存测试工具时报告比特错误。
- 排查:
- 信号完整性:这是最常见原因。在排除软件配置前,先用示波器或矢量网络分析仪检查DDR线缆的信号质量(过冲、振铃、眼图)。阻抗不匹配、过长的走线、糟糕的参考平面都会导致问题。
- 时序余量不足:尝试在计算值上增加1-2个周期的余量,特别是
tRCD、tRP、tWTR等关键时序。如果错误消失,说明原配置处于临界状态。 - VTT终端电压:检查DDR的VTT参考电压是否准确、稳定。
- PHY训练结果:查看PHY训练的日志或寄存器结果,看读写电平、延迟是否收敛在合理范围内。训练失败或结果不佳,即使控制器时序正确,数据采样也会出错。
6.3 性能不达预期
- 症状:内存带宽测试结果低于理论值。
- 排查:
- 检查
TFAW和TRRD:过于保守的设置(周期数过大)会限制行激活速率,影响随机访问性能。在满足颗粒最小值的前提下,可以尝试优化。 - 检查Bank Interleaving和Page Policy:这些是控制器的高级调度策略,不在本文所述的时序寄存器中,但在EMIF的其他配置寄存器里。确保Bank交错访问和页管理策略(如开放页或关闭页)设置合理。
- 刷新影响:高密度内存的
TRFC很大,会周期性阻塞总线。使用memtester等工具测试长时间带宽,观察是否因刷新导致周期性掉速。可以尝试调整TREF_INTERVAL(CTL_78),但必须满足64ms刷新全部行的总要求。
- 检查
6.4 调试工具与技巧
- 寄存器查看:在U-Boot或内核中,通过
md(memory display)命令或编写内核模块,直接读取EMIF控制器寄存器,确认配置值是否按预期写入。 - 逻辑分析仪:配合DDR协议分析探头,可以捕获实际的命令、地址、数据总线波形,直观地测量命令之间的间隔(如ACT到RD的延迟),并与你配置的
TRCD周期数进行对比。这是最直接的验证手段。 - TI SysConfig工具:强烈推荐使用TI的SysConfig图形化工具来配置AM62L的DDR子系统。它内置了常见颗粒的数据库,能自动计算时序参数,生成初始化C代码和配置文件,极大减少手动计算错误。
配置AM62L的DDR时序寄存器是一项细致且需要反复验证的工作。它混合了理论计算(时序参数)、硬件知识(信号完整性)和软件实践(寄存器编程)。最深刻的体会是,没有“万能配置”。即使使用同一颗处理器,不同的PCB设计、不同的DDR颗粒批次、不同的工作温度,都可能需要对时序余量进行微调。最好的习惯是:严格依据颗粒手册计算,在板卡硬件设计允许的范围内预留合理余量,通过压力测试(内存测试、高低温循环)进行充分验证,并保留完整的配置计算文档,为后续的调试和产品迭代打下坚实基础。当你看到系统稳定地通过所有内存测试,并达到预期的性能指标时,之前与这些十六进制数字搏斗的所有时间都是值得的。
