如何学习FPGA

如何学习FPGA,第1张

掌握FPGA可以找到一份很好的工作,对于有经验的工作人员,使用FPGA可以让设计变得非常有灵活性。掌握了FPGA设计,单板硬件设计就非常容易(不是系统设计),特别是上大学时如同天书的逻辑时序图,看起来就非常亲切。但FPGA入门却有一定难度,因为它不像软件设计,只要有一台计算机,几乎就可以完成所有的设计。FPGA设计与硬件直接相关,需要实实在在的调试仪器,譬如示波器等。这些硬件设备一般比较昂贵,这就造成一定的入门门槛,新人在入门时遇到一点问题或者困难,由于没有调试设备,无法定位问题,最后可能就会放弃。其实这时如果有人稍微指点一下,这个门槛很容易就过去。我用FPGA做设计很多年了,远达不到精通的境界,只是熟悉使用,在这里把我对FPGA学习步骤理解写出来,仅是作为一个参考,不对的地方,欢迎大家讨论和指正。FPGA学习步骤1、工欲善其事,必先利其器。计算机必不可少。目前FPGA应用较多的是Altera和xilinx这两个公司,可以选择安装quartusII或者ISE软件。这是必备的软件环境。FPGA学习步骤2、熟悉verilog语言或者vhdl语言 ,熟练使用quartusII或者ISE软件。起步阶段不希望报一些培训班,除非你有钱,或者运气好,碰到一个水平高、又想把自己的经验和别人共享的培训老师,不然的话,培训完后总会感觉自己是一个冤大头。入门阶段可以在利用网络资源完成。FPGA学习步骤3、设计一个小代码,下载到目标板看看结果此时可以设计一个最简答的程序,譬如点灯。如果灯在闪烁了,表示基本入门了。如果此时能够下载到FPGA外挂的flash,FPGA程序能够从flash启动,表明FPGA的最简单设计你已经成功,可以到下一步。FPGA学习步骤4、设计稍微复杂的代码,下载到目标板看看结果。可以设计一个UART程序,网上有参考,你要懂RS232协议和FPGA内置的逻辑分析仪。网上下载一个串口调试助手,调试一番,如果通信成功了, 恭喜,水平有提高。进入下一步。FPGA学习步骤5、设计复杂的代码,下载到目标板看看结果。譬如sdram的程序,网上也有参考,这个设计难度有点大。可用串口来调试sdram,把串口的数据存储到sdram,然后读回,如果成功,那你就比较熟悉FPGA设计了FPGA学习步骤6、设计高速接口,譬如ddr2或者高速串行接口这要对FPGA的物理特性非常了解,而且要懂得是时序约束等设计方法,要看大量的原厂文档,这部分成功了,那就对FPGA的物理接口掌握很深,你就是设计高手了FPGA学习步骤7、设计一个复杂的协议譬如USB、PCIexpress、图像编解码等,锻炼对系统的整体把握和逻辑划分。完成这些,你就是一个一流的高手、

你先仿真下看看数据最后输出的对不对。。各部分都没有问题了你再下板子。。如果有错误分析下现象,不然错也不知道错在哪,直接看程序问题也没有重点不好找。。。

如果仿真是对的,看下你的串口助手的波特率设置的对不对,必须要和你FPGA程序里波特率一致,串口助手才可以显示出来你的数据。。。

如果是程序仿真都不对,那再追问,我帮你看这个程序,前年的时候我做过UART的东西。。

VGA显示控制器须提供R、G、B三基色图像信号,HS行同步信号和VS场同步信号。由于VGA接口显示器仅能处理串行模拟信号,因此,VGA控制器所产生的信号经D/A转换器将数字信号转变为模拟信号后发送给显示器使用。VGA显示器正确、完整地显示数字图像包括时序的构建和数字图像信息的模拟化两个方面。据此系统硬件实现框图如图4所示,系统硬件由ADC控制模块、波形稳定控制模块、RAM存储模块、VGA控制模块组成。在ADC控制模块的控制下将A/D转换器转换后的数字信号经波形稳定模块处理后,存入数据缓存器RAM内,而后VGA控制器在驱动显示器的时候,读取数据缓存器中的数据进行显示[5]。通常VGA显示器显示的数据量较大,而FPGA内置的片内存储器资源很难满足存储量的需求,因此,一般都需要通过外接存储器进行扩展,对于图像等大数据量处理系统通常选用SDRAM进行扩展[6],本系统只进行波形的显示,不进行数据处理等 *** 作,故片内存储器足够满足使用要求。

