FPGA入门学习——流水灯

FPGA入门学习——流水灯

初入门FPGA,对于FPGA到底是什么,能做什么,网上一大堆,但由于之前一直学习单片机,二者差别还是很明显的。单片机是硬件结构已经固定,用软件编程操作软件指令在硬件上执行。而FPGA是硬件可编程,是直接操作硬件的。同时,单片机程序是串行执行的,一句一句顺序往下执行,而FPGA程序是并行执行的,这也就是为什么FPGA比单片机快的原因。而这个特点也导致在编程时有一个思维转换。

入门还是回到经典的流水灯实验。FPGA编程主要是Verilog和VHDL语言,Verilog的灵活度高等特点使得Verilog使用率越来越高,国内开发采用的也主要是Verilog。首先介绍一些基本Verilog语法知识。

Verilog语法

1.标识符
Verilog的标识符与C语言相同,区分大小写。普通内部信号小写,参数定义大写。低电平采用_n 后缀:enable_n,统一缩写,如全局复位信号 rst_n。

2.进制

  • (1)二进制 :4’b0101 表示 4 位二进制数字 0101;
  • (2)十进制 :4’d4 表示 4 位十进制数字 4(二进制 0100);
  • (3)十六进制表示如下:4’ha 表示 4 位十六进制数字 a(二进制 1010)
    代码中若未指定,默认为 32 位的十进制。

3.数据类型:
说明:X 和 Z:X 代表不定值,z 代表高阻值,例如,5’b00x11,第三位不定值,3’b00z 表示最低位为高阻值。
(1)寄存器类型
寄存器数据类型有reg、integer、real 等,最常用的是 reg 类型:

//reg define
reg [31:0] delay_cnt; //延时计数器
reg key_flag ; //按键标志
  • parameter可以用标识符定义常量,运用时只使用标识符即可,比如定义 parameter width_8 = 8 ; 定义寄存器 reg [width_8-1:0] t; 即定义了 8 位宽度的寄存器。
  • 在时序逻辑中该寄存器变量对应为寄存器;若语句描述的是组合逻辑,则该寄存器变量对应为硬件连线;
  • 若未赋值 ,寄存器的值为x。

(2)网络类型变量
网络类型也有很多种,如 tri 和 wire 等,最常用的是 wire 类型,用于结构实体之间的物理连接,不能储存值,用连续赋值语句 assign 赋值。

//wire define
wire data_en; //数据使能信号 wire [7:0] data ; //数据
assign data_en = b ; // 是将 b 的结点连接到连线 data_en 上

(3)Memory类型变量
memory 类型定义 RAM,ROM 等存储器,其实本质就是多个寄存器组合其结构为 reg [n-1:0] 存储器名[m-1:0],即 m 个 n 位宽度的寄存器。

reg [7:0] ram [255:0]//表示定义了 256 个 8 位寄存器,256 也即是存储器的深度,8 为数据宽度

3.运算符:
Verilog的运算符与C语言很多相似之处,此处只记录一些不同的点。

  • 拼接运算符 {} :这是C语言中没有的,可以把两个或多个信号的某些位拼接起来进行运算操作,如 {a,b}将 a 和 b 拼接起来,作为一个新信号
  • 赋值运算符 = , <=
    “=”阻塞赋值,”<=”非阻塞赋值(而不再是C语言中的比较运算符)。阻塞赋值为顺序执行,而且赋值是立即执行;非阻塞赋值为并行执行,不考虑顺序。
    组合逻辑的always模块中使用阻塞赋值;时序逻辑的always模块中使用非阻塞赋值;比如可以理解为,组合中计算马上赋值,时序逻辑中上升沿计算,下降沿赋值。
    在 assign 语句中必须用阻塞赋值

4.关键字:
与C语言一样 ,Verilog中规定了部分关键字。这里列举一部分常用的关键字。

关键字含义
module模块开始定义
input输入端口定义
output输出端口定义
inout双向端口定义
parameter信号的参数定义
wirewire信号定义
regreg信号定义
always产生reg信号语句的关键字
assign产生wire信号语句的关键字
begin语句的起始标志
end语句的结束标志
posedge/negedge时序电路的标志
caseCase语句起始标记
defaultCase语句的默认分支标志
endcaseCase语句结束标记
endmodule模块结束定义

实战——流水灯

黑金AX309开发板——Spartan6LED原理图
在这里插入图片描述

1.编写代码

