DE2-115开发板LED流水灯Verilog设计

一、实验环境搭建

  1. 安装并打开VSCode,在扩展商店中搜索并安装Verilog-HDL/SystemVerilog插件,实现语法高亮和自动补全功能。
    vcverilog
  2. 下载并打开Quartus 18,按顺序点击Tools->Options->Preferred Text Editor进入偏好文本编辑器,在Text editor中选择自定义文本编辑器(Custom),并在Command-line中选择VSCode的Code.exe程序所在文件位置,将VSCode与Quartus进行绑定。
    在这里插入图片描述
  3. 新建led_floa项目以开发流水灯项目,选择合适的设备和仿真软件,例如EP4CE115F29C7设备和ModelSim-ALtera(Verilog HDL)仿真软件。

在这里插入图片描述

在VSCode中的项目文件夹led_floa中创建以下文件:

  • LedBlink.v(顶层模块)
  • fenpin.v(分频模块)
  • display.v(显示模块)

在这里插入图片描述

二、设计思路

采用层次化设计,将系统分为三个模块:

  1. 顶层模块(LedBlink.v):负责实例化和连接其他模块
  2. 分频模块(fenpin.v):将输入时钟分频到合适频率
  3. 显示模块(display.v):控制LED的流水灯效果

三、代码实现

代码如下:
LedBlink

module LedBlink(
    input   clk,//50Mhz
    input   rst_n,//复位信号
    input   pause_key,
    output [5:0] led
);

//模块间连接信号
wire en_ls;
wire pause_reg;//暂停状态信号

fenpin u_fenpin(
    .clk    (clk),
    .rst_n  (rst_n),
    .en     (en_ls)
);

display u_display(
        .clk    (clk),
    .rst_n  (rst_n),
    .en_ls  (en_ls),
    .pause_key  (pause_key),
    .pause_reg  (pause_reg),
    .led  (led)
);

endmodule

在这里插入图片描述

代码说明:负责将分频模块和显示模块连接起来,并处理复位信号。

fenpin.v

module fenpin(
    input   clk,
    input   rst_n,
    output  reg en
);

reg [22:0] cnt;