开源硬件领域MCU板卡很火,著名的Arduino、树莓派(Raspberry Pi)、Micro:bit,开源的MCU也是个热门的话题,除了老牌的8051、OpenRisc等,这两年的明星就是 Risc V 了,在中国集成电路大跃进的加持下,它几乎成了中国处理器追赶世界的一剂春药。

两天前穷途末路的MIPS也宣布要开源了。

开源要成功,最重要的是要形成一个良性的生态,这个生态中缺少一个环节,无论吵吵的多热闹,最后还是会散场。

很多人在问, FPGA 领域有没有开源的平台呢?在以前还真的难找见,本来FPGA的器件厂商就凤毛麟角了(本身也说明了一定的问题),支持FPGA开发的工具更是难以靠民间的力量搞定了,从商业角度貌似没有足够的吸引力让一些大的玩家去投入足够的资源来构建这么一个生态。

但在民间,仍不乏一些痴迷的技术高手,他们以玩转技术、让不可能变成可能作为人生最大的乐趣,他们利用仅有的一个后门通道 - Lattice的iCE40系列,热烈地玩着,不问前途,虽然小众,但也收获了越来越多的喝彩。

他们是小脚丫FPGA的榜样,也许我们没有足够的力量像他们一样在技术上 探索 如此之深,但我们的使命是让FPGA更加接地气、亲民,通过自己的努力构建尽可能完整的学习生态系统,让每个学习数字逻辑的学生都能掌握FPGA的使用。

在FPGA领域,Lattice的iCE40系列是硬件开源领域最受欢迎的一款,在海外的骇客(Hacker,水平远高于我们碰到的Maker)工程师中知名度极高,无论是基于这个系列的板卡还是开发工具都有众多的玩家,尤其是Lattice最近新推出的iCE40UP5K - 非常低的价格(估计批量的单片售价低于10元RMB)、虽然有限的逻辑资源但能够支持Sensor AI的功能,乃至支持现在如日中天的开源处理器Risc V软核,无疑iCE40UP5K将成为开源FPGA届的新星。

借Crowsupply上的一个众筹产品来简单看看FPGA在开源方面的一些 探索 ,也让一些学习fpga的朋友更直观地体会一下究竟怎样玩FPGA才能学到真的技能,对这个板子有兴趣的朋友可以点击左下角“阅读原文”到达相应的页面上去详细阅读:

iCEBreaker FPGA板专为FPGA的初学者量身定制的,它支持最新的开源FPGA 开发工具以及下一代的开源CPU架构。iCEBreaker很轻松地跟各种Pmod外设进行连接,可以有大量得第三方Pmod模块可以选用,本板卡的设计者也专门制作了一系列的Pmod专用模块。

iCEBreaker的照片

性能指标

核心器件使用Lattice iCE40UP5k FPGA

5280逻辑单元 (4-LUT + Carry + FF),跟我们Lattice版本的小脚丫FPGA资源相当,比Altera版本的逻辑资源少;

120 Kbit双口RAM

1 Mbit (128 KByte)单口RAM,这个存储量超过了我们小脚丫用的FPGA的内部存储器容量,这是它能支持CNN的原因;

PLL, 2 x SPI, 2 x I2C硬化的IPs

两个内部振荡器 (10 kHz and 48 MHz) 大大简化设计

8个DSP乘法器块用于信号处理,比如语音合成以及软件定义无线电;

非常低的功耗,用于电池供电得场合

3个24 mA驱动和 3 x hard IP PWM (可以直接驱动RGB LED灯和小电流的马达)

足够多、足够快的存储器

128 Mbit (16 MB) quad SPI double data rate (QSPI-DDR) flash

例如: 可以向LED点阵传送视频流

很多的I/O(其实UP5K FPGA的管脚很少,但也够用了)

3个管脚用于RGB LED

2个用于板上的LEDs

一个UART端口, RX管脚和TX管脚可以通过虚拟USB串口进行访问

一个按键

两个Pmod 连接器 (总计16x管脚)

有一个可以掰下来的Pmod (8 x pins)

