跳到主要内容

时序逻辑设计

时序逻辑电路的输出不仅取决于当前输入,还取决于电路的历史状态。本章介绍如何使用 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 &gt; 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

非阻塞赋值的优势

  1. 正确模拟硬件行为
  2. 避免竞争条件
  3. 仿真与综合结果一致

小结

本章介绍了 Verilog 时序逻辑设计:

  • 触发器:D 触发器、同步/异步复位
  • 寄存器:基本寄存器、移位寄存器
  • 计数器:二进制、BCD、可逆计数器
  • 状态机:Moore、Mealy 状态机
  • 时钟分频:偶数分频、奇数分频
  • 脉冲生成:单脉冲、可编程脉冲
  • 同步器:跨时钟域同步
  • 非阻塞赋值:时序逻辑的正确赋值方式

掌握时序逻辑设计是数字电路设计的核心技能。下一章将介绍层次化设计。