全球排行榜123网,关注世界各行业排行榜前十名,国内全行业十大品牌2023年最新排名!

小梅哥的FPGA,小梅哥的adv7180(附2023年最新排行榜前十名单)

2023-07-28 08:25:20 全球排行榜123网 品牌

 

小梅哥的FPGA,小梅哥的adv7180(附2023年最新排行榜前十名单):大家好!今天让小编来大家介绍下关于小梅哥的FPGA的问题,以下是酷知号的小编对此问题的归纳整理,让我们一起来看看吧。本文基于小梅哥的FPGA学习所得。 推荐一个更加完善的笔记00.……全球排行榜123网www.vai8.com)小编为你整理了本篇文章,希望能解对你有所帮助!

 

大家好!今天让小编来大家介绍下关于小梅哥的FPGA的问题,以下是酷知号的小编对此问题的归纳整理,让我们一起来看看吧。

小梅哥的FPGA

本文基于小梅哥的FPGA学习所得。 推荐一个更加完善的笔记

00.FPGA基本单元

可编程逻辑块 可编程IO块 可编程内部互联

PLL、DLL 块RAM存储器 数学运算单元 高速串行I/O PCIE、DDR等硬核控制器 嵌入式处理器硬核()

01.科学FPGA开发流程

  • 设计定义
  • 设计输入
  • 分析和综合(Quartus初学)
  • 功能仿真(modelsim-altera)
  • 设计约束
  • 布局布线
  • 时序仿真(modelsim-altera)
  • IO分配以及配置文件的生成
  • 配置(烧结FPGA)
  • 在线调试(…)
  • 设计定义: 二选一多路器 两个输入IO,可以是高电平,也可以是低电平, 输入按键按下时,LED灯与a端口保持一致。 输入按键释放时,LED灯与b端口状态保持一致。

    led_test.v

    module led_test(a,b,key_in,led_out);input a;//输入端口Ainput b;//输入端口Binput key_in;//按键输入,实现输入通道的选择output led_out;//led控制端口//当key——in == 0;led_out = a;assign led_out = (key_in == 0) ? a : b; endmodule
    • 编译快捷键ctrl+k
    • 模块名和信号名不要有冲突

    led_test_tb.v

    `timescale 1ns/1ps module led_test_tb;//激励信号定义,对应连接到待测模块的输入端口reg signal_a;reg signal_b;reg signal_c;//待检测信号定义,对应连接到待测模块的输出端口wire led;//例化待测试模块led_test led_test0(.a(signal_a),.b(signal_b),.key_in(signal_c),.led_out(led));//产生激励initial beginsignal_a = 0;signal_b = 0;signal_c = 0;#100//延时100nssignal_a = 0;signal_b = 0;signal_c = 1;#100signal_a = 0;signal_b = 1;signal_c = 0;#100signal_a = 0;signal_b = 1;signal_c = 1;#100signal_a = 1;signal_b = 0;signal_c = 0;#100signal_a = 1;signal_b = 0;signal_c = 1;#100signal_a = 1;signal_b = 1;signal_c = 0;#100signal_a = 1;signal_b = 1;signal_c = 1;#200$stop;end endmodule

    功能仿真(前仿真)——RTL Simulation

    门级仿真(时序仿真、后仿真)——Gate Level Simulation

    与前仿真相比,存在延迟和脉冲

    管脚分配pin planner

    02.3-8译码器设计与验证

    A B C out
    0 0 0 0000_0001
    0 0 1 0000_0010
    0 1 0 0000_0100
    0 1 1 0000_1000
    1 0 0 0001_0000
    1 0 1 0010_0000
    1 1 0 0100_0000
    1 1 1 1000_0000

    my3_8.v

    module my3_8(a,b,c,out);input a;//输入端口Ainput b;//输入端口Binput c;//输入端口Coutput [7:0] out;//输出端口reg [7:0] out;//always块中生成的必须用reg型always@(a,b,c)begincase({a,b,c})3'b000:out = 8'b0000_0001;3'b001:out = 8'b0000_0010;3'b010:out = 8'b0000_0100;3'b011:out = 8'b0000_1000;3'b100:out = 8'b0001_0000;3'b100:out = 8'b0010_0000;3'b110:out = 8'b0100_0000;3'b111:out = 8'b1000_0000;//default: out = 8'b1000_0000;endcaseend endmodule

    my3_8_tb.v

    `timescale 1ns/1ps module my3_8_tb;reg a;reg b;reg c;wire [7:0] out;my3_8 u1(.a(a),.b(b),.c(c),.out(out));initial begina = 0;b = 0;c = 0;#200a = 0;b = 0;c = 1;#200a = 0;b = 1;c = 0;#200a = 0;b = 1;c = 1;#200a = 1;b = 0;c = 0;#200a = 1;b = 0;c = 1;#200a = 1;b = 1;c = 0;#200a = 1;b = 1;c = 1;#200$stop;end endmodule

    功能仿真(前仿真)

    门级仿真(时序仿真、后仿真)

    • 上面标记的地方有下面这种情况(竞争冒险?):

      错误:modelsim只支持打开一次,如果未关闭打开则会报错如下:

    02. 4-16译码器(对比3-8译码器练习)

    A B C D out
    0 0 0 0 0000_0000_0000_0001
    0 0 0 1 0000_0000_0000_0010
    0 0 1 0 0000_0000_0000_0100
    0 0 1 1 0000_0000_0000_1000
    0 1 0 0 0000_0000_0001_0000
    0 1 0 1 0000_0000_0010_0000
    0 1 1 0 0000_0000_0100_0000
    0 1 1 1 0000_0000_1000_0000
    1 0 0 0 0000_0001_0000_0000
    1 0 0 1 0000_0010_0000_0000
    1 0 1 0 0000_0100_0000_0000
    1 0 1 1 0000_1000_0000_0000
    1 1 0 0 0001_0000_0000_0000
    1 1 0 1 0010_0000_0000_0000
    1 1 1 0 0100_0000_0000_0000
    1 1 1 1 1000_0000_0000_0000

    my4_16.v

    module my4_16(a,b,c,d,out);input a;input b;input c;input d;output [15:0]out;reg [15:0]out;always@(a,b,c,d)begincase({a,b,c,d}) 4'b0000 :out = 16'b0000_0000_0000_0001;4'b0001 :out = 16'b0000_0000_0000_0010;4'b0010 :out = 16'b0000_0000_0000_0100;4'b0011 :out = 16'b0000_0000_0000_1000;4'b0100 :out = 16'b0000_0000_0001_0000;4'b0101 :out = 16'b0000_0000_0010_0000;4'b0110 :out = 16'b0000_0000_0100_0000;4'b0111 :out = 16'b0000_0000_1000_0000;4'b1000 :out = 16'b0000_0001_0000_0000;4'b1001 :out = 16'b0000_0010_0000_0000;4'b1010 :out = 16'b0000_0100_0000_0000;4'b1011 :out = 16'b0000_1000_0000_0000;4'b1100 :out = 16'b0001_0000_0000_0000;4'b1101 :out = 16'b0010_0000_0000_0000;4'b1110 :out = 16'b0100_0000_0000_0000;4'b1111 :out = 16'b1000_0000_0000_0000;endcaseend endmodule

    my4_16_tb.v

    `timescale 1ns/1psmodule my4_16_tb;reg a;reg b;reg c;reg d;wire [15:0] out;my4_16 my4_16_inst(.a (a ),.b (b ),.c (c ),.d (d ),.out (out));initial begina = 0;b = 0;c = 0;d = 0;#200a = 0;b = 0;c = 0;d = 1;#200a = 0;b = 0;c = 1;d = 0;#200a = 0;b = 0;c = 1;d = 1;#200a = 0;b = 1;c = 0;d = 0;#200a = 0;b = 1;c = 0;d = 1;#200a = 0;b = 1;c = 1;d = 0;#200a = 0;b = 1;c = 1;d = 1;#200a = 1;b = 0;c = 0;d = 0;#200a = 1;b = 0;c = 0;d = 1;#200a = 1;b = 0;c = 1;d = 0;#200a = 1;b = 0;c = 1;d = 1;#200a = 1;b = 0;c = 0;d = 0;#200a = 1;b = 0;c = 0;d = 1;#200a = 1;b = 0;c = 1;d = 0;#200a = 1;b = 0;c = 1;d = 1;#200$stop;end endmodule
    • 在仿真中,任何信号都要赋给初始值

    前仿真(功能仿真)

    后仿真

    • 后仿真和之前一样,也存在延迟和上电的高阻。

    语法乱记

    模块调用

    综合与不可综合

    task中只要没有不可综合语句(如延时#),就可以综合! initial也未必,

    时序逻辑:

    有时钟和存储能力(reg),计数器是最简单的时序逻辑(时序逻辑中也需要组合逻辑的配合才有意义)。

    03.计数器

    LED,每500ms,状态翻转一次, 系统时钟为50M,对应周期为20ns 500ms = 500_000_000ns/20 = 25_000_000;

    电路原理图

    counter.v

    module counter(Clk50M,Rst_n,led);input Clk50M;//系统时钟,50Minput Rst_n;//全局复位,低电平复位output reg led;//led输出,always块中的变量都要声明为reg型reg [24:0] cnt;//定义计数器寄存器//计数器计数进程 always@(posedge Clk50M or negedge Rst_n)beginif(Rst_n == 1'b0)cnt <= 25'd0;//else if(cnt == 25'd24_999_999)else if(cnt == 25'd24_999)cnt <= 25'd0;elsecnt <= cnt + 1'b1;end//led输出控制进程always@(posedge Clk50M or negedge Rst_n)beginif(Rst_n == 1'b0)led <= 1'b1;//else if(cnt == 25'd24_999_999) else if(cnt == 25'd24_999)led <= ~led;else led <= led;endendmodule

    couter_tb.v

    `timescale 1ns/1ps `define clock_period 20module counter_tb;reg clk;reg rst_n;wire led;counter counter0(.Clk50M(clk),.Rst_n(rst_n),.led(led));initial clk = 1;always #(`clock_period/2) clk = ~clk;initial beginrst_n = 1'b0;#(`clock_period *200);rst_n = 1'b1;#2000000000;$stop;endendmodule

    前仿真(功能仿真)

    后仿真(时序仿真、门级仿真)

    IO管脚分配

    04.计数器IP核调用与验证

    四位计数器 (Quartus II 提供的LPM_counter IP核的使用)

    • FPGA设计方式: 原理图(不推荐) Verilog HDL设计方式 IP核输入方式

    调用步骤

    counter.v

    `timescale 1 ps / 1 ps // synopsys translate_on module counter (cin,clock,cout,q);input cin;input clock;output cout;output [3:0] q;wire sub_wire0;wire [3:0] sub_wire1;wire cout = sub_wire0;wire [3:0] q = sub_wire1[3:0];lpm_counter LPM_COUNTER_component (.cin (cin),.clock (clock),.cout (sub_wire0),.q (sub_wire1),.aclr (1'b0),.aload (1'b0),.aset (1'b0),.clk_en (1'b1),.cnt_en (1'b1),.data ({4{1'b0}}),.eq (),.sclr (1'b0),.sload (1'b0),.sset (1'b0),.updown (1'b1));defparamLPM_COUNTER_component.lpm_direction = "UP",LPM_COUNTER_component.lpm_modulus = 10,LPM_COUNTER_component.lpm_port_updown = "PORT_UNUSED",LPM_COUNTER_component.lpm_type = "LPM_COUNTER",LPM_COUNTER_component.lpm_width = 4;endmodule

    tb_counter.v

    `timescale 1ns/1ns`define clock_period 20module counter_tb;reg cin;//进位输入reg clk;//计数基准时钟wire cout;//进位输出wire [3:0] q;counter counter0(.cin(cin),.clock(clk),.cout(cout),.q(q));initial clk = 1;always #(`clock_period/2) clk = ~clk;initial beginrepeat(5)begincin = 0;#(`clock_period *5) cin = 1;#(`clock_period ) cin = 0;end#(`clock_period *200);$stop; endendmodule

    前仿真(功能仿真)

    • 把counter_tb.v中的repeat(5)改为repeat(10),出现如下的仿真结果:(记得选无符号显示)
    • 这个IP核的这个学习小节中,我们开始设置了计数到10,其实我们还可以计数到满(四位计数到4’b1111),下面将重新配置
    • 这里的功能仿真如下:

    八位计数器(四位计数器拼接升级)

    前面的例码,说的是4位的,如果说要8位的,该怎么办?

    一种是:在设计代码中,将位宽设置为8位的;还有一种就是将两个四位的拼接,原理图如下:

    功能仿真

    • 当然,肯定是直接在设计中修改为8位位宽比较方便,如果继续同这节课开始,把计数器设置回10的时候,继续仿真,就会出现如下的效果,引出BCD计数器

    05. BCD计数器设计与使用

    158

    158/100 = 1 158%100 = 5 58/10 = 5

    158%10 = 8

    BCD计数器

    BCD计数器基本模块

    BCD_counter.v

    module BCD_counter(Clk, Cin, Rst_n, Cout, q );input Clk;//计数基准时钟input Cin;//计数器进位输入input Rst_n;//系统复位output reg Cout;//计数器进位输出output [3:0] q;//计数器输出reg [3:0] cnt;//定义计数器寄存器//执行计数过程always@(posedge Clk or negedge Rst_n)if(Rst_n == 1'b0)cnt <= 4'd0;else if(Cin == 1'b1) beginif(cnt == 4'd9)cnt <= 4'd0;else cnt <= cnt + 4'd1;endelsecnt <= cnt;//产生进位输出信号 always@(posedge Clk or negedge Rst_n)if(Rst_n == 1'b0)Cout = 1'b0;else if(Cin == 1'b1 && cnt == 4'd9 ) Cout <= 1'b1;else Cout <= 1'b0;assign q = cnt;endmodule

    BCD_counter_tb.v

    `timescale 1ns/1ns `define clock_period 20 module BCD_counter_tb;reg clk;reg cin;reg rst_n;wire cout;wire [3:0]q;BCD_counter BCD_counter_inst(.Clk(clk),.Cin(cin), .Rst_n(rst_n), .Cout(cout),.q(q) );initial clk = 1'b1;always #(`clock_period/2) clk = ~clk;initial beginrst_n = 1'b0;cin = 1'b0;#(`clock_period *200);rst_n = 1'b1;#(`clock_period *20);repeat(30)begincin = 1'b1;#`clock_period;cin = 1'b0;#(`clock_period *5);end#(`clock_period *20);$stop;endendmodule

    功能仿真

    BCD计数器综合(由模块组成top)

    按照上节课的套路,这次按照12位输出,也就是需要例化三次counter

    BCD_counter_top.v

    module BCD_counter_top(Clk, Cin, Rst_n, Cout, q );input Clk;//计数基准时钟input Cin;//计数器进位输入input Rst_n;//系统复位output Cout;//计数器进位输出output [11:0] q;//计数器输出wire Cout1;wire Cout2;wire [3:0] q0,q1,q2;assign q = {q2,q1,q0};BCD_counter BCD_counter_inst0(.Clk(Clk),.Cin(Cin), .Rst_n(Rst_n), .Cout(Cout1),.q(q0) );BCD_counter BCD_counter_inst1(.Clk(Clk),.Cin(Cout1), .Rst_n(Rst_n), .Cout(Cout2),.q(q1) );BCD_counter BCD_counter_inst2(.Clk(Clk),.Cin(Cout2), .Rst_n(Rst_n), .Cout(Cout),.q(q2) );endmodule

    BCD_counter_top_tb.v

    `timescale 1ns/1ns `define clock_period 20 module BCD_counter_top_tb;reg clk;reg cin;reg rst_n;wire cout;wire [11:0]q;BCD_counter_top BCD_counter_top_inst(.Clk(clk),.Cin(cin), .Rst_n(rst_n), .Cout(cout),.q(q) );initial clk = 1'b1;always #(`clock_period/2) clk = ~clk;initial beginrst_n = 1'b0;cin = 1'b0;#(`clock_period *200);rst_n = 1'b1;#(`clock_period *20);cin = 1'b1;#(`clock_period *5000);$stop;endendmodule

    功能仿真(发现错误)

    修改后的BCD_counter.v

    module BCD_counter(Clk, Cin, Rst_n, Cout, q );input Clk;//计数基准时钟input Cin;//计数器进位输入input Rst_n;//系统复位output Cout;//计数器进位输出output [3:0] q;//计数器输出reg [3:0] cnt;//定义计数器寄存器//执行计数过程always@(posedge Clk or negedge Rst_n)if(Rst_n == 1'b0)cnt <= 4'd0;else if(Cin == 1'b1) beginif(cnt == 4'd9)cnt <= 4'd0;else cnt <= cnt + 1'b1;endelsecnt <= cnt;//产生进位输出信号 assign Cout = (Cin == 1'b1 && cnt == 4'd9 ); assign q = cnt;endmodule

    修改后的功能仿真

    07.阻塞赋值与非阻塞赋值

    这节课,为了讲述阻塞与非阻塞,感受一下非阻塞与阻塞之间的区别, 将 out <= a+b+c 设计拆分为: d <= a+b; out <= c+d; 当然,实际过程中,就是直接用out = a+b+c即可,不用再引入寄存器增加延迟。

    block_nonblock.v(第一种)

    module block_nonblock(Clk,Rst_n,a,b,c,out);input Clk;input Rst_n;input a;input b;input c;output reg [1:0] out;//out = a + b + c;//d = a + b;//out = d + c;reg [1:0]d;always@(posedge Clk or negedge Rst_n)if(!Rst_n)out = 2'b0;else begind = a + b;out = d + c;endendmodule

    综合之后的电路原理图

    block_nonblock.v(第二种)

    module block_nonblock(Clk,Rst_n,a,b,c,out);input Clk;input Rst_n;input a;input b;input c;output reg [1:0] out;//out = a + b + c;//d = a + b;//out = d + c;reg [1:0]d;always@(posedge Clk or negedge Rst_n)if(!Rst_n)out = 2'b0;else beginout = d + c;d = a + b;endendmodule

    综合之后的电路原理图(第二种)

    block_nonblock.v(第三种)

    module block_nonblock(Clk,Rst_n,a,b,c,out);input Clk;input Rst_n;input a;input b;input c;output reg [1:0] out;//out = a + b + c;//d = a + b;//out = d + c;reg [1:0]d;always@(posedge Clk or negedge Rst_n)if(!Rst_n)out <= 2'b0;else beginout <= d + c;d <= a + b;endendmodule

    综合之后的电路原理图(第三种)

    block_nonblock.v(第四种)

    module block_nonblock(Clk,Rst_n,a,b,c,out);input Clk;input Rst_n;input a;input b;input c;output reg [1:0] out;//out = a + b + c;//d = a + b;//out = d + c;reg [1:0]d;always@(posedge Clk or negedge Rst_n)if(!Rst_n)out <= 2'b0;else begind <= a + b;out <= d + c;endendmodule

    综合之后的电路原理图(第四种)

    测试平台block_nonblock_tb.v

    `timescale 1ns/1ns `define clock_period 20module block_nonblock_tb;reg Clock;reg Rst_n;reg a,b,c;wire [1:0]out;block_nonblock block_nonblock0(Clock,Rst_n,a,b,c,out);initial Clock = 1'b1;always #(`clock_period/2) Clock = ~Clock;initial beginRst_n = 1'b0;a = 0;b = 0;c = 0;#(`clock_period*200 +1);#(`clock_period*200);Rst_n = 1'b1;a = 0;b = 0;c = 0;#(`clock_period*200);a = 0;b = 0;c = 1;#(`clock_period*200);a = 0;b = 1;c = 0;#(`clock_period*200);a = 0;b = 1;c = 1;#(`clock_period*200);a = 1;b = 0;c = 0;#(`clock_period*200);a = 1;b = 0;c = 1;#(`clock_period*200);a = 1;b = 1;c = 0;#(`clock_period*200);a = 1;b = 1;c = 1;#(`clock_period*200);$stop;end endmodule

    第四种的功能仿真

    block_nonblock.v(在第四种的基础上添加了延迟)

    `timescale 1ns/1ns `define tp 1 module block_nonblock(Clk,Rst_n,a,b,c,out);input Clk;input Rst_n;input a;input b;input c;output reg [1:0] out;//out = a + b + c;//d = a + b;//out = d + c;reg [1:0]d;always@(posedge Clk or negedge Rst_n)if(!Rst_n)out <= #`tp 2'b0;else begind <= #`tp a + b;out <= #`tp d + c;endendmodule

    对应的功能仿真

    后仿真(这里去掉延迟,用第四种设计代码)

    从这里可以考虑一下:时序分析常识

    不推荐使用阻塞赋值的原因如下:

    08.状态机的设计思想

    (工作日,闹钟响起)

    • 06:00~09:00 |(起床后的整理以及工作前的准备) 询问是否需要上班——是(工作),不是(其他)
    • 09:00~18:00|(工作) 是否需要加班——需要加班(工作),不需要工作(回家)
    • 18:00~22:00|(下班回家、做饭、吃饭) 是否需要休息——是(休息),不是(其他)
    • 22:00~06:00|(睡觉)

    序列检测的一个例子

    Hello.v

    module Hello(Clk,Rst_n,data,led);input Clk;//50Minput Rst_n;//input [7:0]data;output reg led;localparam CHECk_H = 5'b0_0001,CHECk_e = 5'b0_0010,CHECk_la = 5'b0_0100,CHECk_lb = 5'b0_1000,CHECk_o = 5'b1_0000;reg [4:0] state;//一段式状态机、两段式状态机、三段式状态机always@(posedge Clk or negedge Rst_n)if(!Rst_n)beginled <= 1'b1;state <= CHECk_H;endelse begincase(state)CHECk_H:if(data == "H")//这是可综合的state <= CHECk_e;elsestate <= CHECk_H;CHECk_e:if(data == "e")state <= CHECk_la;elsestate <= CHECk_H;CHECk_la:if(data == "l")state <= CHECk_lb;else state <= CHECk_H;CHECk_lb:if(data == "l")state <= CHECk_o;elsestate <= CHECk_H;CHECk_o:beginstate <= CHECk_H;if(data == "o")led <= ~led;else led <=led; enddefault:state <= CHECk_H;endcase end endmodule

    Hello_tb.v

    `timescale 1ns/1ns `define clock_period 20 module Hello_tb;reg Clk;reg Rst_n;reg [7:0]ASCII;wire led;Hello Hello(.Clk(Clk),.Rst_n(Rst_n),.data(ASCII),.led(led));initial Clk = 1'b1;always #(`clock_period/2) Clk = ~Clk;initial beginRst_n = 1'b0;#(`clock_period*200);Rst_n = 1;#(`clock_period*200);forever beginASCII = "I";#(`clock_period);ASCII = "A";#(`clock_period);ASCII = "M";#(`clock_period);ASCII = "X";#(`clock_period);ASCII = "i";#(`clock_period);ASCII = "a";#(`clock_period);ASCII = "o";#(`clock_period);ASCII = "M";#(`clock_period);ASCII = "e";#(`clock_period);ASCII = "i";#(`clock_period);ASCII = "g";#(`clock_period);ASCII = "e";#(`clock_period);ASCII = "H";#(`clock_period);ASCII = "E";#(`clock_period);ASCII = "M";#(`clock_period);ASCII = "l";#(`clock_period);ASCII = "H";#(`clock_period);ASCII = "E";#(`clock_period);ASCII = "L";#(`clock_period);ASCII = "L";#(`clock_period);ASCII = "O";#(`clock_period);ASCII = "H";#(`clock_period);ASCII = "e";#(`clock_period);ASCII = "l";#(`clock_period);ASCII = "l";#(`clock_period);ASCII = "o";#(`clock_period);ASCII = "l";endend endmodule

    功能仿真

    门级仿真

    • 对比功能仿真,发现led状态并没有发生立即的翻转,而是在采集到的时钟沿后面一段时间才翻转,这是符合真实电路的。因此建议在功能仿真时,建议在testbench中加上一定的延时,和后仿真贴近。在tb中修改成如下的格式:

    Rst_n = 1; #(`clock_period*200 + 1);

    添加延迟后的功能仿真

    09.A按键消抖模式设计与验证

    独立按键消抖实验

    课程目标:复习状态机的设计思想 实验平台:Snow Dream starter board 核心板

    实验现象:每次按下键0,4个LED显示状态以二进制加法格式加1,每次按下按键1,4个LED显示状态以二进制加法格式减1

    独立按键的物理模型

    知识点:

  • testbench 中随机数发生函数$random的使用;
  • 仿真模型的概念;
  • 未按下时空闲状态(IDLE) 按下抖动滤除状态(FILTER) 按下稳定状态(DOWN) 释放抖动滤除状态(FILTER)

    消抖模型

    边缘检测

    key_filter.v

    module key_filter(Clk,Rst_n,key_in,key_flag,key_state);input Clk;input Rst_n;input key_in;output reg key_flag;output reg key_state;localparamIDLE = 4'b0001,FILTER0 = 4'b0010,DOWN = 4'b0100,FILTER1 = 4'b1000;reg [3:0] state;reg [19:0] cnt;reg en_cnt;//使能计数寄存器//50_000_000 20ns//20ms=20_000_000ns ,则需要计数1_000_000即为20msreg key_tmp0,key_tmp1;wire pedge,nedge;reg cnt_full;//计数满表示信号always@(posedge Clk or negedge Rst_n)if(!Rst_n)beginkey_tmp0 <= 1'b0;key_tmp1 <= 1'b0;endelse beginkey_tmp0 <= key_in;key_tmp1 <= key_tmp0;endassign nedge = !key_tmp0 & key_tmp1;assign pedge = key_tmp0 &(!key_tmp1);//assign pedge = key_tmp0 &(~key_tmp1);//~0110 = 1001//!0110 = 0000always@(posedge Clk or negedge Rst_n)if(!Rst_n)beginen_cnt <= 1'b0;state <= IDLE;key_flag <=1'b0;key_state <= 1'b1;endelse begincase(state)IDLE:beginkey_flag <= 1'b0;if(nedge)beginstate <= FILTER0;en_cnt <= 1'b1;endelse state <= IDLE;endFILTER0:beginif(cnt_full)beginkey_flag <=1'b1;key_state <= 1'b0;state <= DOWN;en_cnt <= 1'b0;endelse if(pedge)beginstate <= IDLE;en_cnt <= 1'b0;endelse state <= FILTER0;endDOWN:beginkey_flag <= 1'b0;if(pedge)beginstate <= FILTER1;en_cnt <= 1'b1;endelsestate <= DOWN;endFILTER1:beginif(cnt_full)beginkey_flag <=1'b1;key_state <= 1'b1;state <= IDLE;endelse if(nedge)beginen_cnt <= 1'b0;state <= DOWN;endelsestate <= FILTER1;end default:beginstate <= IDLE;en_cnt <= 1'b0;key_flag <= 1'b0;key_state <= 1'b1;endendcaseendalways@(posedge Clk or negedge Rst_n)if(!Rst_n)cnt <= 20'd0;else if (en_cnt)cnt <= cnt + 1'b1;else cnt <= 20'd0;always@(posedge Clk or negedge Rst_n)if(!Rst_n)cnt_full <= 1'b0;else if (cnt == 999_999)cnt_full <= 1'b1;else cnt_full <= 1'b0; endmodule

    综合之后得到的状态图

    key_filter_tb.v

    `timescale 1ns/1ns `define clk_period 20module key_filter_tb;reg Clk;reg Rst_n;reg key_in;wire key_flag;wire key_state;key_filter key_filter0(.Clk(Clk),.Rst_n(Rst_n),.key_in(key_in),.key_flag(key_flag),.key_state(key_state));initial Clk = 1'b1;always #(`clk_period/2) Clk = ~Clk;initial beginRst_n = 1'b0;key_in = 1'b1;#(`clk_period *10) Rst_n = 1'b1;#(`clk_period *10 +1);//第一次key_in = 0; #1000;key_in = 1; #2000;key_in = 0; #1400;key_in = 1; #2600;key_in = 0; #1300;key_in = 1; #200;key_in = 0; #20000100;#50000100;key_in = 1; #2000;key_in = 0; #1000;key_in = 1; #2000;key_in = 0; #1400;key_in = 1; #2600;key_in = 0; #1300;key_in = 1; #20000100;#50000100;//第二次key_in = 0; #1000;key_in = 1; #2000;key_in = 0; #1400;key_in = 1; #2600;key_in = 0; #1300;key_in = 1; #200;key_in = 0; #20000100;#50000100;key_in = 1; #2000;key_in = 0; #1000;key_in = 1; #2000;key_in = 0; #1400;key_in = 1; #2600;key_in = 0; #1300;key_in = 1; #20000100;#50000100;//第三次key_in = 0; #1000;key_in = 1; #2000;key_in = 0; #1400;key_in = 1; #2600;key_in = 0; #1300;key_in = 1; #200;key_in = 0; #20000100;#50000100;key_in = 1; #2000;key_in = 0; #1000;key_in = 1; #2000;key_in = 0; #1400;key_in = 1; #2600;key_in = 0; #1300;key_in = 1; #20000100;#50000100;//第四次key_in = 0; #1000;key_in = 1; #2000;key_in = 0; #1400;key_in = 1; #2600;key_in = 0; #1300;key_in = 1; #200;key_in = 0; #20000100;#50000100;key_in = 1; #2000;key_in = 0; #1000;key_in = 1; #2000;key_in = 0; #1400;key_in = 1; #2600;key_in = 0; #1300;key_in = 1; #20000100;#50000100; $stop;end endmodule

    功能仿真

    • 向上的箭头:按下抖动
    • 向下的箭头:释放抖动

    通过$random简化测试平台

    随机数发生函数

    `timescale 1ns/1ns `define clk_period 20module key_filter_tb;reg Clk;reg Rst_n;reg key_in;wire key_flag;wire key_state;key_filter key_filter0(.Clk(Clk),.Rst_n(Rst_n),.key_in(key_in),.key_flag(key_flag),.key_state(key_state));initial Clk = 1'b1;always #(`clk_period/2) Clk = ~Clk;initial beginRst_n = 1'b0;key_in = 1'b1;#(`clk_period *10) Rst_n = 1'b1;#(`clk_period *10 +1);press_key;#10000;press_key;#10000;press_key;$stop;endreg [15:0] myrand;task press_key;begin//按下抖动过程repeat(50)beginmyrand = {$random}%65536;//0-65535;#myrand key_in = ~key_in;endkey_in = 0;#50000000;//释放抖动过程repeat(50)beginmyrand = {$random}%65536;//0-65535;#myrand key_in = ~key_in;endkey_in = 1;#50000000;endendtaskendmodule

    功能仿真(随机函数是起作用的)

    仿真模型

    key_model.v

    `timescale 1ns/1nsmodule key_model(key);output reg key;reg [15:0] myrand;initial beginkey = 1'b1;press_key;#10000;press_key;#10000;press_key;$stop;endtask press_key;begin//按下抖动过程repeat(50)beginmyrand = {$random}%65536;//0-65535;#myrand key = ~key;endkey = 0;#50000000;//释放抖动过程repeat(50)beginmyrand = {$random}%65536;//0-65535;#myrand key = ~key;endkey = 1;#50000000;endendtaskendmodule

    key_filter_tb.v

    `timescale 1ns/1ns`define clk_period 20module key_filter_tb;reg Clk;reg Rst_n;wire key_in;wire key_flag;wire key_state;key_filter key_filter0(.Clk(Clk),.Rst_n(Rst_n),.key_in(key_in),.key_flag(key_flag),.key_state(key_state));key_model key_model1(.key(key_in));initial Clk = 1'b1;always #(`clk_period/2) Clk = ~Clk;initial beginRst_n = 1'b0;#(`clk_period *10) Rst_n = 1'b1;#(`clk_period *10 +1);end endmodule

    09.B按键消抖与亚稳态问题引入

    • 异步信号:它与系统没有关系的,由外部产生的
    • 模块化思想
    • 异步信号的同步,使用两级D触发器进行同步

    key_led_top.v

    module key_led_top(Clk,Rst_n,key_in0,key_in1,led);input Clk;input Rst_n;input key_in0;input key_in1;output [3:0] led;wire key_flag0,key_flag1;wire key_state0,key_state1;key_filter key_filter0(.Clk(Clk),.Rst_n(Rst_n),.key_in(key_in0),.key_flag(key_flag0),.key_state(key_state0));key_filter key_filter1(.Clk(Clk),.Rst_n(Rst_n),.key_in(key_in1),.key_flag(key_flag1),.key_state(key_state1));led_ctrl led_ctrl0(.Clk(Clk),.Rst_n(Rst_n),.key_flag0(key_flag0),.key_flag1(key_flag1),.key_state0(key_state0),.key_state1(key_state1),.led(led)); endmodule

    key_filter.v

    module key_filter(Clk,Rst_n,key_in,key_flag,key_state);input Clk;input Rst_n;input key_in;output reg key_flag;output reg key_state;localparamIDLE = 4'b0001,FILTER0 = 4'b0010,DOWN = 4'b0100,FILTER1 = 4'b1000;reg [3:0] state;reg [19:0] cnt;reg en_cnt;//使能计数寄存器//50_000_000 20ns//20ms=20_000_000ns ,则需要计数1_000_000即为20ms//对外部输入的异步信号进行同步处理reg key_in_s0,key_in_s1;always@(posedge Clk or negedge Rst_n)if(!Rst_n)beginkey_in_s0 <= 1'b0;key_in_s1 <= 1'b0;endelse beginkey_in_s0 <= key_in;key_in_s1 <= key_in_s0;endreg key_tmp0,key_tmp1;wire pedge,nedge;reg cnt_full;//计数满表示信号//使用D触发器存储两个相邻时钟上升沿外部输入信号(已经同步到系统时钟域中)的电平状态always@(posedge Clk or negedge Rst_n)if(!Rst_n)beginkey_tmp0 <= 1'b0;key_tmp1 <= 1'b0;endelse beginkey_tmp0 <= key_in_s1;key_tmp1 <= key_tmp0;end//产生跳变沿信号 assign nedge = !key_tmp0 & key_tmp1;assign pedge = key_tmp0 &(!key_tmp1);//assign pedge = key_tmp0 &(~key_tmp1);//~0110 = 1001//!0110 = 0000always@(posedge Clk or negedge Rst_n)if(!Rst_n)beginen_cnt <= 1'b0;state <= IDLE;key_flag <=1'b0;key_state <= 1'b1;endelse begincase(state)IDLE:beginkey_flag <= 1'b0;if(nedge)beginstate <= FILTER0;en_cnt <= 1'b1;endelse state <= IDLE;endFILTER0:beginif(cnt_full)beginkey_flag <=1'b1;key_state <= 1'b0;state <= DOWN;en_cnt <= 1'b0;endelse if(pedge)beginstate <= IDLE;en_cnt <= 1'b0;endelse state <= FILTER0;endDOWN:beginkey_flag <= 1'b0;if(pedge)beginstate <= FILTER1;en_cnt <= 1'b1;endelsestate <= DOWN;endFILTER1:beginif(cnt_full)beginkey_flag <=1'b1;key_state <= 1'b1;state <= IDLE;endelse if(nedge)beginen_cnt <= 1'b0;state <= DOWN;endelsestate <= FILTER1;end default:beginstate <= IDLE;en_cnt <= 1'b0;key_flag <= 1'b0;key_state <= 1'b1;endendcaseendalways@(posedge Clk or negedge Rst_n)if(!Rst_n)cnt <= 20'd0;else if (en_cnt)cnt <= cnt + 1'b1;else cnt <= 20'd0;always@(posedge Clk or negedge Rst_n)if(!Rst_n)cnt_full <= 1'b0;else if (cnt == 999_999)cnt_full <= 1'b1;else cnt_full <= 1'b0; endmodule

    led_ctrl.v

    module led_ctrl(Clk,Rst_n,key_flag0,key_flag1,key_state0,key_state1,led);input Clk;input Rst_n;input key_flag0,key_flag1;input key_state0,key_state1;output [3:0] led;reg [3:0] led_r;always@(posedge Clk or negedge Rst_n)if(!Rst_n)led_r <= 4'b0000;else if(key_flag0 && !key_state0)led_r <= led_r + 1'b1;else if(key_flag1 && !key_state1)led_r <= led_r - 1'b1;else led_r <= led_r;assign led = ~led_r; endmodule

    key_led_top_tb.v

    `timescale 1ns/1ns`define clk_period 20 module key_led_top_tb;reg Clk;reg Rst_n;wire key_in0,key_in1;reg press0,press1;wire [3:0] led;key_led_top key_led_top0(.Clk(Clk),.Rst_n(Rst_n),.key_in0(key_in0),.key_in1(key_in1),.led(led));key_model key_model0(.press(press0),.key(key_in0));key_model key_model1(.press(press1),.key(key_in1));initial Clk = 1'b1;always #(`clk_period/2) Clk = ~Clk;initial beginRst_n = 1'b0;press0 = 0;press1 = 0;#(`clk_period *10) Rst_n = 1'b1;#(`clk_period *10 +1);press0 = 1;#(`clk_period *3) press0 = 0;#80_000_000press0 = 1;#(`clk_period *3) press0 = 0;#80_000_000press0 = 1;#(`clk_period *3) press0 = 0;#80_000_000press1 = 1;#(`clk_period *3) press1 = 0;#80_000_000press1 = 1;#(`clk_period *3) press1 = 0;$stop;end endmodule

    key_model.v

    `timescale 1ns/1nsmodule key_model(press,key);input press;output reg key;reg [15:0] myrand;initial beginkey = 1'b1; // press_key; // #10000; // press_key; // #10000; // press_key; // $stop;endalways@(posedge press)press_key;task press_key;begin//按下抖动过程repeat(50)beginmyrand = {$random}%65536;//0-65535;#myrand key = ~key;endkey = 0;#25000000;//释放抖动过程repeat(50)beginmyrand = {$random}%65536;//0-65535;#myrand key = ~key;endkey = 1;#25000000;endendtask endmodule

    功能仿真

    10.A数码管动态扫描设计与验证

    8位7段数码管驱动实验

    课程目标:FPGA驱动数码管显示实验 实验平台:Snow Dream starter board 核心板 + 数码管_VGA_PS2学生模块

    实验现象:在Quartus II 中,使用In system sources andprobes editor工具,输入需要显示在数码管上的数据,则数码管显示对应数值。 知识点:

  • 数码管动态扫描实现
  • In system sources and probes editor(ISSP)调试工具的使用。
    • 数码管显示数字原理
    • 数码管动态扫描原理
    • ISSP调试工具的简单使用

    a. 4输入查找表,8位输出 b. 分频模块,从系统时钟分频得到1KHz的扫描时钟 c. 8选1多路器,选择端为当前扫描的数码管位置 d. 8位循环移位寄存器,1000_0000 ->0000_0001

    由于代码加长,之后的代码都不附上

    恰好可以不看参考代码,自己从头写一下代码,这样才有长进

    HEX8.v

    设计代码

    HEX8_tb.v

    测试平台

    HEX_top.v和ISSP.v

    板级验证的文件

    10.B串行移位寄存器原理与结构分析

    串转并 锁存器

    10.C串行移位寄存器驱动数码管显示设计与实现

    11.A串口发送模块与验证

    • 实验平台:芯航线FPGA学习套件核心板、PC机

    • 实验现象:在 Quartus II中,使用 In system sources and probes editor工具,输入需要通过串口发送出去的数据,然后按下学习板上的按键O,则FPGA自动将所需要发送的数据发送出去

    • 知识点:

    • 1.UART通信协议实现

    • 2.In system sources and probes editor(ISSP)调试工具的使用

    11.B例解使用串口发送多个字节的数据方案

    通过串口发送模块发送“HELLO”字符串。

    以上就是小编对于小梅哥的FPGA问题和相关问题的解答了,小梅哥的FPGA的问题希望对你有用!

     

    声明:本文图片、文字、视频等内容来源于互联网,本站无法甄别其准确性,建议谨慎参考,本站不对您因参考本文所带来的任何后果负责!本站尊重并保护知识产权,本文版权归原作者所有,根据《信息网络传播权保护条例》,如果我们转载内容侵犯了您的权利,请及时与我们联系,我们会做删除处理,谢谢。

     

    相关内容