5个星型排列的LED灯

3个按键

足够的资源支持CPU软核!

picorv32

pico soc

RISC-V以及其它软核

板上FPGA编程器以及USB到串行适配器

兼容IceStorm iceprog工具

简单, 同主机连接无需驱动

Pmod连接器和Pmod模块

这个板子有3种标准的Pmod连接器,可以做非常丰富的扩展 - 可以用市场上众多的现成的Pmod模块,也可以自己设计专用的Pmod模块,比如他们自己开发得7段数码管Pmod、DIP开关Pmod、LED屏驱动Pmod和一个HDMI输出Pmod

各种Pmod模块

开源的工具链

iCEBreaker FPGA原型板支持一下的开源设计工具:

Yosys: 用于Verilog RTL综合的框架;

Arachne-pnr: 针对iCE40 FPGA的布局布线工具

nextpnr: 时序驱动的FPGA布局布线工具;

IceStorm: 针对iCE40 FPGA进行分析和创建bitstreams的工具;

icestudio: 基于IceStorm的可视化编辑器

Migen: 基于Python的FPGA硬件设计工具包

ICEStudio的界面

Nextpnr的界面

iCEBreaker板子的框图:

下面几张图是这个FPGA的制作者去年参加我们母公司SupplyFrame在Pasadena举办的Hackaday SuperCon的状况;

数码管显示Pmod和8位开关Pmod

HDMI输出Pmod

这个板子上用到的iCE40UP5k FPGA速度足够快,能够输出720p的视频!通过上图中的HDMI Pmod模块(支持12 bits/pixel)可以输出800 x 600分辨率的测试图案以及一个720p的图案。

LED屏幕驱动Pmod

大的视频广告板,采用很多小型的RGB LED灯组成,可以驱动产生动画效果和视频。

驱动大的LED屏幕的Pmod

下面的表格是国外一些成型的开源FPGA平台的简单对比,从这个表格中可以看出,所有的FPGA器件都是来自Lattice的iCE40系列。

//本模块的功能是验证实现和PC机进行基本的串口通信的功能。需要在

//PC机上安装一个串口调试工具来验证程序的功能。

//程序实现了一个收发一帧10个bit(即无奇偶校验位)的串口控

//制器,10个bit是1位起始位,8个数据位,1个结束

//位。串口的波特律由程序中定义的div_par参数决定,更改该参数可以实

//现相应的波特率。程序当前设定的div_par 的值是0x145,对应的波特率是

//9600。用一个8倍波特率的时钟将发送或接受每一位bit的周期时间

//划分为8个时隙以使通信同步

//程序的工作过程是:串口处于全双工工作状态,按动key1,FPGA/CPLD向PC发送“21 EDA"

//字符串(串口调试工具设成按ASCII码接受方式);PC可随时向FPGA/CPLD发送0-F的十六进制

//数据,FPGA接受后显示在7段数码管上。

//视频教程适合我们21EDA电子的所有学习板

module UART(clk, rst, rxd, txd, en, seg_data, key_input, lowbit);

input clk,rst;

input rxd; //串行数据接收端

input key_input; //按键输入

output[7:0] en;

output[7:0] seg_data;

reg[7:0] seg_data;

output txd; //串行数据发送端

output lowbit;

//inner reg//

reg[15:0] div_reg; //分频计数器,分频值由波特率决定。分频后得到频率8倍波特率的时钟

reg[2:0] div8_tras_reg; //该寄存器的计数值对应发送时当前位于的时隙数

reg[2:0] div8_rec_reg; //该寄存器的计数值对应接收时当前位于的时隙数

reg[3:0] state_tras; //发送状态寄存器

reg[3:0] state_rec; //接受状态寄存器

reg clkbaud_tras; //以波特率为频率的发送使能信号

reg clkbaud_rec; //以波特率为频率的接受使能信号

reg clkbaud8x; //以8倍波特率为频率的时钟,它的作用是将发送或接受一个bit的时钟周期分为8个时隙

reg recstart; //开始发送标志

reg recstart_tmp;

reg trasstart; //开始接受标志

reg rxd_reg1; //接收寄存器1

reg rxd_reg2; //接收寄存器2,因为接收数据为异步信号,故用两级缓存

reg txd_reg; //发送寄存器

