一、状态机思想重新写一个 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