时序逻辑设计
时序逻辑电路的输出不仅取决于当前输入,还取决于电路的历史状态。本章介绍如何使用 Verilog 描述时序逻辑电路。
时序逻辑基础
时序逻辑特点
- 包含存储元件(触发器、锁存器)
- 输出取决于当前输入和当前状态
- 需要时钟信号同步
- 具有记忆功能
同步时序电路
现代数字设计主要采用同步时序电路:
- 所有触发器使用同一个时钟
- 状态变化发生在时钟边沿
- 易于分析和验证
触发器
D 触发器
D 触发器是最基本的时序元件:
module d_flip_flop(
input clk,
input reset,
input d,
output reg q
);
always @(posedge clk or posedge reset) begin
if (reset)
q <= 1'b0;
else
q <= d;
end
endmodule
带使能的 D 触发器
module dff_enable(
input clk,
input reset,
input enable,
input d,
output reg q
);
always @(posedge clk or posedge reset) begin
if (reset)
q <= 1'b0;
else if (enable)
q <= d;
end
endmodule
同步复位 D 触发器
module dff_sync_reset(
input clk,
input reset,
input d,
output reg q
);
always @(posedge clk) begin
if (reset)
q <= 1'b0;
else
q <= d;
end
endmodule
异步复位与同步复位的区别
| 特性 | 异步复位 | 同步复位 |
|---|---|---|
| 复位时机 | 立即生效 | 时钟边沿生效 |
| 敏感列表 | 包含 reset | 不包含 reset |
| 适用场景 | 系统初始化 | 正常操作中的复位 |
寄存器
基本寄存器
module register #(
parameter WIDTH = 8
)(
input clk,
input reset,
input enable,
input [WIDTH-1:0] din,
output reg [WIDTH-1:0] dout
);
always @(posedge clk or posedge reset) begin
if (reset)
dout <= {WIDTH{1'b0}};
else if (enable)
dout <= din;
end
endmodule
移位寄存器
module shift_register #(
parameter WIDTH = 8
)(
input clk,
input reset,
input din,
input enable,
input direction, // 0: 左移, 1: 右移
output dout
);
reg [WIDTH-1:0] shift_reg;
always @(posedge clk or posedge reset) begin
if (reset)
shift_reg <= {WIDTH{1'b0}};
else if (enable) begin
if (direction)
shift_reg <= {din, shift_reg[WIDTH-1:1]}; // 右移
else
shift_reg <= {shift_reg[WIDTH-2:0], din}; // 左移
end
end
assign dout = direction ? shift_reg[0] : shift_reg[WIDTH-1];
endmodule
通用移位寄存器
module universal_shift_reg #(
parameter WIDTH = 8
)(
input clk,
input reset,
input [1:0] mode, // 00: 保持, 01: 左移, 10: 右移, 11: 加载
input din_left,
input din_right,
input [WIDTH-1:0] din,
output [WIDTH-1:0] dout
);
reg [WIDTH-1:0] data;
always @(posedge clk or posedge reset) begin
if (reset)
data <= {WIDTH{1'b0}};
else begin
case (mode)
2'b00: data <= data; // 保持
2'b01: data <= {data[WIDTH-2:0], din_left}; // 左移
2'b10: data <= {din_right, data[WIDTH-1:1]}; // 右移
2'b11: data <= din; // 加载
endcase
end
end
assign dout = data;
endmodule
计数器
二进制计数器
module binary_counter #(
parameter WIDTH = 8
)(
input clk,
input reset,
input enable,
output [WIDTH-1:0] count,
output carry
);
reg [WIDTH-1:0] counter;
always @(posedge clk or posedge reset) begin
if (reset)
counter <= {WIDTH{1'b0}};
else if (enable)
counter <= counter + 1;
end
assign count = counter;
assign carry = (counter == {WIDTH{1'b1}}) & enable;
endmodule
可预置计数器
module loadable_counter #(
parameter WIDTH = 8
)(
input clk,
input reset,
input load,
input enable,
input [WIDTH-1:0] data,
output [WIDTH-1:0] count
);
reg [WIDTH-1:0] counter;
always @(posedge clk or posedge reset) begin
if (reset)
counter <= {WIDTH{1'b0}};
else if (load)
counter <= data;
else if (enable)
counter <= counter + 1;
end
assign count = counter;
endmodule
减法计数器
module down_counter #(
parameter WIDTH = 8
)(
input clk,
input reset,
input enable,
output [WIDTH-1:0] count,
output zero
);
reg [WIDTH-1:0] counter;
always @(posedge clk or posedge reset) begin
if (reset)
counter <= {WIDTH{1'b0}};
else if (enable)
counter <= counter - 1;
end
assign count = counter;
assign zero = (counter == {WIDTH{1'b0}});
endmodule
可逆计数器
module up_down_counter #(
parameter WIDTH = 8
)(
input clk,
input reset,
input enable,
input up_down, // 0: 减法, 1: 加法
output [WIDTH-1:0] count
);
reg [WIDTH-1:0] counter;
always @(posedge clk or posedge reset) begin
if (reset)
counter <= {WIDTH{1'b0}};
else if (enable) begin
if (up_down)
counter <= counter + 1;
else
counter <= counter - 1;
end
end
assign count = counter;
endmodule
BCD 计数器
module bcd_counter(
input clk,
input reset,
input enable,
output reg [3:0] count,
output carry
);
always @(posedge clk or posedge reset) begin
if (reset)
count <= 4'b0;
else if (enable) begin
if (count == 4'd9)
count <= 4'b0;
else
count <= count + 1;
end
end
assign carry = (count == 4'd9) & enable;
endmodule
状态机
Moore 状态机
Moore 状态机的输出只取决于当前状态:
module moore_fsm(
input clk,
input reset,
input x,
output reg y
);
// 状态定义
parameter S0 = 2'b00;
parameter S1 = 2'b01;
parameter S2 = 2'b10;
reg [1:0] state, next_state;
// 状态寄存器
always @(posedge clk or posedge reset) begin
if (reset)
state <= S0;
else
state <= next_state;
end
// 次态逻辑
always @(*) begin
case (state)
S0: next_state = x ? S1 : S0;
S1: next_state = x ? S2 : S0;
S2: next_state = x ? S2 : S0;
default: next_state = S0;
endcase
end
// 输出逻辑
always @(*) begin
case (state)
S0: y = 1'b0;
S1: y = 1'b0;
S2: y = 1'b1;
default: y = 1'b0;
endcase
end
endmodule
Mealy 状态机
Mealy 状态机的输出取决于当前状态和当前输入:
module mealy_fsm(
input clk,
input reset,
input x,
output reg y
);
// 状态定义
parameter S0 = 2'b00;
parameter S1 = 2'b01;
parameter S2 = 2'b10;
reg [1:0] state, next_state;
// 状态寄存器
always @(posedge clk or posedge reset) begin
if (reset)
state <= S0;
else
state <= next_state;
end
// 次态逻辑
always @(*) begin
case (state)
S0: next_state = x ? S1 : S0;
S1: next_state = x ? S2 : S0;
S2: next_state = x ? S2 : S0;
default: next_state = S0;
endcase
end
// 输出逻辑(依赖输入)
always @(*) begin
case (state)
S0: y = 1'b0;
S1: y = x;
S2: y = 1'b1;
default: y = 1'b0;
endcase
end
endmodule
一段式状态机
module one_process_fsm(
input clk,
input reset,
input x,
output reg y
);
parameter S0 = 2'b00, S1 = 2'b01, S2 = 2'b10;
reg [1:0] state;
always @(posedge clk or posedge reset) begin
if (reset) begin
state <= S0;
y <= 1'b0;
end else begin
case (state)
S0: begin
y <= 1'b0;
if (x) state <= S1;
end
S1: begin
y <= x;
if (x) state <= S2;
else state <= S0;
end
S2: begin
y <= 1'b1;
if (!x) state <= S0;
end
default: begin
state <= S0;
y <= 1'b0;
end
endcase
end
end
endmodule
序列检测器
检测序列 "1101":
module sequence_detector(
input clk,
input reset,
input din,
output detected
);
parameter IDLE = 0;
parameter S1 = 1;
parameter S11 = 2;
parameter S110 = 3;
reg [1:0] state, next_state;
// 状态寄存器
always @(posedge clk or posedge reset) begin
if (reset)
state <= IDLE;
else
state <= next_state;
end
// 次态逻辑
always @(*) begin
case (state)
IDLE: next_state = din ? S1 : IDLE;
S1: next_state = din ? S11 : IDLE;
S11: next_state = din ? S11 : S110;
S110: next_state = din ? S1 : IDLE;
default: next_state = IDLE;
endcase
end
// 输出逻辑
assign detected = (state == S110) && din;
endmodule
时钟分频器
简单分频器
module clock_divider #(
parameter DIV = 2
)(
input clk,
input reset,
output clk_out
);
reg [$clog2(DIV)-1:0] counter;
reg clk_reg;
always @(posedge clk or posedge reset) begin
if (reset) begin
counter <= 0;
clk_reg <= 1'b0;
end else if (counter == DIV/2 - 1) begin
counter <= 0;
clk_reg <= ~clk_reg;
end else begin
counter <= counter + 1;
end
end
assign clk_out = clk_reg;
endmodule
偶数分频
module even_divider #(
parameter N = 4 // 分频系数
)(
input clk,
input reset,
output clk_out
);
reg [$clog2(N)-1:0] counter;
always @(posedge clk or posedge reset) begin
if (reset)
counter <= 0;
else if (counter == N/2 - 1)
counter <= 0;
else
counter <= counter + 1;
end
assign clk_out = counter[$clog2(N)-1];
endmodule
奇数分频
module odd_divider #(
parameter N = 3 // 分频系数(奇数)
)(
input clk,
input reset,
output clk_out
);
reg [$clog2(N)-1:0] count_p, count_n;
reg clk_p, clk_n;
// 上升沿计数
always @(posedge clk or posedge reset) begin
if (reset)
count_p <= 0;
else if (count_p == N - 1)
count_p <= 0;
else
count_p <= count_p + 1;
end
// 上升沿时钟
always @(posedge clk or posedge reset) begin
if (reset)
clk_p <= 1'b0;
else if (count_p == (N-1)/2 || count_p == N-1)
clk_p <= ~clk_p;
end
// 下降沿计数
always @(negedge clk or posedge reset) begin
if (reset)
count_n <= 0;
else if (count_n == N - 1)
count_n <= 0;
else
count_n <= count_n + 1;
end
// 下降沿时钟
always @(negedge clk or posedge reset) begin
if (reset)
clk_n <= 1'b0;
else if (count_n == (N-1)/2 || count_n == N-1)
clk_n <= ~clk_n;
end
assign clk_out = clk_p | clk_n;
endmodule
脉冲生成
单脉冲生成
module pulse_generator(
input clk,
input reset,
input trigger,
output pulse
);
reg trigger_d;
always @(posedge clk or posedge reset) begin
if (reset)
trigger_d <= 1'b0;
else
trigger_d <= trigger;
end
assign pulse = trigger & ~trigger_d;
endmodule
可编程脉冲
module programmable_pulse #(
parameter WIDTH = 8
)(
input clk,
input reset,
input trigger,
input [WIDTH-1:0] duration,
output pulse
);
reg [WIDTH-1:0] counter;
reg pulse_reg;
reg running;
always @(posedge clk or posedge reset) begin
if (reset) begin
counter <= {WIDTH{1'b0}};
pulse_reg <= 1'b0;
running <= 1'b0;
end else begin
if (trigger && !running) begin
running <= 1'b1;
counter <= duration;
pulse_reg <= 1'b1;
end else if (running && counter > 0) begin
counter <= counter - 1;
end else if (running && counter == 0) begin
running <= 1'b0;
pulse_reg <= 1'b0;
end
end
end
assign pulse = pulse_reg;
endmodule
同步器
两级同步器
用于跨时钟域的单比特信号同步:
module synchronizer(
input clk,
input reset,
input din,
output dout
);
reg din_d1, din_d2;
always @(posedge clk or posedge reset) begin
if (reset) begin
din_d1 <= 1'b0;
din_d2 <= 1'b0;
end else begin
din_d1 <= din;
din_d2 <= din_d1;
end
end
assign dout = din_d2;
endmodule
非阻塞赋值
时序逻辑必须使用非阻塞赋值(<=):
// 正确:时序逻辑使用非阻塞赋值
always @(posedge clk) begin
q <= d;
end
// 错误:时序逻辑使用阻塞赋值
always @(posedge clk) begin
q = d; // 可能导致仿真与综合不一致
end
非阻塞赋值的优势
- 正确模拟硬件行为
- 避免竞争条件
- 仿真与综合结果一致
小结
本章介绍了 Verilog 时序逻辑设计:
- 触发器:D 触发器、同步/异步复位
- 寄存器:基本寄存器、移位寄存器
- 计数器:二进制、BCD、可逆计数器
- 状态机:Moore、Mealy 状态机
- 时钟分频:偶数分频、奇数分频
- 脉冲生成:单脉冲、可编程脉冲
- 同步器:跨时钟域同步
- 非阻塞赋值:时序逻辑的正确赋值方式
掌握时序逻辑设计是数字电路设计的核心技能。下一章将介绍层次化设计。