reg[7:0] rxd_buf; //接受数据缓存

reg[7:0] txd_buf; //发送数据缓存

reg[2:0] send_state; //每次按键给PC发送"Welcome"字符串,这是发送状态寄存器

reg[19:0] cnt_delay; //延时去抖计数器

reg start_delaycnt; //开始延时计数标志

reg key_entry1, key_entry2;//确定有键按下标志

parameter div_par = 16'h145; //分频参数,其值由对应的波特率计算而得,按此参数分频的时钟频率是波倍特率的8

//倍,此处值对应9600的波特率,即分频出的时钟频率是96008 (CLK 50M)

////

assign txd = txd_reg;

assign lowbit = 0;

assign en = 0; //7段数码管使能信号赋值

////

always@(posedge clk)

begin

if(!rst) begin

cnt_delay <= 0;

start_delaycnt <= 0; end

else if(start_delaycnt) begin

if(cnt_delay != 20'd800000) begin

cnt_delay <= cnt_delay + 1;end

else begin

cnt_delay <= 0;

start_delaycnt <= 0; end end

else begin

if(!key_input && cnt_delay == 0)

start_delaycnt <= 1; end

end

////

always@(posedge clk)

begin

if(!rst)

key_entry1 <= 0;

else begin

if(key_entry2)

key_entry1 <= 0;

else if(cnt_delay == 20'd800000) begin

if(!key_input)

key_entry1 <= 1; end end

end

////

always@(posedge clk)

begin

if(!rst)

div_reg <= 0;

else begin

if(div_reg == div_par - 1)

div_reg <= 0;

else

div_reg <= div_reg + 1; end

end

////

always@(posedge clk) //分频得到8倍波特率的时钟

begin

if(!rst)

clkbaud8x <= 0;

else if(div_reg == div_par - 1)

clkbaud8x <= ~clkbaud8x;

end

////

always@(posedge clkbaud8x or negedge rst)

begin

if(!rst)

div8_rec_reg <= 0;

else if(recstart) //接收开始标志

div8_rec_reg <= div8_rec_reg + 1; //接收开始后,时隙数在8倍波特率的时钟下加1循环

end

////

always@(posedge clkbaud8x or negedge rst)

begin

if(!rst)

div8_tras_reg <= 0;

else if(trasstart)

div8_tras_reg <= div8_tras_reg + 1; //发送开始后,时隙数在8倍波特率的时钟下加1循环

end

////

always@(div8_rec_reg)

begin

if(div8_rec_reg == 7)

clkbaud_rec = 1; //在第7个时隙,接收使能信号有效,将数据打入

else

clkbaud_rec = 0;

end

////

always@(div8_tras_reg)

begin

if(div8_tras_reg == 7)

clkbaud_tras = 1; //在第7个时隙,发送使能信号有效,将数据发出

else

clkbaud_tras = 0;

end

////

always@(posedge clkbaud8x or negedge rst)

begin

if(!rst) begin

txd_reg <= 1;

trasstart <= 0;

txd_buf <= 0;

state_tras <= 0;

send_state <= 0;

key_entry2 <= 0; end

else begin

if(!key_entry2) begin

if(key_entry1) begin

key_entry2 <= 1;

txd_buf <= 8'd50; end end//"2"

else begin

case(state_tras)

4'b0000: begin //发送起始位

if(!trasstart && send_state < 7)

trasstart <= 1;

else if(send_state < 7) begin

if(clkbaud_tras) begin

txd_reg <= 0;

state_tras <= state_tras + 1;end end

else begin

key_entry2 <= 0;

state_tras <= 0; end end

4'b0001: begin //发送第1位

if(clkbaud_tras) begin

txd_reg <= txd_buf[0];

txd_buf[6:0] <= txd_buf[7:1];

state_tras <= state_tras + 1; end end

4'b0010: begin //发送第2位

if(clkbaud_tras) begin

txd_reg <= txd_buf[0];

txd_buf[6:0] <= txd_buf[7:1];

state_tras <= state_tras + 1; end end

4'b0011: begin //发送第3位

if(clkbaud_tras) begin

txd_reg <= txd_buf[0];

txd_buf[6:0] <= txd_buf[7:1];

state_tras <= state_tras + 1; end end

4'b0100: begin //发送第4位

if(clkbaud_tras) begin

txd_reg <= txd_buf[0];

txd_buf[6:0] <= txd_buf[7:1];

state_tras <= state_tras + 1; end end

4'b0101: begin //发送第5位

if(clkbaud_tras) begin

txd_reg <= txd_buf[0];

txd_buf[6:0] <= txd_buf[7:1];

state_tras <= state_tras + 1; end end

4'b0110: begin //发送第6位

if(clkbaud_tras) begin

txd_reg <= txd_buf[0];

txd_buf[6:0] <= txd_buf[7:1];

state_tras <= state_tras + 1; end end

4'b0111: begin //发送第7位

if(clkbaud_tras) begin

txd_reg <= txd_buf[0];

txd_buf[6:0] <= txd_buf[7:1];

state_tras <= state_tras + 1; end end

4'b1000: begin //发送第8位

if(clkbaud_tras) begin

txd_reg<=txd_buf[0];

txd_buf[6:0]<=txd_buf[7:1];

state_tras<=state_tras+1;

end

end

4'b1001: begin //发送停止位

if(clkbaud_tras) begin

txd_reg<=1;

txd_buf<=8'h55;

state_tras<=state_tras+1;

end

end

4'b1111:begin

if(clkbaud_tras) begin

state_tras<=state_tras+1;

send_state<=send_state+1;

trasstart<=0;

case(send_state)

3'b000:

txd_buf<=8'd49;//"1"

3'b001:

txd_buf<=8'd32;//" "

3'b010:

txd_buf<=8'd69;//"E"

3'b011:

txd_buf<=8'd68;//"D"

3'b100:

txd_buf<=8'd65;//"A"

3'b101:

txd_buf<=8'd10;//"e"

default:

txd_buf<=0;

endcase

end

end

default: begin

if(clkbaud_tras) begin

state_tras<=state_tras+1;

trasstart<=1;

end

end

endcase

end

end

end

////

always@(posedge clkbaud8x or negedge rst)//接受PC机的数据

begin

if(!rst) begin

rxd_reg1<=0;

rxd_reg2<=0;

rxd_buf<=0;

state_rec<=0;

recstart<=0;

recstart_tmp<=0;

end

else begin

rxd_reg1<=rxd;

rxd_reg2<=rxd_reg1;

if(state_rec==0) begin

if(recstart_tmp==1) begin

recstart<=1;

recstart_tmp<=0;

state_rec<=state_rec+1;

end

else if(!rxd_reg1&&rxd_reg2) //检测到起始位的下降沿,进入接受状态

recstart_tmp<=1;

end

else if(state_rec>=1&&state_rec<=8) begin

if(clkbaud_rec) begin

rxd_buf[7]<=rxd_reg2;

rxd_buf[6:0]<=rxd_buf[7:1];

state_rec<=state_rec+1;

end

end

else if(state_rec==9) begin

if(clkbaud_rec) begin

state_rec<=0;

recstart<=0;

end

end

end

end

always@(rxd_buf) //将接受的数据用数码管显示出来

begin

case (rxd_buf)

8'h30: seg_data=8'b11000000;

8'h31: seg_data=8'b11111001;

8'h32: seg_data=8'b10100100;

8'h33: seg_data=8'b10110000;

8'h34: seg_data=8'b10011001;

8'h35: seg_data=8'b10010010;

8'h36: seg_data=8'b10000010;

8'h37: seg_data=8'b11111000;

8'h38: seg_data=8'b10000000;

8'h39: seg_data=8'b10010000;

8'h41: seg_data=8'b10001000;//a

8'h42: seg_data=8'b10000011;

8'h43: seg_data=8'b11000110;

8'h44: seg_data=8'b10100001;

8'h45: seg_data=8'b10000110;

8'h46: seg_data=8'b10001110;

default: seg_data=8'b11111111;

endcase

end

endmodule

以上就是关于如何学习FPGA全部的内容,包括:如何学习FPGA、大家快来帮帮忙.关于verilog-UART的。有代码可是FPGA数码管低二位无法显示ASCII码、FPGA实现一个有UART接收信息,再将信息使用VGA显示在屏幕上。由信息转图像应如何将字母转化成点阵等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zz/10079719.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-05
下一篇 2023-05-05

发表评论

登录后才能评论

评论列表(0条)

保存