1. 组合逻辑 (Combinational Logic)
1.1 专业描述
组合逻辑电路是指在任何时刻,其输出值仅取决于该时刻输入值的逻辑电路。它不包含任何记忆元件(如触发器或寄存器),因此电路本身没有“状态”的概念。可以将其视为一个纯粹的、无状态的计算函数,输入一旦确定,输出便唯一确定。
核心特征:
无记忆性: 电路不存储历史信息。
无时钟依赖: 理论上,组合逻辑的运算与时钟信号无关。其输出会随着输入的改变而立即(经过一定的传播延迟后)改变。
传播延迟 (Propagation Delay): 电流通过逻辑门需要时间,从输入变化到输出稳定下来的这段时间称为传播延迟。这是组合逻辑电路中一个非常关键的物理特性。
1.2 设计与实现
组合逻辑是实现数据通路(Datapath)的基础,用于执行各种算术和逻辑运算。
常见电路:
逻辑门 (Logic Gates): AND, OR, NOT, XOR 等是构成所有组合逻辑的基础。
加法器 (Adder): 从半加器、全加器到多位的超前进位加法器。
比较器 (Comparator)**: 比较两个二进制数的大小。
多路选择器 (Multiplexer, MUX): 根据选择信号从多个输入中选择一个输出。
解码器/编码器 (Decoder/Encoder): 将一种编码转换为另一种编码,如将二进制码转换为独热码(One-hot)。
算术逻辑单元 (ALU): 能够执行多种算术和逻辑运算的复杂组合逻辑单元。
HDL 实现 (以 Verilog 为例): 主要使用 `assign` 连续赋值语句和 `always @(*)` 块来实现。
`always @(*)` 是描述组合逻辑的推荐方式,其敏感列表会自动包含所有在块内读取的信号,可以有效避免因敏感列表不完整而意外生成锁存器(Latch)。
示例:一个2选1多路选择器
module Mux2_to_1 ( input wire a, // 输入数据0 input wire b, // 输入数据1 input wire sel, // 选择信号 output reg y // 输出 ); // 使用 always@(*) 块描述组合逻辑 always @(*) begin if (sel == 1'b0) begin y = a; else begin y = b; end end // 也可以用 assign 语句实现,更为简洁 // assign y = (sel == 1'b0) ? a : b; endmodule
设计关键点:
避免生成锁存器 (Latch): 在 `always` 块中,如果 `if` 或 `case` 语句没有覆盖所有可能的情况,并且没有为所有分支下的所有输出信号赋值,综合器会为了“记住”之前的值而生成一个锁存器。锁存器对时序非常敏感,容易导致时序问题,在同步设计中应极力避免。逻辑冒险 (Logic Hazard): 由于信号通过不同路径的延迟不同,输入信号的单个跳变可能导致输出产生短暂的毛刺(Glitch)。这在异步电路中是致命的,但在全同步设计中,只要保证在时钟有效沿到来之前输出已经稳定,毛刺通常不会造成危害。
2. 时序逻辑 (Sequential Logic)
2.1 专业描述
时序逻辑电路的输出不仅取决于当前的输入,还与电路过去的状态(存储在记忆元件中的值)有关。因此,时序逻辑是具有记忆功能的电路。时钟信号(Clock)是时序逻辑的灵魂,它规定了电路状态更新的节奏,确保整个系统能够同步、有序地工作。
核心特征:
记忆性: 电路包含存储元件(通常是D触发器),能够保存状态信息。
时钟驱动: 电路的状态通常只在时钟的特定边沿(上升沿或下降沿)发生变化。
反馈 (Feedback): 时序逻辑通常包含从记忆元件输出到其输入的反馈路径,这是实现状态更新的基础。
2.2 设计与实现
时序逻辑是构建寄存器、计数器、移位寄存器以及状态机等模块的基础。
核心元件: D触发器 (D Flip-Flop, DFF): 是FPGA中最基本的时序逻辑单元。在时钟有效沿,它会将D输入端的值锁存到Q输出端,并保持到下一个有效沿。
HDL 实现 (以 Verilog 为例): 使用 `always @(posedge clk or negedge rst_n)` 这种带有时钟边沿触发的 `always` 块来描述。 使用非阻塞赋值 (`<=`)是时序逻辑设计的金科玉律。它能够正确地模拟出在同一个时钟沿,所有触发器同时采样、然后同时更新的行为。
示例:一个带异步复位的4位计数器
module Counter_4bit ( input wire clk, // 时钟 input wire rst_n, // 异步复位,低有效 output reg [3:0] q // 计数器输出 ); // 使用带时钟边沿的 always 块描述时序逻辑 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin // 异步复位逻辑 q <= 4'b0000; else begin // 同步逻辑 q <= q + 1'b1; end end endmodule
设计关键点:
同步设计原则:
坚持“所有存储元件都使用同一个(或同源的)时钟”的原则,可以极大简化时序分析。
复位策略:
异步复位: 复位信号不依赖时钟,可以立即生效。优点是复位快,缺点是复位信号的释放(撤销)必须是同步的,否则会引入亚稳态,即所谓的“异步复位,同步释放”。
同步复位: 复位信号只有在时钟有效沿才生效。优点是设计更稳定,时序分析更简单。
阻塞赋值 vs. 非阻塞赋值: 在时序逻辑块中,务必使用非阻塞赋值 (`<=`)。如果误用阻塞赋值 (`=`),会改变仿真行为,使其不再能准确模拟硬件的并行更新特性,可能导致仿真结果与实际硬件行为不符。
3. 状态机 (Finite State Machine, FSM)
3.1专业描述
状态机是时序逻辑的一种特殊且强大的应用,用于设计控制逻辑(Control Path)。它由一组有限的状态(States)、一个初始状态(Initial State)、一组输入(Inputs)以及在输入作用下决定状态如何转移的转移函数(Transition Function)和决定输出的输出函数(Output Function)构成。 FSM是系统控制逻辑的抽象模型,能够清晰、结构化地描述一个系统如何根据外部输入和内部状态来决策其行为。
两大类型:
Mealy 型状态机: 输出不仅与当前状态有关,还与当前输入有关。这意味着输入的变化能够立即(经过组合逻辑延迟后)影响输出,而无需等待下一个时钟沿。
Moore 型状态机: 输出只与当前状态有关。这意味着只有在时钟沿到来、状态发生改变后,输出才会更新。
特性 | Moore 型状态机 | Mealy 型状态机 |
输出 | 仅依赖于当前状态 | 依赖于当前状态和当前输入 |
响应速度 | 输出对输入的响应会延迟一个时钟周期 | 输出对输入的变化可以立即响应 |
状态数量 | 通常需要更多的状态来实现相同功能 | 可能用更少的状态 |
硬件实现 | 输出逻辑更简单,通常直接是状态寄存器的输出或其译码 | 输出逻辑更复杂,是状态和输入的组合逻辑 |
时序 | 时序性能更好,因为输出是寄存器输出,没有长的组合逻辑路径 | 从输入到输出的组合逻辑路径可能很长,影响最高工作频率 |
3.2 设计与实现
一个标准的FSM实现包含三个部分:
1. 次态逻辑 (Next State Logic): 这是一个组合逻辑块,根据当前状态和输入,决定下一个状态是什么。
2. 状态寄存器 (State Register): 这是一个时序逻辑块,在时钟有效沿到来时,用次态更新当前状态。
3. 输出逻辑 (Output Logic): 这是一个组合逻辑块,根据当前状态(和输入,如果是Mealy型)产生输出信号。
HDL 实现 (三段式描述法): 这是最推荐、最清晰、最不易出错的FSM写法。
示例:一个检测序列“1101”的Mealy型状态机
module SequenceDetector_1101 ( input wire clk, input wire rst_n, input wire din, output reg match ); // 1. 定义状态 (使用 parameter 定义,增加可读性) parameter S_IDLE = 3'b000; parameter S_1 = 3'b001; parameter S_11 = 3'b010; parameter S_110 = 3'b011; // 2. 状态寄存器 (时序逻辑) reg [2:0] current_state, next_state; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin current_state <= S_IDLE; else begin current_state <= next_state; end end // 3. 次态逻辑 (组合逻辑) always @(*) begin // 默认情况下保持当前状态 next_state = current_state; case (current_state) S_IDLE: if (din == 1'b1) next_state = S_1; else next_state = S_IDLE; S_1: if (din == 1'b1) next_state = S_11; else next_state = S_IDLE; S_11: if (din == 1'b0) next_state = S_110; else next_state = S_11; S_110: if (din == 1'b1) next_state = S_1; else next_state = S_IDLE; default: next_state = S_IDLE; endcase end // 4. 输出逻辑 (组合逻辑, Mealy型) always @(*) begin // 默认输出为0 match = 1'b0; if (current_state == S_110 && din == 1'b1) begin match = 1'b1; end end endmodule
示例:一个检测序列“1101”的 Moore 型状态机
module SequenceDetector_1101_Moore ( input wire clk, input wire rst_n, input wire din, // 输入数据 output reg match // 匹配成功信号 ); // 1. 定义状态 (比Mealy型多一个专门的匹配状态) parameter S_IDLE = 3'b000; parameter S_1 = 3'b001; parameter S_11 = 3'b010; parameter S_110 = 3'b011; parameter S_1101_MATCH = 3'b100; // <--- 专门用于输出 match=1 的状态 // 2. 状态寄存器 (时序逻辑,与Mealy型完全相同) reg [2:0] current_state, next_state; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin current_state <= S_IDLE; else begin current_state <= next_state; end end // 3. 次态逻辑 (组合逻辑) // 根据当前状态和当前输入,决定下一个状态 always @(*) begin // 默认情况下,下一个状态是IDLE,这是一种安全的设计实践 next_state = S_IDLE; case (current_state) S_IDLE: if (din == 1'b1) next_state = S_1; else next_state = S_IDLE; S_1: if (din == 1'b1) next_state = S_11; else next_state = S_IDLE; S_11: if (din == 1'b0) next_state = S_110; else next_state = S_11; // 接收到"111",末尾两位仍是"11" S_110: if (din == 1'b1) next_state = S_1101_MATCH; // <--- 序列匹配成功,跳转到MATCH状态 else next_state = S_IDLE; S_1101_MATCH: // 在输出匹配信号之后,判断下一个状态 if (din == 1'b1) next_state = S_11; // 处理重叠序列,"1101"后面的"1"可以是新序列"11"的开始 else next_state = S_IDLE; default: next_state = S_IDLE; endcase end // 4. 输出逻辑 (组合逻辑, Moore型) // 输出只和当前状态有关! always @(*) begin if (current_state == S_1101_MATCH) begin match = 1'b1; else begin match = 1'b0; end end // 上面的 always 块也可以用一条 assign 语句等效实现,更简洁: // assign match = (current_state == S_1101_MATCH); endmodule