【PFPGA学习】状态机思想编程&&HDLbitsFPGA练习

发布于:2025-04-08 ⋅ 阅读:(18) ⋅ 点赞:(0)

目录

一、用状态机实现LED流水灯

1.1状态机思想

1.2状态机思想LED流水灯

1.3 modesim仿真

1.4 FPGA烧录实现

二、CPLD和FPGA芯片

1. 核心结构与技术原理

2. 性能与容量

3. 适用场景

4. 选型建议

三、HDLbitsFPGA练习记录(combinational logic)

练习一:7420chip

练习二:电路描述

 练习三、多路选择器1-9

练习四:Full adder

 练习五、Bcdadd4

四、总结

前言:

  1. 掌握状态机思想在FPGA开发中的应用。

  2. 学习Modelsim仿真与DE2-115开发板验证流程。

  3. 理解CPLD与FPGA技术差异及适用场景。

  4. 通过HDLbits刷题巩固组合逻辑设计能力。

一、用状态机实现LED流水灯

1.1状态机思想

状态机(Finite State Machine, FSM) 是一种描述系统状态转移的数学模型,广泛应用于时序逻辑设计。在FPGA中,状态机通过明确的状态划分和转移条件,简化复杂控制流程的设计。

①现态:是指当前所处的状态。

②条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。

③动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。

④次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

核心要素

  • 现态(Current State):系统当前所处的状态。

  • 事件(Event):触发状态迁移的条件(如用户输入、传感器信号等)。

  • 动作(Action):状态迁移时执行的操作(如开启设备、发送数据等)。

  • 次态(Next State):事件触发后系统将进入的新状态。

分类与模型

  • Moore型:输出仅由当前状态决定(如交通信号灯的红绿灯切换)。

  • Mealy型:输出由当前状态和输入共同决定(如网络协议中的应答机制)。

  • 确定型(DFA):每个状态对同一事件有唯一迁移路径。

  • 非确定型(NFA):同一事件可能触发多个迁移路径,需额外逻辑处理。

1.2状态机思想LED流水灯

1.代码编写(verilog):

状态机通过定义不同的状态和状态之间的转换逻辑,能够精确地控制LED灯的点亮顺序。在代码中,S0S7这8个状态分别对应着8个LED灯依次点亮的状态。通过状态的转换,实现了LED流水灯的效果。

module led_fsm(
    input clk,          // 50MHz时钟
    input rst_n,        // 复位信号(低有效)
    output reg [7:0] led
);
    // 状态定义(8个状态)
    localparam [2:0] S0=0, S1=1, S2=2, S3=3, S4=4, S5=5, S6=6, S7=7;
    reg [2:0] state;
    reg [24:0] cnt;
    wire en = (cnt == 25'd24_999_999); // 分频使能

    // 分频计数器
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) cnt <= 0;
        else if (en) cnt <= 0;
        else cnt <= cnt + 1;
    end

    // 状态机主逻辑
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state <= S0;
            led <= 8'b00000001; // 初始状态S0点亮LED0
        end
        else if (en) begin
            case(state)
              S0: begin led <= 8'b00000010; state <= S1; end  //S0→S1(第2个LED亮)
                S1: begin led <= 8'b00000100; state <= S2; end  //S1→S2(第3个LED亮)
              S2: begin led <= 8'b00001000; state <= S3; end  //S2→S3(第4个LED亮)
             S3: begin led <= 8'b00010000; state <= S4; end  //S3→S4(第5个LED亮)
                S4: begin led <= 8'b00100000; state <= S5; end  //S4→S5(第6个LED亮)
                S5: begin led <= 8'b01000000; state <= S6; end  //S5→S6(第7个LED亮)
             S6: begin led <= 8'b10000000; state <= S7; end  //S6→S7(第8个LED亮)
                S7: begin led <= 8'b00000001; state <= S0; end  //S7→S0(第1个LED亮,循环)
            default: state <= S0;                          // 默认回到初始状态
            endcase
        end
    end
endmodule

1.3 modesim仿真

创建工程文件后,跟着步骤来进行仿真操作(可参考参考文件三仿真步骤比较详细,由于之前的博客写过步骤,我就只保存截图了关键部分)

设置成为顶层文件后编译代码

点击Processing-->Start-->Start test bench template writer,这时我们的文件夹之下已经有了一个example.vt文件

(默认是保存在当前目录simulationmodelsim文件夹下的.vt格式文件。)对刚才生成的文件进行修改(可以直接复制下面代码,注意修改文件名称),然后在编译一次

