在数字电路设计领域,有限状态机(Finite State Machine,FSM)是一种强大的设计工具,它能够有效地处理复杂的逻辑控制。结合 Verilog 语言进行基于有限状态机的数字电路设计,是深入理解数字系统设计的关键路径。
一、实验目的
本次实验聚焦多个关键目标,旨在全面提升在数字电路设计方面的能力。
- 掌握 Verilog 设计要点:深入学习 Verilog 数字系统设计的三个关键要点,包括代码可读性与可移植性、算法优化以及基于有限状态机的逻辑控制设计。
- 优化代码质量:学会通过具体的代码学习,让 Verilog 代码更具可读性和可移植性,这对于构建大型、复杂的数字系统至关重要。
- 降低计算复杂度:运用算法优化策略,减少计算复杂度,提高电路的运行效率,例如利用特定的数学变换公式或更高效的算法结构。
- 理解有限状态机:理解有限状态机的概念和设计原则,包括状态转移、状态存储和输出逻辑等方面,这是设计复杂数字逻辑的核心。
- 掌握状态机结构:掌握 Moore 状态机和 Mealy 状态机的基本结构,了解它们在状态转移和输出逻辑上的差异,以便在不同场景中选择合适的状态机类型。
- 实现序列检测器:使用 Verilog 语言实现两个序列检测器(1101 序列和 1001 序列)的逻辑功能,并通过仿真验证其正确性。
二、实验环境
实验借助了以下工具:
- Modelsim:用于对设计的数字电路进行功能仿真,能够直观地观察信号的变化和电路的工作状态,帮助发现和解决设计中的问题。
- Notepad++:作为代码编辑器,提供了便捷的代码编写环境,支持语法高亮等功能,提高编程效率。
- 笔记本电脑:为整个实验提供运行和存储环境,确保工具和代码的正常运行。
三、实验步骤
(一)Verilog 数字系统设计要点
Verilog 数字系统设计的三个关键要点分别是代码的可读性与可移植性、算法的运用以及基于有限状态机的逻辑控制设计。代码的可读性和可移植性影响着项目的维护和扩展,算法的选择直接关系到电路的计算效率,而有限状态机则是实现复杂逻辑控制的核心手段。
(二)提升代码可读性和可移植性
通过具体的代码学习来掌握提升代码质量的方法。例如,在代码中使用参数化设计,将常用的常量定义为参数,方便修改和复用。如定义参数parameter SIZE = 2;
,后续在定义输入输出端口时使用input [SIZE:1] ain_r;
,这样当需要修改数据位宽时,只需修改变量SIZE
的值,而无需在多处重复修改端口定义,大大提高了代码的可维护性和可移植性。
(三)降低计算复杂度的算法
采用有效的算法来降低计算复杂度。如(cos α)×(cos β)=cos(α+β)+cos(α -β)
这样的数学变换公式,在涉及三角函数运算的电路设计中,使用这种变换可以减少乘法运算的次数,从而降低计算复杂度,提高电路的运行速度。在数字信号处理领域,从离散傅里叶变换(DFT)到快速傅里叶变换(FFT)的转变,也是通过优化算法结构,显著减少了计算量。
(四)有限状态机
状态转移图与逻辑图:有限状态机的设计核心在于状态转移图和状态电路逻辑图。以实验中的状态转移图为例,它清晰地展示了不同输入条件下状态之间的转换关系。如在某个状态机中,当满足条件A&(!B)&C
时,状态从当前状态转移到特定状态,同时输出信号也会相应改变。状态电路逻辑图则描述了状态机的内部结构,包括组合逻辑和状态寄存器等部分,它们协同工作实现状态的转移和输出。
Moore 状态机和 Mealy 状态机:Moore 状态机和 Mealy 状态机在结构和特性上有所不同。Moore 状态机的输出仅取决于当前状态,而 Mealy 状态机的输出则与当前状态和输入信号都相关。在实际设计中,需要根据具体的应用场景选择合适的状态机类型。例如,在对输出稳定性要求较高,且输出不依赖于即时输入变化的场景中,Moore 状态机更为合适;而在需要根据输入及时调整输出的场景下,Mealy 状态机则能更好地发挥作用。
(五)1101 序列检测器(Moore 型)
状态转移与输出逻辑代码实现
module detector_1101(
input wire clk, // 时钟信号
input wire reset, // 复位信号
input wire in, // 输入序列
output reg out // 输出信号
);
// 定义状态常量,模拟枚举类型
parameter S0 = 0, S1 = 1, S11 = 2, S110 = 3, DETECTED = 4;
// 声明表示当前状态和下一个状态的寄存器
reg [2:0] current_state, next_state;
// 状态转移逻辑
always @(posedge clk or posedge reset) begin
if (reset)
current_state <= S0;
else
current_state <= next_state;
end
// 下一个状态计算
always @(*) begin
case (current_state)
S0: next_state = (in == 1)? S1 : S0;
S1: next_state = (in == 1)? S11 : S0;
S11: next_state = (in == 0)? S110 : S11;
S110: next_state = (in == 1)? DETECTED : S0;
DETECTED: next_state = (in == 1)? S11 : S0;
default: next_state = S0;
endcase
end
// 输出逻辑(Moore型状态机)
always @(*) begin
case (current_state)
DETECTED: out = 1;
default: out = 0;
endcase
end
endmodule
测试平台搭建:
`timescale 1ps / 1ps
module tb();
// 输入输出声明
reg clk; // 时钟信号
reg reset; // 复位信号
reg in; // 输入序列
wire out; // 输出信号
// 实例化待测模块
detector_1101 d_1101 (
.clk(clk),
.reset(reset),
.in(in),
.out(out)
);
// 时钟信号生成
initial begin
clk = 0;
forever #(5) clk = ~clk; // 生成一个周期为10ns的时钟信号
end
// 复位信号和输入序列生成
initial begin
// 初始化
reset = 1;
in = 0;
// 复位
#(10);
reset = 0;
// 测试序列
#(10); in = 0; // 空闲
#(10); in = 1; // 开始发送1
#(10); in = 1; // 继续发送1,进入S11状态
#(10); in = 0; // 发送0,进入S110状态
#(10); in = 1; // 发送1,应该检测到1101序列,并输出高电平
#(10); in = 0; // 发送0,退出检测状态
#(10); in = 1; // 重新发送1,开始新的检测过程
#(10); in = 1; // 发送第二个1
#(10); in = 0; // 此时不会进入检测状态,因为没有完整的1101序列
#(10); in = 0; // 发送0,回到初始状态
// 完成测试
$stop;
end
endmodule
仿真结果:
(六)1101 序列检测器(Mealy 型)
状态转移与输出逻辑代码实现:
module detector_1001(
input clk, // 时钟信号
input reset, // 复位信号
input in, // 输入信号
output reg detected // 输出信号,当检测到1001序列时为1
);
// 定义状态常量,模拟枚举类型
parameter S0 = 0, S1 = 1, S10 = 2, S100 = 3, DETECTED = 4;
// 声明表示当前状态和下一个状态的寄存器
reg [2:0] current_state, next_state;
// 状态转移和输出逻辑
always @(posedge clk or posedge reset) begin
if (reset) begin
current_state <= S0;
detected <= 0;
end else begin
current_state <= next_state;
end
end
// 确定下一个状态和输出
always @(*) begin
case(current_state)
S0: begin
if (in == 1) begin
next_state = S1;
detected = 0;
end else begin
next_state = S0;
detected = 0;
end
end
S1: begin
if (in == 0) begin
next_state = S10;
detected = 0;
end else begin
next_state = S1;
detected = 0;
end
end
S10: begin
if (in == 0) begin
next_state = S100;
detected = 0;
end else if (in == 1) begin
next_state = S1;
detected = 0;
end
end
S100: begin
if (in == 1) begin
next_state = DETECTED;
detected = 0;
end else if (in == 0) begin
next_state = S0;
detected = 0;
end
end
DETECTED: begin
if (in == 1) begin
next_state = S1;
detected = 1;
end else if (in == 0) begin
next_state = 10; // 这里疑似代码错误,可能应为S0,你可以根据实际情况确认
detected = 1;
end
end
default: begin
next_state = S0;
detected = 0;
end
endcase
end
endmodule
测试平台搭建
`timescale 1ps / 1ps
module tb();
reg clk;
reg reset;
reg in;
wire detected;
// Instantiate the seq_detector module
detector_1001 d_1001 (
.clk(clk),
.reset(reset),
.in(in),
.detected(detected)
);
// Clock generation
initial begin
clk = 0;
forever begin
#5 clk = ~clk; // Generate a 10 MHz clock signal
end
end
// Test stimulus
initial begin
// Initialize signals
reset = 1;
in = 0;
#10 reset = 0; // Release reset
#10 in = 1;
#10 in = 0;
#10 in = 0;
#10 in = 1;
#10;
#10 in = 0;
#10 in = 1;
#10 in = 1;
#10 in = 0;
#10 in = 1;
#10;
#10 reset = 1; // Apply reset
#10 reset = 0; // Release reset
#10;
$stop; // Stop the simulation
end
endmodule
仿真结果: