状态机思想编程

发布于:2025-04-05 ⋅ 阅读:(12) ⋅ 点赞:(0)


一、状态机思想重新写一个 LED流水灯的FPGA代码

1.状态机的概念

状态机的基本要素有 3 个,其实我们在第一节的举例中都有涉及,只是没有点明,它们是:状态、输出和输入。
1、状态:也叫状态变量。在逻辑设计中,使用状态划分逻辑顺序和时序规律。比如:设计伪随机码发生器时,可以用移位寄存器序列作为状态;在设计电机控制电路时,可以以电机的不同转速作为状态;在设计通信系统时,可以用信令的状态作为状态变量等。
2、输出:输出指在某一个状态时特定发生的事件。如设计电机控制电路中,如果电机转速过高,则输出为转速过高报警,也可以伴随减速指令或降温措施等。
3、输入:指状态机中进入每个状态的条件,有的状态机没有输入条件,其中的状态转移较为简单,有的状态机有输入条件,当某个输入条件存在时才能转移到相应的状态。
根据状态机的输出是否与输入条件相关,可将状态机分为两大类:摩尔(Moore)型状态机和米勒(Mealy)型状态机。

2.代码设计

// 流水灯模块代码:led_fsm.v
module led_fsm(
    input        clk,     // 时钟信号 (50MHz)
    input        rst_n,   // 复位信号 (低有效)
    output reg [3:0] led  // LED输出,低电平点亮
);

// 定义状态编码
parameter S0 = 2'b00;    // LED0亮
parameter S1 = 2'b01;    // LED1亮
parameter S2 = 2'b10;    // LED2亮
parameter S3 = 2'b11;    // LED3亮

reg [1:0] state;         // 当前状态
reg [1:0] next_state;    // 下一状态
reg [24:0] counter;      // 计时器(0.5秒@50MHz)

// 状态寄存器
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        state <= S0;
    else
        state <= next_state;
end

// 状态转移逻辑
always @(*) begin
    case(state)
        S0: next_state = (counter == 25'd24_999_999) ? S1 : S0;
        S1: next_state = (counter == 25'd24_999_999) ? S2 : S1;
        S2: next_state = (counter == 25'd24_999_999) ? S3 : S2;
        S3: next_state = (counter == 25'd24_999_999) ? S0 : S3;
        default: next_state = S0;
    endcase
end

// 计数器逻辑
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        counter <= 25'd0;
    else if(next_state != state)
        counter <= 25'd0;
    else
        counter <= counter + 25'd1;
end

// 输出逻辑
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        led <= 4'b1110;  // 初始状态LED0亮
    else begin
        case(state)
            S0: led <= 4'b1110;
            S1: led <= 4'b1101;
            S2: led <= 4'b1011;
            S3: led <= 4'b0111;
            default: led <= 4'b1111;
        endcase
    end
end

endmodule

代码解释:
使用 二进制编码(2位可表示4个状态)。每个状态对应一个LED点亮(S0→LED0,S1→LED1,依此类推)。
state 和 next_state 用于实现状态寄存器和组合逻辑的分离。
计数器阈值:24_999_999 对应 0.5 秒计时(50MHz时钟下,周期20ns,总时间=25,000,000×20ns=500ms)。
状态转移条件:当计数器达到阈值时切换到下一状态,否则保持当前状态。
复位清零:复位或状态即将切换时计数器清零。连续计数:在状态保持期间,每个时钟周期加1。低电平有效:0 表示点亮LED(例如 4’b1110 表示LED0亮)。同步输出:在时钟上升沿更新输出,避免毛刺。

二、CPLD和FPGA芯片的主要技术区别与适用场合

在这里插入图片描述
二、适用场合
CPLD 的典型应用
1.控制密集型任务
状态机、总线接口控制(如PCI、I2C)、逻辑粘合(Glue Logic)。简单数据处理(编码转换、电平转换)。
2.实时性与可靠性要求高的场景
工业控制、电源管理、电机驱动。需要快速上电启动(毫秒级配置加载)。
3.低功耗与成本敏感设计
便携设备、加密芯片控制、简单协议转换。

FPGA 的典型应用
1.数据密集型任务
高速信号处理(图像处理、通信基带)、数字滤波器、FFT。并行计算(AI加速、区块链哈希运算)。
2.复杂系统原型与动态重构
ASIC/SoC原型验证、软件定义无线电(SDR)。支持部分动态重配置(如Xilinx的部分型号)。
3.高吞吐量接口
高速串行通信(PCIe、10G以太网)、视频编解码(H.265/AV1)。数据中心加速(SmartNIC、存储控制器)。

三、hdlbitsFPGA教程网站上进行学习

1.创建一个具有两个 2 位输入A[1:0]和B[1:0]的电路,并产生一个输出z。如果A = B ,则z的值应为 1 ,否则z应为 0。

module top_module ( input [1:0] A, input [1:0] B, output z ); 
    assign z = (A ==B) ? 1'B1 : 1'B0;
endmodule

2.创建一个半加法器。半加器将两位相加(没有进位)得到加和和进位。

module top_module( 
    input a, b,
    output cout, sum );
	assign sum = a^b;
    assign cout = a&b;
endmodule

3.创建一个全加器。全加器将三位相加(包括进位)并产生和和进位。

module top_module( 
    input a, b, cin,
    output cout, sum );
    assign sum = a^b^cin;
    assign cout = a&b | a&cin | b&cin;
endmodule

4.创建 3 个实例来创建一个 3 位二进制波纹进位加法器。加法器将两个 3 位数字和一个进位相加产生一个 3 位加和和进位。为了鼓励实例化全加器,还要输出纹波进位加法器中每个全加器的进位。cout[2] 是最后一个全加器的最终进位,也是您通常看到的进位。

//第一种方法
module top_module( 
    input [2:0] a, b,
    input cin,
    output [2:0] cout,
    output [2:0] sum );
    add1 u1 (.a(a[0]),.b(b[0]),.cin(cin),.sum(sum[0]),.cout(cout[0]));
    add1 u2 (.a(a[1]),.b(b[1]),.cin(cout[0]),.sum(sum[1]),.cout(cout[1]));
    add1 u3 (.a(a[2]),.b(b[2]),.cin(cout[1]),.sum(sum[2]),.cout(cout[2]));
endmodule
 
module add1 ( input a, input b, input cin,   output sum, output cout );
	assign sum = a^b^cin;
    assign cout = a&b|a&cin|b&cin;
endmodule

5.假设您有两个 8 位 的补码,a[7:0] 和 b[7:0]。这些数字相加产生 s[7:0]。还要计算是否发生了(有符号的)溢出。

module top_module (
    input [7:0] a,
    input [7:0] b,
    output [7:0] s,
    output overflow
); 
    assign s = a+b;
    assign overflow = (a[7]&b[7]&~s[7])|(~a[7]&~b[7]&s[7]);
endmodule