时序逻辑(sequential logic)
想想健身房储物柜上的密码锁。把转盘拨到"12"并不会把锁打开——接下来会发生什么,取决于你之前拨过哪些数字。这把锁记得自己处在序列中的哪一步。普通的门铃则恰恰相反:一按就响,一松就停,根本不记得你一分钟前是否按过。时序逻辑就是这把密码锁的数字版。它的输出不仅取决于此刻的输入,还取决于一份对此前发生过什么的记忆——也就是电路的状态。与之对照的是组合逻辑,后者的输出纯粹是当前输入的函数,就像那只门铃。
让时序电路得以"记忆"的,是一份保存在触发器或寄存器里的状态——一个个微小的一位(或多位)存储单元。而真正让整套机制"嘀嗒"运转起来的关键在于:这份记忆只在时钟信号的边沿才会更新,时钟信号就是那拍共同的心跳,驱使一个时钟域齐步向前。每逢时钟的上升沿(或下降沿),每个受时钟驱动的触发器都会把它的输入拍下一个快照,并把这个值稳定地保持到下一个边沿。(带使能端的触发器在未被使能时,只会保留它原来的值。)所以时序电路并不是连续不停地漂移,而是踩着节拍,从一个状态到下一个状态,一次只前进一个离散的步子。
在实践中,你会把时序逻辑设计成一个回路:当前状态加上当前输入,一起送进一块组合逻辑,它计算出两样东西——输出,以及下一个状态,而下一个状态会在下一个边沿被重新锁存回触发器里。这个结构正是一个有限状态机,计数器、红绿灯控制器、通信协议和处理器控制单元都是这样搭出来的。它的另一面是:时钟从此为你定下速度上限——两个触发器之间所有的组合逻辑,都必须在一个时钟周期内稳定下来,这正是工程师对关键路径和时序如此较真的全部缘由。
// A simple counter: state (count) advances one step on each clock edge. // (count is a 4-bit register, e.g. reg [3:0] count;) always @(posedge clk) if (rst) count <= 4'b0000; // synchronous reset else count <= count + 1;
一个 4 位计数器——它的输出取决于前一次的计数值,并在每个时钟边沿更新,这正是时序逻辑的精髓。
这里的"时序"(sequential)指的是随时间推进、穿过一连串状态,而不是软件里指令一条接一条地执行。一个时序电路可以在同一个时钟边沿上同时更新数百万个触发器——它在硬件上是并行的,所谓"时序"只是说它一步一步地穿过各个状态。