`timescale 1ns/1ns
 
module led_fsm;
   reg        clk;
	reg        rst_n;
	reg        data;
	
	wire       pos_edge;
   wire       neg_edge;
   wire       data_edge;
	wire [1:0] D;
 
 
  example   u1(
      .clk(clk),
      .rst_n(rst_n),
      .data(data),
    
      .pos_edge(pos_edge),
      .neg_edge(neg_edge),
      .data_edge(data_edge),
		.D(D)
 );
 
 //产生时钟激励
 initial  clk = 1; 
 always #10  clk = ~clk;
 
 
 //输入激励
 initial  begin 
      rst_n=0;
		data=0;
      #100;
      rst_n = 1;
		#50
      data=1;
      #201;
      data=0;
      #201;
      data=1; 
      #101;
      data=0;	
      #200;
      $stop;
 
 end
 
 endmodule 

 现在设置仿真,点击Tools-->Options-->EDA ToolsOptions选项,点击浏览Modelsim安装目录下的Win64或者Win32

4、然后对仿真文件设置点击Assignments-->Settings,再点击下面的Simulation按照如图设置

5、选择上面的Compile test bench点击后面Test Bench进入选择,点击New新建

按照顺序先编辑名字,然后浏览刚才的.vt文件,最后点击Add添加达到如下图效果,再点击OK,这里如果浏览的是.vo文件则后面仿真需要自己设置时间及电平,然后一直点击OK回到代码编辑界面

 来到主页点击:自动跳转至Modelsim,选择下面的Wave即可看到波形。

 

1.4 FPGA烧录实现

 配置确认引脚,烧录程序(可参考之前的FPGA文件,有详细记录每一步操作,这里就给出关键操作截图)

 烧录效果:

【博客调用】状态机思想实习流水灯

二、CPLD和FPGA芯片

CPLD(Complex Programmable Logic Device,复杂可编程逻辑器件)和FPGA(Field-Programmable Gate Array,现场可编程门阵列)是可编程逻辑器件的两大分支,它们在结构、性能和应用场景上有显著差异。

1. 核心结构与技术原理

特性 CPLD FPGA
逻辑单元 基于**乘积项(Product-Term)**结构,包含与阵列和或阵列,逻辑块较大且固定。 基于**查找表(LUT)**结构,每个LUT(如4/6输入)模拟任意组合逻辑,逻辑块细粒度。
互连资源 全局总线式互连,延迟固定且确定性高 分布式可编程互连,延迟可变但灵活性高
存储技术 非易失性(如Flash/EEPROM),配置掉电保留。 易失性(SRAM为主),需外部配置芯片(如Flash)存储代码。
触发器数量 较少,适合组合逻辑为主的设计。 大量触发器,支持复杂时序逻辑。

2. 性能与容量

指标 CPLD FPGA
逻辑规模 较小(数百至数千逻辑门) 大(数万至数百万逻辑门)
运行速度 高(固定延迟,适合高速控制) 中高(依赖布线优化,适合并行计算)
功耗 低(静态功耗为主) 中高(动态功耗显著,尤其高频场景)
配置时间 毫秒级(非易失性,上电即用) 秒级(需从外部加载配置)

3. 适用场景

场景 CPLD FPGA
简单控制逻辑 电源管理、接口转换、总线桥接。 不适用(资源浪费)。
复杂算法处理 不适用(容量不足)。 图像处理、通信协议(如5G)、AI推理。
确定性时序需求 高(如电机控制、工业PLC)。 低(时序受布线影响)。
原型验证 不适用。 高(可快速迭代设计)

4. 选型建议

  • 选择CPLD

    • 需要非易失性存储,上电即用。

    • 设计简单(如组合逻辑、小型状态机)。

    • 对功耗敏感(电池供电设备)。

  • 选择FPGA

    • 处理复杂算法(如FFT、卷积运算)。

    • 需要动态重构(如通信协议切换)。

    • 高并行性需求(如数据中心加速)。

三、HDLbitsFPGA练习记录(combinational logic)

练习点击这里:HDLBitshttps://hdlbits.01xz.net/wiki/Main_Page

 实验记录:

练习一:7420chip

7400 系列集成电路是一系列每个都包含几个门电路的数字芯片。7420 是一种包含两个 4 输入 NAND 门的芯片。
创建一个具有与 7420 芯片相同功能的模块。该模块有 8 个输入和 2 个输出。

分析:

  • 每个 4 输入的 NAND 门的逻辑功能是:当所有输入都为 1 时,输出为 0;否则,输出为 1。
  • 每个 4 输入 NAND 门的输入是4个输入信号,输出是 1 个信号。

代码:

module top_module ( 
    input p1a, p1b, p1c, p1d,
    output p1y,
    input p2a, p2b, p2c, p2d,
    output p2y );
	assign p1y=~(p1a&p1b&p1c&p1d);
    assign p2y=~(p2a&p2b&p2c&p2d);
endmodule

 仿真电路图;

练习二:电路描述

电路 B 可以通过以下模拟波形来描述:

代码:

module top_module ( input x, input y, output z );
	assign z = ~(x ^ y);
endmodule

仿真图:

 练习三、多路选择器1-9

设计一个 16 位宽、9 对 1 的多路复用器。当 sel=0 时选择 a,当 sel=1 时选择 b,以此类推。对于未使用的模式(sel=9 到 15),将所有输出位都设置为 '1'。

代码如下:

module top_module (
    input [15:0] a,
    input [15:0] b,
    input [15:0] c,
    input [15:0] d,
    input [15:0] e,
    input [15:0] f,
    input [15:0] g,
    input [15:0] h,
    input [15:0] i,
    input [3:0] sel,
    output logic [15:0] out
);

    // Case语句只能用在过程性块(always块)中
    // 这是一个组合逻辑电路,所以使用组合逻辑的always @(*)块。
    always @(*) begin
        out = '1;    // '1 是一种特殊的字面量语法,表示所有位都为1的数。
                     // '0、'x 和 'z 也是有效的。
                     // 给 'out' 赋一个默认值,
        case (sel)
            4'h0: out = a;
            4'h1: out = b;
            4'h2: out = c;
            4'h3: out = d;
            4'h4: out = e;
            4'h5: out = f;
            4'h6: out = g;
            4'h7: out = h;
            4'h8: out = i;
        endcase
    end
    
endmodule

 仿真波形图:

 

练习四:Full adder

创建一个全加器。全加器接收三个位(包括进位输入)并输出和以及进输出。位

预期的解决方案长度:约 2 行。

代码:

module top_module (
    input a,         // 输入位a
    input b,         // 输入位b
    input cin,       // 进位输入
    output sum,      // 和输出
    output cout      // 进位输出
);

    assign {cout, sum} = a + b + cin;  // 使用加法运算直接计算和与进位

endmodule

代码解释

  1. 模块接口

    • input ainput b:两个加数位。

    • input cin:进位输入(来自低位的进位)。

    • output sum:加法结果的和。

    • output cout:进位输出(用于更高位的加法)。

  2. 逻辑实现

    • 使用 assign 语句实现组合逻辑。

    • a + b + cin:直接对三个输入位进行加法运算。

    • {cout, sum}:将加法结果的高位赋值给 cout(进位输出),低位赋值给 sum(和输出)。

 练习五、Bcdadd4

您将获得一个名为 bcd_fadd 的 BCD(二进制编码十进制)一位数加法器,它能将两个 BCD 数字以及进位输入相加,并生成和以及进位输出。

module bcd_fadd (
    input [3:0] a,
    input [3:0] b,
    input     cin,
    output   cout,
    output [3:0] sum );

实例化 4 个 bcd_fadd 模块以创建一个 4 位 BCD 溪流进位加法器。您的加法器应将两个 4 位 BCD 数字(打包成 16 位向量)和一个进位输入相加,以产生一个 4 位和进位输出。

代码:

module top_module (
    input [15:0] a, b,  // 输入的两个 4 位 BCD 数字,打包成 16 位向量
    input cin,         // 进位输入
    output cout,       // 进位输出
    output [15:0] sum  // 4 位 BCD 和,打包成 16 位向量
);

    // 中间进位信号,用于连接各个 BCD 加法器
    wire c1, c2, c3;  

    // 实例化 4 个 bcd_fadd 模块
    bcd_fadd fa0 (
        .a(a[3:0]),      // 第一个 BCD 数字的最低位
        .b(b[3:0]),      // 第二个 BCD 数字的最低位
        .cin(cin),       // 进位输入
        .cout(c1),       // 输出到下一个加法器的进位
        .sum(sum[3:0])   // 第一个 BCD 位的和
    );

    bcd_fadd fa1 (
        .a(a[7:4]),      // 第一个 BCD 数字的次低位
        .b(b[7:4]),      // 第二个 BCD 数字的次低位
        .cin(c1),        // 从上一个加法器传来的进位
        .cout(c2),       // 输出到下一个加法器的进位
        .sum(sum[7:4])   // 第二个 BCD 位的和
    );

    bcd_fadd fa2 (
        .a(a[11:8]),     // 第一个 BCD 数字的次高位
        .b(b[11:8]),     // 第二个 BCD 数字的次高位
        .cin(c2),        // 从上一个加法器传来的进位
        .cout(c3),       // 输出到下一个加法器的进位
        .sum(sum[11:8])  // 第三个 BCD 位的和
    );

    bcd_fadd fa3 (
        .a(a[15:12]),    // 第一个 BCD 数字的最高位
        .b(b[15:12]),    // 第二个 BCD 数字的最高位
        .cin(c3),        // 从上一个加法器传来的进位
        .cout(cout),     // 最终的进位输出
        .sum(sum[15:12]) // 第四个 BCD 位的和
    );

endmodule

 练习结果仿真图:

 

四、总结

本次实验完成了,收获颇丰,进一步掌握状态机思想,又一次复习了仿真操作,对FPGA的掌握更上一层楼。还学会了使用HDLbits 用来模拟仿真波形图,很是方便。在这个在线网站上也可以自己做题,理解底层电路设计的更多知识,收获很大,这是本人实验时候的一个记录过程,希望与大家共勉!

另外,本人才疏学浅,要是有错误的地方,还望大家海涵,感谢您的阅读!

参考文献:

什么是状态机(Finite-state machine)?-CSDN博客

FPGA学习(四)——状态机重写LED流水灯并仿真-CSDN博客

Quartus使用步骤及联合Modelsim仿真教程_quartus仿真-CSDN博客

HDLbits刷题中文完整版,按照刷题网站顺序每日更新一道_hdlbits网站中文版-CSDN博客


网站公告

今日签到

点亮在社区的每一天
去签到