always @(posedge clk or negedge rst_n) begin
    if(!rst_n)begin
        cnt<=23'd0;
        en<=1'b0;
    end else begin
        if(cnt==23'd8_333_332) begin
            cnt<=23'd0;
            en<=1'b1;
        end else begin
            cnt<=cnt+1'b1;
            en<=1'b0;
        end
    end
end

endmodule

在这里插入图片描述

代码说明:通过计数器实现时钟分频,用于控制流水灯的速度。

display.v

module display(
    input   clk,
    input   rst_n,
    input   en_ls,
    input   pause_key,
    output reg pause_reg,
    output reg [5:0] led
);

reg [1:0] sync_key;
reg  key_delay;
reg [19:0] debounce_cnt;
reg key_pressed; 

// 按键消抖逻辑
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        sync_key <= 2'b11;
        key_delay <= 1'b1;
        debounce_cnt <= 20'd0;
        key_pressed <= 1'b0;
    end  else begin
        sync_key <= {sync_key[0], pause_key};
        key_delay <= sync_key[1];
        if(sync_key[1] != key_delay)
            debounce_cnt <= 20'd999_999;
        else if (debounce_cnt != 0)
            debounce_cnt <= debounce_cnt - 1;
        if(debounce_cnt == 0 && sync_key[1] == 1'b0)
            key_pressed <= 1'b1;
        if(debounce_cnt == 0 && sync_key[1] == 1'b1)
            key_pressed <= 1'b0;
    end
end

reg key_release_flag;  // 记录按键释放事件
always @(posedge  clk or negedge rst_n) begin
    if(!rst_n) begin
        pause_reg <= 1'b0;
        key_release_flag <= 1'b0;
    end else begin
        // 当按键从按下状态变为释放状态时,翻转暂停状态
        if(key_pressed == 1'b1 && sync_key[1] == 1'b1)
            key_release_flag <= 1'b1;
        else
            key_release_flag <= 1'b0;
        
        if(key_release_flag)
            pause_reg <= ~pause_reg;
    end
end

// LED闪烁控制逻辑
always @(posedge  clk or negedge rst_n) begin
    if(!rst_n) begin
        led <= 6'b000001;
    end else if(en_ls && !pause_reg) begin
        led <= {led[0], led[5:1]};
    end
end

endmodule

在这里插入图片描述

代码说明:采用状态机设计,定义6个状态分别对应6个LED点亮的位置,状态按顺序循环切换,实现流水灯效果。

  1. 保存全部项目后,在Quartus中点击Files->...->选中.v文件,即可添加刚才在VSCode中保存的.v文件。
    在这里插入图片描述

在这里插入图片描述

  1. 按下Ctrl+K开始分析与综合,检查语法,编译信息没有红色,表示编译通过。如果出现undefined类错误,将LedBlink.v设置为顶层即可。
    在这里插入图片描述
  2. 编译通过后没问题了,便可开始分配管脚。

在这里插入图片描述

  1. 按下Ctrl+L开始编译,准备进行烧录程序,编译通过即可开始烧录。
    在这里插入图片描述
  2. 开始烧录程序

在这里插入图片描述

四、收获总结

通过LED流水灯实验,我学会了用Verilog做分频和状态机控制,还掌握了模块化设计和规范写代码的方法。以前觉得抽象的数字电路知识,现在通过实际操作,终于明白了怎么在FPGA里实现。动手能力也提升了不少,能更灵活地运用到以后的项目里。而且在调试过程中,也锻炼了自己解决问题的能力,遇到错误不再慌张,知道如何去分析和修正。这次实验让我对数字系统设计有了更深入的理解,也增强了我继续学习和探索的信心。

五、参考资料

【FPGA实战】Verilog实现DE2-115的流水灯控制
Quartus联合Vscode开发教程

// // Permission: // // Terasic grants permission to use and modify this code for use // in synthesis for all Terasic Development Boards and Altera Development // Kits made by Terasic. Other use of this code, including the selling // ,duplication, or modification of any portion is strictly prohibited. // // Disclaimer: // // This VHDL/Verilog or C/C++ source code is intended as a design reference // which illustrates how these types of functions can be implemented. // It is the user's responsibility to verify their design for // consistency and functionality through the use of formal // verification methods. Terasic provides no warranty regarding the use // or functionality of this code. // // -------------------------------------------------------------------- // // Terasic Technologies Inc // 356 Fu-Shin E. Rd Sec. 1. JhuBei City, // HsinChu County, Taiwan // 302 // // web: http://www.terasic.com/ // email: [email protected] // // -------------------------------------------------------------------- // // Major Functions: DE2_115_PS2 Mouse Controller // // -------------------------------------------------------------------- // // Revision History : // -------------------------------------------------------------------- // Ver :| Author :| Mod. Date :| Changes Made: // V1.0 :| Johnny FAN,HdHuang :| 05/16/10 :| Initial Revision // -------------------------------------------------------------------- module ps2( iSTART, //press the button for transmitting instrucions to device; iRST_n, //FSM reset signal; iCLK_50, //clock source; PS2_CLK, //ps2_clock signal inout; PS2_DAT, //ps2_data signal inout; oLEFBUT, //left button press display; oRIGBUT, //right button press display; oMIDBUT, //middle button press display; oX_MOV1, //lower SEG of mouse displacement display for X axis. oX_MOV2, //higher SEG of mouse displacement display for X axis. oY_MOV1, //lower SEG of mouse displacement display for Y axis. oY_MOV2 //higher SEG of mouse displacement display for Y axis. ); //interface; //======================================================= // PORT declarations //======================================================= input iSTART; input iRST_n; input iCLK_50; inout PS2_CLK; inout PS2_DAT; output oLEFBUT; output oRIGBUT; output oMIDBUT; output [6:0] oX_MOV1; output [6:0] oX_MOV2; output [6:0] oY_MOV1; output [6:0] oY_MOV2; //instantiation SEG7_LUT U1(.oSEG(oX_MOV1),.iDIG(x_latch[3:0])); SEG7_LUT U2(.oSEG(oX_MOV2),.iDIG(x_latch[7:4])); SEG7_LUT U3(.oSEG(oY_MOV1),.iDIG(y_latch[3:0])); SEG7_LUT U4(.oSEG(oY_MOV2),.iDIG(y_latch[7:4])); //instruction define, users can charge the instruction byte here for other purpose according to ps/2 mouse datasheet. //the MSB is of parity check bit, that's when there are odd number of 1's with data bits, it's value is '0',otherwise it's '1' instead. parameter enable_byte =9'b011110100; //======================================================= // REG/WIRE declarations //======================================================= reg [1:0] cur_state,nex_state; reg ce,de; reg [3:0] byte_cnt,delay; reg [5:0] ct; reg [7:0] x_latch,y_latch,cnt; reg [8:0] clk_div; reg [9:0] dout_reg; reg [32:0] shift_reg; reg leflatch,riglatch,midlatch; reg ps2_clk_in,ps2_clk_syn1,ps2_dat_in,ps2_dat_syn1; wire clk,ps2_dat_syn0,ps2_clk_syn0,ps2_dat_out,ps2_clk_out,flag; //======================================================= // PARAMETER declarations //======================================================= //state define parameter listen =2'b00, pullclk=2'b01, pulldat=2'b10, trans =2'b11; //======================================================= // Structural coding //======================================================= //clk division, derive a 97.65625KHz clock from the 50MHz source; always@(posedge iCLK_50) begin clk_div <= clk_div+1; end assign clk = clk_div[8]; //tristate output control for PS2_DAT and PS2_CLK; assign PS2_CLK = ce?ps2_clk_out:1'bZ; assign PS2_DAT = de?ps2_dat_out:1'bZ; assign ps2_clk_out = 1'b0; assign ps2_dat_out = dout_reg[0]; assign ps2_clk_syn0 = ce?1'b1:PS2_CLK; assign ps2_dat_syn0 = de?1'b1:PS2_DAT; // assign oLEFBUT = leflatch; assign oRIGBUT = riglatch; assign oMIDBUT = midlatch; //multi-clock region simple synchronization always@(posedge clk) begin ps2_clk_syn1 <= ps2_clk_syn0; ps2_clk_in <= ps2_clk_syn1; ps2_dat_syn1 <= ps2_dat_syn0; ps2_dat_in <= ps2_dat_syn1; end //FSM shift always@(*) begin case(cur_state) listen :begin if ((!iSTART) && (cnt == 8'b11111111)) nex_state = pullclk; else nex_state = listen; ce = 1'b0; de = 1'b0; end pullclk :begin if (delay == 4'b1100) nex_state = pulldat; else nex_state = pullclk; ce = 1'b1; de = 1'b0; end pulldat :begin nex_state = trans; ce = 1'b1; de = 1'b1; end trans :begin if (byte_cnt == 4'b1010) nex_state = listen; else nex_state = trans; ce = 1'b0; de = 1'b1; end default : nex_state = listen; endcase end //idle counter always@(posedge clk) begin if ({ps2_clk_in,ps2_dat_in} == 2'b11) begin cnt <= cnt+1; end else begin cnt <= 8'd0; end end //periodically reset ct; ct counts the received data length; assign flag = (cnt == 8'hff)?1:0; always@(posedge ps2_clk_in,posedge flag) begin if (flag) ct <= 6'b000000; else ct <= ct+1; end //latch data from shift_reg;outputs is of 2's complement; //Please treat the cnt value here with caution, otherwise wrong data will be latched. always@(posedge clk,negedge iRST_n) begin if (!iRST_n) begin leflatch <= 1'b0; riglatch <= 1'b0; midlatch <= 1'b0; x_latch <= 8'd0; y_latch <= 8'd0; end else if (cnt == 8'b00011110 && (ct[5] == 1'b1 || ct[4] == 1'b1)) begin leflatch <= shift_reg[1]; riglatch <= shift_reg[2]; midlatch <= shift_reg[3]; x_latch <= x_latch+shift_reg[19 : 12]; y_latch <= y_latch+shift_reg[30 : 23]; end end //pull ps2_clk low for 100us before transmit starts; always@(posedge clk) begin if (cur_state == pullclk) delay <= delay+1; else delay <= 4'b0000; end //transmit data to ps2 device;eg. 0xF4 always@(negedge ps2_clk_in) begin if (cur_state == trans) dout_reg <= {1'b0,dout_reg[9:1]}; else dout_reg <= {enable_byte,1'b0}; end //transmit byte length counter always@(negedge ps2_clk_in) begin if (cur_state == trans) byte_cnt <= byte_cnt+1; else byte_cnt <= 4'b0000; end //receive data from ps2 device; always@(negedge ps2_clk_in) begin if (cur_state == listen) shift_reg <= {ps2_dat_in,shift_reg[32:1]}; end //FSM movement always@(posedge clk,negedge iRST_n) begin if (!iRST_n) cur_state <= listen; else cur_state <= nex_state; end endmodule
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一大Cpp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值