FEATURED · 精选文章

Verilog状态机设计从入门到实战:三段式写法与FPGA应用

发布时间 / 2026/9/8 16:52:59
来源 / 创域科博编辑部
栏目 / 资讯中心
Verilog状态机设计从入门到实战:三段式写法与FPGA应用 1. 状态机到底难在哪先搞清楚它解决什么问题做FPGA和数字IC设计的同学早晚要跟状态机正面相遇。Verilog基础里别的东西比如计数器、选择器、加法器都是有固定写法的看两眼手册就能上手。状态机不一样同样的功能十个人能写出十种风格而且风格好坏直接决定后期调试的难易程度。先说个我自己的经历。刚学Verilog那会儿写一个UART接收模块波特率时钟、起始位检测、数据位采样、停止位判断这些逻辑全堆在一个always块里用一堆if-else嵌套。功能能跑通但到了要加奇偶校验的时候代码已经乱到我自己都不想看。后来把整个接收逻辑重构成状态机才真正体会到这玩意儿的好处。状态机解决的核心问题是把时序逻辑从流水中抽离出来变成显式的状态跳转。组合逻辑天然是并发执行的而很多协议、接口、算法本身是分步骤的、有先后顺序的。比如I2C读写EEPROM要先生成起始条件然后发设备地址等ACK再发寄存器地址再等ACK最后才是数据读写。这种步骤关系用if-else写不是不能写但可读性和可维护性会很差。状态机把所有可能的步骤抽象成节点把触发跳转的条件抽象成连线整个控制流就变成了一张清晰的图。所以我的结论很直接写状态机不是Verilog的选修课是必修课。不仅是FPGA开发芯片设计、嵌入式软件里的复杂控制逻辑本质都是在跟状态机打交道。学会把“步骤”变成“状态”是数字逻辑设计从入门到进阶的转折点。本文适合正在学Verilog的初学者也适合已经写过几个模块但总感觉状态机写得不够利索的工程师。我会从最基础的概念讲起一直聊到三段式状态机的写法、仿真验证方法以及实际调试中踩过的坑。2. 状态机的三个核心要素状态、转移条件、输出2.1 状态机的本质是一张有向图状态机的数学定义不用背记住一句话它是一张有向图。图的节点叫状态State图的边叫状态转移Transition每条边上挂了转移条件Condition每个节点可以附带输出Output。举个最贴近生活的例子一台自动售货机。投入的钱累计到一定金额出货找零。这个过程的状态可以是空闲、收款中、出货中、找零中。决定状态跳转的条件是投币、金额达标、货道电机转完、找零完成。这样想状态机是不是没那么神秘了在Verilog里状态用parameter或localparam定义常量用寄存器变量保存当前状态用组合逻辑判断转移条件用时序逻辑完成状态更新。就这么简单。2.2 摩尔型和米利型的区别状态机按输出生成方式分为两大类摩尔型Moore输出只取决于当前状态。特点是输出稳定、无毛刺风险低但需要更多的状态来表达相同的逻辑。米利型Mealy输出取决于当前状态和输入。特点是状态数少、响应快但输出可能受输入毛刺影响组合逻辑输出的稳定性和时序差一些。实际工程中FPGA里用的比较多的是Moore型因为FPGA的寄存器资源丰富状态多点无所谓但稳定性很重要。通信协议里的状态机多数也是Moore型。米利型在需要极低延迟响应的场景有优势比如仲裁器。我知道很多教材喜欢让学生记这两个名词但从实用角度看初学阶段你把状态机写成Moore型然后理解两者差异就够用了。真到了必须用米利型节省状态数的时候你已经能自己判断了。2.3 状态编码方式独热码和二进制码状态编码是新手最容易忽视的细节。常见的编码方式有三种直接决定资源消耗和工作频率二进制编码Binary状态值按0、1、2、3…排列使用寄存器数最少N个状态需要log2(N)个寄存器。但状态跳转时多个bit同时翻转组合逻辑复杂程度高容易产生竞争和毛刺在高速设计中可能成为时序瓶颈。格雷码Gray相邻状态间只有1个bit翻转适合状态连续跳转的场景比如计数器结构的状态机。功耗低、毛刺少。独热码One-HotN个状态用N个bit表示每个状态只有对应bit为1其余为0。状态转移只需判断1个bit位逻辑简单、速度快、无竞争。缺点是用寄存器多N个状态需要N个触发器。FPGA有大量触发器资源所以独热码在FPGA设计里非常常用。ASIC设计则通常优先二进制编码因为面积敏感。一个实际工程中的选择逻辑FPGA 状态数不多比如小于16个直接用独热码省去很多麻烦。ASIC或对面积有严格约束选二进制编码综合时让工具优化。状态连续递增或循环比如计数器格雷码是好选择。代码里用localparam定义状态常量有的工程师喜欢给独热码直接写十六进制值比如localparam IDLE 4b0001; localparam RUN 4b0010; localparam DONE 4b0100;这样写没问题但我个人更推荐用移位表达式localparam IDLE 4b0001; localparam RUN 4b0010; localparam DONE 4b0100;两种写法最终综合出的电路是一样的。关键在于更新代码时不要手动去算第几个bit位减少出错概率。3. 二段式与三段式两种主流的Verilog写法对比3.1 一段式新手最容易写出但最难维护的写法先说不推荐的写法。有些电路设计是一个always块里把所有事情全干完状态跳转、输出更新、信号复位全塞进去。这种叫一段式状态机。一段式最大的问题是输出和状态转移搅在一起产生组合逻辑环路或时序延迟不一致时很难排查。比如某个输出信号在状态A下要拉高在状态B下要拉低你可能在case分支里分别写assign语句一旦逻辑复杂编译器给出的警告你根本定位不到是哪段代码的问题。我的建议很明确一段式不要在正式项目里用。它只适合写极小的练习模块或者为了应付考试。3.2 二段式状态跳转和时序输出分离二段式的结构是两个always块第一个always块时序逻辑负责状态寄存器更新即状态跳转。第二个always块组合逻辑根据当前状态和输入产生输出信号。二段式的问题在于第二个always块里的组合逻辑输出是电平信号如果需要寄存器输出即输出信号要打一拍必须在外部再补一个时序逻辑。这让代码结构变得有点拧巴。看一下典型写法// 状态编码定义 parameter IDLE 3b000; parameter S1 3b001; parameter S2 3b010; reg [2:0] state, next_state; // 第一个always状态寄存器更新 always (posedge clk or negedge rst_n) begin if (!rst_n) state IDLE; else state next_state; end // 第二个always组合逻辑产生next_state和输出 always (*) begin next_state state; out 0; case (state) IDLE: begin if (en) next_state S1; out 1b0; end S1: begin next_state (cnt_done) ? S2 : S1; out 1b1; end S2: begin next_state IDLE; end endcase end这种写法能工作但out是组合逻辑输出容易因为输入抖动产生毛刺。而且写多了你会发现每个状态分支里都得小心处理out很容易漏赋值导致产生锁存器Latch。3.3 三段式把状态、次态逻辑、输出彻底分开三段式是工程中推荐的标准写法结构如下第一个always块时序逻辑状态寄存器更新同步复位。第二个always块组合逻辑根据当前状态和输入计算下一个状态next_state。第三个always块时序逻辑根据当前状态产生输出信号。核心逻辑状态跳转和输出产生完全分离输出是时序输出不会出现组合毛刺。三段式的第二个always块是纯组合逻辑用来算next_state。建议在进入case之前加默认赋值next_state state;避免某些分支漏赋值导致出现Latch。第三个always块通常用case语句在每个状态下定义输出外部信号用组合逻辑生成mealy型输出时需要额外注意信号延迟问题。三段式的典型结构如下// 三段式状态机模板 module fsmm( input wire clk, input wire rst_n, input wire en, output reg done ); // 状态编码独热码 localparam IDLE 4b0001; localparam WORK 4b0010; localparam DONE 4b0100; reg [3:0] state; reg [3:0] next_state; // 第一段状态寄存器 always (posedge clk or negedge rst_n) begin if (!rst_n) state IDLE; else state next_state; end // 第二段次态组合逻辑 always (*) begin next_state state; case (state) IDLE: if (en) next_state WORK; WORK: if (cnt 4d10) next_state DONE; DONE: next_state IDLE; default: next_state IDLE; endcase end // 第三段输出时序逻辑 always (posedge clk or negedge rst_n) begin if (!rst_n) done 1b0; else begin case (state) DONE: done 1b1; default: done 1b0; endcase end end endmodule三段的优势体现在几个方面输出是寄存器输出在时钟沿到来时才更新不会因为输入的微小毛刺导致输出抖动。定位问题清晰。状态跳转有问题就查第二个块输出不对就查第三个块不用在几百行代码里来回找。方便添加时序约束。综合工具能更准确地分析时序路径。我对三段式的评价是它是初学阶段性价比最高的状态机写法。它稍微多花几行代码但换来的调试效率提升是几何级的。3.4 三种写法对比表写法状态跳转输出产生输出类型工程推荐度一段式与输出混写与状态混写组合不推荐二段式独立时序块独立组合块组合可用但注意毛刺三段式独立时序块独立时序块寄存器强烈推荐4. 实战演练用三段式状态机写一个按键消抖模块光讲概念没意思写个实际模块练手。按键消抖是FPGA开发里出镜率极高的功能机械按键按下和释放时会有ms级别的抖动直接采样会采到多次电平跳变必须做消抖处理。常见的消抖方案有延时采样法、计数器计数法这里我用状态机方式实现好处是逻辑清晰用状态机来描述“等待稳定-确认按下-等待释放”这个过程。4.1 设计思路按键抖动一般持续5-10ms消抖的核心思路是检测到按键电平变化后进入“确认”状态在这个状态下持续采样一段时间如果电平稳定不变才认定为有效按下。状态定义IDLE空闲等待按键按下。采样到低电平按键按下通常接地进入PRESS_OUT。PRESS_OUT按下确认。如果连续N个周期采样到低电平说明按键确实被按下了输出一个脉冲进入RELEASE_OUT。RELEASE_OUT释放确认。如果采样到高电平说明按键释放回到IDLE。这样除了消抖还顺带实现了“边沿检测”的效果。如果想扩展成短按和长按的检测在PRESS_OUT状态再加一个计数器就行。4.2 完整代码module key_debounce_fsm( input wire clk, // 系统时钟这里假设1MHz input wire rst_n, input wire key_in, // 按键输入按下为0 output reg key_pulse // 按键有效按下脉冲 ); // 状态编码 localparam IDLE 3b001; localparam PRESS_OUT 3b010; localparam RELEASE_OUT 3b100; // 消抖计数阈值1MHz时钟下延时10ms localparam DEBOUNCE_CNT 16d10000; reg [2:0] state; reg [2:0] next_state; reg [15:0] cnt; // 第一段状态寄存器 always (posedge clk or negedge rst_n) begin if (!rst_n) state IDLE; else state next_state; end // 第二段次态组合逻辑 always (*) begin next_state state; case (state) IDLE: begin if (key_in 1b0) // 检测到按下 next_state PRESS_OUT; end PRESS_OUT: begin if ((key_in 1b1) || (cnt DEBOUNCE_CNT)) begin // 如果在此过程中按键释放了或者计数器超过最大值回到IDLE // 这里简化处理只要按键提前松开就放弃本次检测 if (cnt DEBOUNCE_CNT) next_state RELEASE_OUT; // 确认有效按下进入释放检测 else next_state IDLE; end else next_state PRESS_OUT; // 继续采样 end RELEASE_OUT: begin if (key_in 1b1) next_state IDLE; // 确认释放 else next_state RELEASE_OUT; // 还没释放 end default: next_state IDLE; endcase end // 计数器用于产生延时 always (posedge clk or negedge rst_n) begin if (!rst_n) cnt 16d0; else if (state PRESS_OUT) cnt cnt 1b1; else cnt 16d0; end // 第三段输出时序逻辑 always (posedge clk or negedge rst_n) begin if (!rst_n) key_pulse 1b0; else begin case (state) RELEASE_OUT: key_pulse 1b1; // 在释放状态产生一个脉冲 default: key_pulse 1b0; endcase end end endmodule4.3 代码里的几个细节为什么在PRESS_OUT状态要设置cnt DEBOUNCE_CNT作为跳转条件如果按键一直按着不放cnt会一直递增直到超过阈值说明按键状态已经稳定确认是真实按下。为什么key_pulse在RELEASE_OUT状态下输出这是一种“释放时检测”的做法避免在按下瞬间因为电平还在抖动导致输出不稳定。实际产品里有人喜欢在PRESS_OUT结束时输出脉冲取决于后面的逻辑怎么用这个脉冲。为什么要默认赋值next_state state这是关键。如果不写这一行case分支里漏掉某种情况next_state会保持上一个值虽然功能上也可能正确但综合时会推断出锁存器。锁存器在FPGA里是很不讨喜的东西对时序不利且造成资源浪费。养成本能加上的习惯能规避大量隐藏问题。4.4 仿真验证要点仿真文件要覆盖几个关键场景按下时产生抖动模拟按键在按下初期的多次抖动。持续按下超过10ms验证能输出脉冲。按下过程中提前松开验证不会误触发。testbench里按键输入可以用一个小task来模拟抖动先跳变几次再稳定按住。写成这样task press_key; begin // 模拟抖动 key_in 1b1; #100 key_in 1b0; #50 key_in 1b1; #50 key_in 1b0; #80 key_in 1b1; #50 key_in 1b0; // 稳定按下 #20000; // 持续按住 key_in 1b1; // 释放 #20000; end endtask跑完仿真看波形如果每个状态转移的时机都符合预期这个模块就可以接进系统了。Modelsim和Vivado自带的仿真器都能跑我用Vivado XSim多一些加载testbench后看波形配合光标测量时间尤其要确认脉冲宽度是不是一个时钟周期的宽度避免输出高电平时间太长被下游误判成持续事件。5. 状态机里的常见隐藏坑从仿真到综合逐个排雷5.1 组合逻辑优先级与case分支覆盖初学阶段用case语句时最容易犯的错误是分支覆盖不全。比如下面这样case (state) IDLE: next_state WORK; WORK: next_state DONE; // 漏了DONE分支 endcaseDONE状态下next_state没有被赋值综合工具会推断出锁存器。更麻烦的是这种问题在仿真里不一定暴露因为DONE状态可能只在特定条件下出现。等到FPGA上板行为就可能变得稀奇古怪一会儿工作正常一会儿卡死。解决方法是在case前先做默认赋值或者在case里补default分支。我的习惯是两个都写双保险。默认赋值可以把绝大部分状态跳转不变的情况直接覆盖default再兜底未知状态让状态机在非法状态下也能回到IDLE不会进入死循环。5.2 复位的方式异步复位、同步复位以及不复位复位策略是状态机设计里绕不开的问题。异步复位negedge rst_n在资源利用率上有优势复位信号不需要经过时钟树复位动作立即生效。同步复位则和时钟同步不会出现复位信号释放时的亚稳态问题。FPGA设计常规做法是异步复位、同步释放Asynchronous Assert, Synchronous Deassert。上电后复位信号先让系统进入已知状态退出复位时因为经过同步释放电路不会和时钟沿产生竞争。综合工具和厂商IP对这两种复位方式都提供了良好支持。关键是一个项目里复位策略要统一不能在某个模块写异步复位另一个模块写同步复位。我之前在同事的代码里见过同一个模块内混用两种复位风格综合时出warning不说时序分析也相当麻烦。5.3 亚稳态问题的本质和应对亚稳态是同步时序设计里真正要命的物理问题。简单说当触发器的数据输入在时钟沿附近变化导致setup/hold时间不满足时触发器的输出会处于一种既不是0也不是1的不稳定状态并且这个状态会向后续逻辑传播。状态机本身因为寄存器是沿触发的天然有抗亚稳态能力但输入信号的采集如果不做同步处理仍然可能采到不确定值。标准做法是对异步输入打两拍甚至三拍同步reg in_sync1, in_sync2; always (posedge clk or negedge rst_n) begin if (!rst_n) begin in_sync1 1b0; in_sync2 1b0; end else begin in_sync1 key_in; in_sync2 in_sync1; end end打两拍之后的in_sync2基本可以认为进入状态机的输入一定是稳定电平。代价是输入信号延迟两个时钟周期对大多数控制类应用来说完全不是问题。如果输入是跨时钟域来的两拍还未必够建议配合异步FIFO或专门的跨时钟域处理模块这里先不展开。5.4 状态机死锁default分支和非法状态恢复状态机跑飞了这在FPGA调试中是每个工程师都可能遇到的噩梦。原因通常是干扰导致状态寄存器进入了未定义的状态而case分支没有覆盖这个非法值状态机卡死了。三段式里第二个always块的default分支就是我们的恢复机制。把所有未编码的值归一到IDLE或某个安全状态。再加一个看门狗超时机制在状态机里加一个计数器任何状态下超过一定时间没有预期的事件发生就强制回到IDLE。这在通信协议类模块里尤其重要因为总线上可能出现不可预期的毛刺。我在调试SPI从机模块时遇到过一次状态机卡死最后定位到原因是外界干扰让状态寄存器跳到了一个未定义的值而代码里的case没有default状态机永远留在那个非法状态。从那以后所有case语句必须有default就变成了我写代码的铁律。5.5 casez和casex的使用边界Verilog提供casez和casex允许在分支条件里用高阻态或不定值做匹配。这在指令译码器里很常用比如不关心某些位casez (ir) 8b1??0_??01: op ADD; 8b0???_??00: op SUB; endcase但这种写法很容易隐藏bug。casex在仿真里把X态也纳入匹配容易掩盖初始化问题。我的建议是除非你有明确的理由比如指令译码需要通配否则不要用casez和casex。普通case配合default已经能覆盖绝大多数场景。问题都暴露在明面上比藏在暗处好。6. 从按键消抖到复杂协议控制状态机的进阶用法6.1 计数器配合状态机SPI从机接收模块的思路按键消抖的状态机只有三个状态逻辑相对简单。现实中更常见的场景是“状态机计数器”配合使用计数器在某个状态下开始计计到指定值后触发状态跳转。这套组合拳可以用来实现SPI从机接收。SPI从机的接收本质是一个8位移位过程可以这样设计状态IDLE - GET_ADDR - GET_DATA - DONE。在GET_ADDR状态下每个SCLK上升沿采样一位数据用一个3bit计数器记录已采到的bit数数到8后跳到下一个状态。这种写法和纯用计数器偏移地址移位寄存器的方式相比好处是逻辑意图一目了然协议扩展起来也方便。6.2 并行状态机主状态机加子状态机的架构当协议变复杂时把所有状态放在一层会非常臃肿状态可能膨胀到几十个跳转关系乱成一团。一个常见的解决思路是层次化状态机主状态机控制大阶段比如“配置阶段-传输阶段-校验阶段”每个大阶段内部再来一个子状态机处理细节。这个模式在I2C读写EEPROM里特别好用。主状态机控制整包传输的流程START、发送设备地址、ACK、发送寄存器地址、ACK、读写数据序列、STOP。每个阶段内部的bit级时序由计数器或子状态机控制。子状态机做完一件事后让主状态机跳转到下一个阶段。代码上主状态机和子状态机是分模块的通过握手信号通信。子状态机完成后拉一个done信号主状态机检测到done后进入下一状态。这样每个状态机的状态数都控制在10个以内调试时查问题很容易。6.3 状态机的状态爆炸问题FSM抽象当需要处理多路并行数据比如多个传感器通道的轮询状态数会成倍增长。一个二维的状态空间如果分别需要4个和6个状态乘积是24个状态。全写出来case分支很长改起来也麻烦。实践中有些工程师会把这种二维状态拆成两个独立状态机并行运行或者用寄存器变量把子状态编码成子状态号主状态用分支判断子状态号。这两种方案各有取舍拆状态机会增加信号交互的复杂度用子状态号需要小心编码冲突。我的建议是如果状态空间确实很大先画状态转移图把同样行为、只在输入条件上略有区别的状态合并。很多所谓的状态爆炸实际是从设计一开始就没有做好抽象比如把“数据位计数”这种周期行为和真正的事务流程混在一起。把周期性行为抽成计数器让状态机只关心事务级的阶段是控制整个复杂度的关键。6.4 状态机在异步FIFO、Cache替换、仲裁器中的应用状态机的应用范围远不止按键消抖和SPI。几个高频场景列出来供参考异步FIFO格雷码指针既是计数器也是状态机读指针和写指针各自维护一套状态转换规则跨时钟域同步时用两级触发器打拍防亚稳态。Cache替换策略LRU替换算法如果用状态机做可以维护一个访问历史状态列表每次访问命中时更新状态优先级替换时找到最久未使用的块。虽然Cache控制器里更多用硬件栈结构但状态机思想可以帮你理清楚替换逻辑的分步行为。总线仲裁器轮询仲裁器、优先级仲裁器都能建模成状态机仲裁状态在多个请求源之间跳转根据是轮询还是优先级固定决定跳转规则。FIR滤波器控制串行FIR滤波器可以用状态机控制数据加载、乘法累加、结果输出三个阶段。这种系数可配置的滤波器在FPGA里资源紧张时很有用因为时分复用一个乘法器就能完成多级计算。本质上只要一个功能是“分步完成”的就可以尝试用状态机建模。这几乎是数字系统设计里最通用的思维工具。7. 状态机调试经验分享从波形到定位问题的快速方法最后分享一些实际调试中的方法和心态。你写好了状态机综合下载到FPGA上跑发现功能不对从哪里下手7.1 第一步永远是查状态转移而不是查输出先定位状态输入是否正确跳转到了预期的状态。用逻辑分析仪或者Vivado的ILAIntegrated Logic Analyzer抓关键的几位状态寄存器的值和仿真波形对比看跳转条件有没有满足。如果状态之间根本没有跳转说明输入信号或者状态机内部的组合逻辑有问题输出不对只是表象。调试时把状态寄存器在测试代码里单独引出来连线观察或者用ILA采样m_axi等内部信号取决于你用的平台。我用过的FPGA开发板都支持内嵌逻辑分析仪不用额外接线路抓几十个内部信号看看实时跳转就行了。7.2 状态编码本身可能成为排查线索如果是独热码状态跳变时只有一个bit在翻转。如果你在波形上看到某个状态下有多个bit为1那大概率是进入了非法状态。此时要立即检查case里的default分支和完整覆盖。这个特征用看得多了一眼就能识别。7.3 输出信号时序不对如果状态跳转是对的但输出脉冲出现的时间不对多半要检查第三个always块的case判断条件里用的是state还是next_state。用state做输出判断输出会比状态跳转晚一个周期出现这是预期行为。如果用next_state做输出条件组合逻辑的路径会变得很长时序收敛容易出问题。7.4 仿真通过但上板失败这是最揪心的情况。仿真波形怎么看怎么对板子上一跑就是不对概率比较高的原因包括输入信号没有做同步处理实际板子的按键、传感器信号存在抖动和毛刺仿真里信号是理想的不会暴露这个问题。复位信号释放时机和时钟沿对齐有问题导致状态机从随机状态启动。时钟频率高于设计预期组合逻辑路径延迟太大建立时间不满足状态机跳转出现错误。处理顺序是先加输入同步再检查复位最后降频试试看是否时序问题。这基本能覆盖90%的“仿真过了上板就挂”的情况。7.5 定期画状态转移图写状态机之前哪怕是在草稿纸上也把状态转移图先画出来。标注每个状态的来源、去向和跳转条件。这件事看起来繁琐但真的能省掉后期大量的调试时间。我见过太多人代码写了两百行状态关系混乱到画不出图出了问题只能靠猜。画完图之后对着图检查代码每个跳转条件是否在代码里有对应的分支每个分支的默认输出是否都赋值了这两个问题过一遍状态机的大部分问题都不会在调试阶段出现。8. 当心状态机写多了之后容易走火入魔学习状态机的过程中我观察到一个有趣的现象一旦你熟练掌握了状态机就容易什么都往状态机上套。LED流水灯用状态机按键检测用状态机计数器也非要套个状态机壳子。这种写法不是不行但完全没必要。Led流水灯本质是计数器加译码按键消抖本身就可以用计数器实现。状态机的优势在表达复杂的分步逻辑杀鸡用牛刀只会让简单问题复杂化还有可能引入更多时序风险。好的工程判断是一个功能用计数器能表述清楚就不要硬加状态机用状态机能表述清楚就不要用一堆if-else硬凑。状态机的核心价值是把并发的时序逻辑变成显式的步骤序列它不是工具本身而是帮助思维理清结构的方法论。如果你想把状态机练扎实建议找个协议实现来动手UART、SPI、I2C任意选一个甚至用状态机模拟一个简单的交通灯控制逻辑。从状态定义、编码选择、三段式实现、testbench仿真到上板验证走完一整个流程之后你会发现状态机已经变成自然而然的东西。后面再去碰Cache、DDR控制器这类复杂设计状态机的思维会带你少走非常多弯路。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