`timescale 1ns / 1ps

module flow_led(
	input 		sys_clk,			//系统时钟
	input			sys_rst_n,		//系统复位,n表示低电平有效
	output		reg[3:0] led   //4个LED灯 
    );

//reg define
reg [23:0] counter;				//定义24位计数器

//*****************************************************
//** main code
//*****************************************************
//计数器对系统时钟计数,计时0.2秒
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		counter <= 24'd0;						//如果复位信号,则清零计数器
	else if(counter < 24'd1000_0000)//开发板晶振为50MHz,周期为20ns,20*10_000000=200_000000ns=100ms
		counter <= counter + 1'b1;
	else 											//计时到0.2s 需要0.2s/20ns=10_000000,
		counter <= 24'd0;						//每当计时到0.2s时计数器清零
end
//通过移位寄存器控制 IO 口的高低电平,从而改变 LED 的显示状态
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)		
		led <= 4'b0001;							//复位时赋初始值,最低位LED亮
	else if(counter == 24'd1000_0000)
		led[3:0] <= {led[2:0],led[3]};	//完成移位操作,即左移一位,将最高位移至最低位,循环移位
	else
			led <= led;
end
endmodule

2.仿真

代码编译无误后,首先要经过仿真检验代码的正确性。仿真工具有两种,一种是 ISE 自带的 ISIM 仿真工具,另一种是与 Modelsim 软件进行联合仿真,实际应用中使用Modelsim联合仿真会更多。初入门,此处以ISIM仿真为例,

1.编写激励文件 testbench
激励文件可以理解为仿真时给输入端口提供激励信号的文件,是仿真必不可少的。
Verilog中 # 是延迟的意思,#号后面数字是延迟的数量,延迟的单位由 timescale控制,比如有 `timescale 1ns/1ps 意思就是时间单位为1ns,精度是1ps那么,#10.5 就是延迟10.5ns的意思。在编写激励文件的时候要定义时间单位和时间精度,用时间尺度预编译指令timescale。

`timescale 1ns / 1ps					// 定义仿真时间单位 1ns 和仿真时间精度为 1ns

module flow_led_tb;					//测试模块端口定义
	
	//parameter define
	parameter T = 20; 				//时钟周期为20ns
	
	// Inputs
	reg sys_clk;
	reg sys_rst_n;

	// Outputs
	wire [3:0] led;

	initial begin
		// Initialize Inputs
		sys_clk          = 1'b0;
		sys_rst_n        = 1'b0;
		#(T+1) sys_rst_n = 1'b1; //第21ns 时复位信号拉高
   end     
		
		//50Mhz 的时钟,周期则为 1/50Mhz=20ns,所以每 10ns,电平取反一次
		always #(T/2) sys_clk = ~sys_clk;
	// Instantiate the Unit Under Test (UUT)
	flow_led uut (						//例化 led 模块
		.sys_clk(sys_clk), 
		.sys_rst_n(sys_rst_n), 
		.led(led)
	);
endmodule

2.仿真设置
设置单步仿真时间为1000ms,因为代码中流水灯每200ms流水一次所以设置单步仿真为1000ms ,可以看到流水灯切换四次。 设置仿真总时间
设置好单步运行时间后开始运行,显示全部波形后如下图所示:
在这里插入图片描述
可以看到led的值经过四次变化,完成流水灯的效果。仿真成功,说明代码没有问题了,就可以继续进行下去了,也就是要在硬件平台上实现流水灯的效果,那么久进入下一步。

3.创建约束文件

1.创建UCF文件

PlanAhead 可以用来分配引脚,分配好后点击保存会自动生成一个 UCF 文件,但那样操作麻烦,所以直接创建一个用户约束文件,即UCF 文件,在文件中手动输入时序约束语句和引脚分配语句。

2.编写UCF文件。

需要用到以下约束语句和引脚分配语句:
NET:端口关键字,用来引出端口如 sys_clk、sys_rst_n、led<0>、led<1>、led<2>、led<3>。 LOC:引脚关键字,后面接等于号用来把端口分配到具体引脚如 LOC = M6。
IOSTANDARD:电压标准关键字,后面接等于号用来约束引脚的电压如 IOSTANDARD = “LVCMOS33”。(开发板的 IO BNAK 电压为 3.3V,所以设置为“3.3-V LVTTL”)
TNM_NET(timing name for nets):网络约束,把时钟(sys_clk)选出来赋予命名如 TNM_NET =
sys_clk_pin,并对整个网络所在路径的所有有效同步元件进行约束,可以穿过 IBUFG。
TIMESPEC、PERIOD:周期约束,用来约束时钟周期如 TIMESPEC TS_sys_clk_pin = PERIOD
sys_clk_pin 50000 kHz。

NET sys_clk  LOC = T8 |  IOSTANDARD = "LVCMOS33" |TNM_NET = sys_clk_pin;
TIMESPEC TS_sys_clk_pin = PERIOD sys_clk_pin 50000 kHz;

NET sys_rst_n LOC = L3 | IOSTANDARD = "LVCMOS33";   //管脚约束 | 电平标准

################## LED piN Define  #######################
NET led<0> LOC = P4 | IOSTANDARD = "LVCMOS33";
NET led<1> LOC = N5 | IOSTANDARD = "LVCMOS33";
NET led<2> LOC = P5 | IOSTANDARD = "LVCMOS33";
NET led<3> LOC = M6 | IOSTANDARD = "LVCMOS33";
3.生成可执行文件。

第二步编译完成后,就可以生成bit流文件了,以iMPACT方式下载到开发板中,最后的效果就是4个LED灯以200ms的时间间隔实现流水的效果。至此,流水灯项目就完成了。

—————学习心得—————

由于时间有限,又是初入门FPGA,仅介绍了一部分简单Verilog语法,用流水灯设计介绍了用FPGA实现简单的项目的流程。但一整套流程下来,FPGA与之前熟悉的单片机的区别还是肉眼可见的,但现在只实现了最简单的流水灯,以后写更复杂的项目所需要的模块化设计、代码调试等经验都需要更深入学习、长时间积累的,而且FPGA操作硬件,也就是设计电路需要灵活运用数电的知识,需要对电路底层有更深的认真,好在现在有了初步的认识,日后的学习也有了方向,总之,日后要学习的还太多了。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值