stepfpga上基本功能的实现(verilog)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_33946416/article/details/95794841

1.小脚丫FPGA开发平台特色:
1)降低学习门槛,精心挑选方便易学的FPGA芯片,降低难度和成本两个门槛。

2)小巧方便,体积只有手指大小,且开发板集成Jtag下载器,只需要一根MicroUSB线即可下载,使用和携带都很方便。

3)资源丰富,FPGA片上资源和小脚丫板载资源丰富,能够满足数字电路和FPGA相关课程的实验需求。

4).易于扩展,我们同时提供配套底板,方便学员学习和扩展使用,也可以根据自己的需求搭建硬件外设开发学习
在这里插入图片描述stepfpga板载资源
在这里插入图片描述
2.基于verilog语言的在小脚丫上的简单应用
1)点亮led
~硬件说明
STEP-MXO2 V2开发板虽然很小巧,上面也集成了不少外设,用FPGA控制简单外设,用按键或者开关控制LED的亮和灭。
在这里插入图片描述
这是开发板上的8个红色LED,LED1~8信号连接到FPGA的引脚,作为FPGA输出信号控制。当FPGA输出低电平时LED变亮,当FPGA输出高电平时LED熄灭。
在这里插入图片描述
这是开发板上4个按键和4个开关,Key1~4是按键控制信号,SW1-4是开关控制信号,都连接到FPGA的引脚,作为FPGA的输入信号。当按键断开时,FPGA输入为高电平,当按键按下时,FPGA输入为低电平;当开关断开(OFF)时,FPGA输入为低电平,当开关合上(ON)时,FPGA输入为高电平。
所以可以用开关或者按键来控制LED的亮灭。
Verilog代码

module LED (key,sw,led);
    input [3:0] key;						//按键输入信号
    	input [3:0] sw;							//开关输入信号
    	output [7:0] led;						//输出信号到LED
     
    	assign led = {key,sw};                                          //assign连续赋值。大括号是拼接符,表示把key和sw拼接组成一个新的8位数赋值给led
endmodule

管脚分配
描述
2)3-8译码器的设计
硬件说明

组合逻辑电路是数字电路的重要部分,电路的输出只与输入的当前状态相关的逻辑电路,常见的有选择器、比较器、译码器、编码器、编码转换等等。在本实验里以最常见的3-8译码器为例说明如何用Verilog实现。3-8译码器的真值表如下:
在这里插入图片描述
从前面的实验可以知道,当FPGA输出信号到LED为高电平时LED熄灭,反之LED变亮。同时可以开关的信号模拟3-8译码器的输入,这样控制开关我们就能控制特定的LED变亮。
Verilog代码

module decode38 (sw,led);
 
	input [2:0] sw;							//开关输入信号,利用了其中3个开关作为3-8译码器的输入
	output [7:0] led;						//输出信号控制特定LED
 
        reg [7:0] led;                                                  //定义led为reg型变量,在always过程块中只能对reg型变量赋值
 
        //always过程块,括号中sw为敏感变量,当sw变化一次执行一次always中所有语句,否则保持不变
	always @ (sw)
	begin
		case(sw)                                                   //case语句,一定要跟default语句
			3'b000:	led=8'b0111_1111;                         //条件跳转,其中“_”下划线只是为了阅读方便,无实际意义  
			3'b001:	led=8'b1011_1111;                         //位宽'进制+数值是Verilog里常数的表达方法,进制可以是b、o、d、h(二、八、十、十六进制)
			3'b010:	led=8'b1101_1111;
			3'b011:	led=8'b1110_1111;
			3'b100:	led=8'b1111_0111;
			3'b101:	led=8'b1111_1011;
			3'b110: led=8'b1111_1101;
			3'b111:	led=8'b1111_1110;
			default: ;
		endcase
	end
 
endmodule

管脚分配
在这里插入图片描述
3)数码管显示
硬件说明
数码管是工程设计中使用很广的一种显示输出器件。一个7段数码管(如果包括右下的小点可以认为是8段)分别由a、b、c、d、e、f、g位段和表示小数点的dp位段组成。实际是由8个LED灯组成的,控制每个LED的点亮或熄灭实现数字显示。通常数码管分为共阳极数码管和共阴极数码管,结构如下图所示:
在这里插入图片描述
共阴8段数码管的信号端低电平有效,而共阳端接高电平有效。当共阳端接高电平时只要在各个位段上加上相应的低电平信号就可以使相应的位段发光。比如:要使a段发光,则在a段信号端加上低电平即可。共阴极的数码管则相反。 可以看到数码管的控制和LED的控制有相似之处,在小脚丫STEP-MXO2 V2开发板上有两位共阴极数码管,

在这里插入图片描述
数码管所有的信号都连接到FPGA的管脚,作为输出信号控制。FPGA只要输出这些信号就能够控制数码管的那一段LED亮或者灭。这样我们可以通过开关来控制FPGA的输出,和3. 3-8译码器实验一样,通过组合逻辑的输出来控制数码管显示数字,下面是数码管显示的表格
在这里插入图片描述
Verilog代码
module LED (seg_data_1,seg_data_2,seg_led_1,seg_led_2);

input [3:0] seg_data_1;						//数码管需要显示0~9十个数字,所以最少需要4位输入做译码
input [3:0] seg_data_2;						//小脚丫上第二个数码管
output [8:0] seg_led_1;						//在小脚丫上控制一个数码管需要9个信号 MSB~LSB=DIG、DP、G、F、E、D、C、B、A
output [8:0] seg_led_2;						//在小脚丫上第二个数码管的控制信号  MSB~LSB=DIG、DP、G、F、E、D、C、B、A

    reg [8:0] seg [9:0];                                            //定义了一个reg型的数组变量,相当于一个10*9的存储器,存储器一共有10个数,每个数有9位宽

    initial                                                         //在过程块中只能给reg型变量赋值,Verilog中有两种过程块always和initial
                                                                    //initial和always不同,其中语句只执行一次
    begin
          seg[0] = 9'h3f;                                           //对存储器中第一个数赋值9'b00_0011_1111,相当于共阴极接地,DP点变低不亮,7段显示数字  0
      seg[1] = 9'h06;                                           //7段显示数字  1
      seg[2] = 9'h5b;                                           //7段显示数字  2
      seg[3] = 9'h4f;                                           //7段显示数字  3
      seg[4] = 9'h66;                                           //7段显示数字  4
      seg[5] = 9'h6d;                                           //7段显示数字  5
      seg[6] = 9'h7d;                                           //7段显示数字  6
      seg[7] = 9'h07;                                           //7段显示数字  7
      seg[8] = 9'h7f;                                           //7段显示数字  8
      seg[9] = 9'h6f;                                           //7段显示数字  9
        end

    assign seg_led_1 = seg[seg_data_1];                         //连续赋值,这样输入不同四位数,就能输出对于译码的9位输出
    assign seg_led_2 = seg[seg_data_2];
 
endmodule

引脚分配
小脚丫上正好有4路按键和4路开关,可以用来作为输入信号分别控制数码管的输出。按照下面表格定义输入输出信号
信号 引脚 信号 引脚
segdata1(0) M7 segdata2(0) L14
segdata1(1) M8 segdata2(1) M13
segdata1(2) M9 segdata2(2) M14
segdata1(3) M10 segdata2(3) N14

信号 引脚 信号 引脚
segled1(0) A10 segled2(0) C12
segled1(1) C11 segled2(1) B14
segled1(2) F2 segled2(2) J1
segled1(3) E1 segled2(3) H1
segled1(4) E2 segled2(4) H2
segled1(5) A9 segled2(5) B12
segled1(6) B9 segled2(6) A11
segled1(7) F1 segled2(7) K1
segled1(8) C9 segled2(8) A12

4)时钟分频

硬件说明
时钟信号的处理是FPGA的特色之一,因此分频器也是FPGA设计中使用频率非常高的基本设计之一。一般在FPGA中都有集成的锁相环可以实现各种时钟的分频和倍频设计,但是通过语言设计进行时钟分频是最基本的训练,在对时钟要求不高的设计时也能节省锁相环资源。实现任意整数的分频器,分频的时钟保持50%占空比。
(1)偶数分频:偶数倍分频相对简单,比较容易理解。通过计数器计数是完全可以实现的。如进行N倍偶数分频,那么通过时钟触发计数器计数,当计数器从0计数到N/2-1时,输出时钟进行翻转,以此循环下去。
在这里插入图片描述
(2)奇数分频: 如果要实现占空比为50%的奇数倍分频,不能同偶数分频一样计数器记到一半的时候输出时钟翻转,那样得不到占空比50%的时钟。以待分频时钟CLK为例,如果以偶数分频的方法来做奇数分频,在CLK上升沿触发,将得到不是50%占空比的一个时钟信号(正周期比负周期多一个时钟或者少一个时钟);但是如果在CLK下降沿也触发,又得到另外一个不是50%占空比的时钟信号,这两个时钟相位正好相差半个CLK时钟周期。通过这两个时钟信号进行逻辑运算我们可以巧妙的得到50%占空比的时钟。
总结如下:对于实现占空比为50%的N倍奇数分频,首先进行上升沿触发进行模N计数,计数选定到某一个值进行输出时钟翻转,然后经过(N-1)/2再次进行翻转得到一个占空比非50%奇数n分频时钟。再者同时进行下降沿触发的模N计数,到和上升沿触发输出时钟翻转选定值相同值时,进行输出时钟时钟翻转,同样经过(N-1)/2时,输出时钟再次翻转生成占空比非50%的奇数n分频时钟。两个占空比非50%的n分频时钟进行逻辑运算(正周期多的相与,负周期多的相或),得到占空比为50%的奇数n分频时钟。
在这里插入图片描述
Verilog代码
module divide ( clk,rst_n,clkout);

    input 	clk,rst_n;                       //输入信号,其中clk连接到FPGA的C1脚,频率为12MHz
    output	clkout;                          //输出信号,可以连接到LED观察分频的时钟

    
parameter	WIDTH	= 3;             //计数器的位数,计数的最大值为 2**WIDTH-1
parameter	N	= 5;             //分频系数,请确保 N < 2**WIDTH-1,否则计数会溢出

reg 	[WIDTH-1:0]	cnt_p,cnt_n;     //cnt_p为上升沿触发时的计数器,cnt_n为下降沿触发时的计数器
reg			clk_p,clk_n;     //clk_p为上升沿触发时分频时钟,clk_n为下降沿触发时分频时钟

//上升沿触发时计数器的控制
always @ (posedge clk or negedge rst_n )         //posedge和negedge是verilog表示信号上升沿和下降沿
                                                     //当clk上升沿来临或者rst_n变低的时候执行一次always里的语句
	begin
		if(!rst_n)
			cnt_p<=0;
		else if (cnt_p==(N-1))
			cnt_p<=0;
		else cnt_p<=cnt_p+1;             //计数器一直计数,当计数到N-1的时候清零,这是一个模N的计数器
	end

     //上升沿触发的分频时钟输出,如果N为奇数得到的时钟占空比不是50%;如果N为偶数得到的时钟占空比为50%
     always @ (posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			clk_p<=0;
		else if (cnt_p<(N>>1))          //N>>1表示右移一位,相当于除以2去掉余数
			clk_p<=0;
		else 
			clk_p<=1;               //得到的分频时钟正周期比负周期多一个clk时钟
	end

    //下降沿触发时计数器的控制        	
always @ (negedge clk or negedge rst_n)
	begin
		if(!rst_n)
			cnt_n<=0;
		else if (cnt_n==(N-1))
			cnt_n<=0;
		else cnt_n<=cnt_n+1;
	end

    //下降沿触发的分频时钟输出,和clk_p相差半个时钟
always @ (negedge clk)
	begin
		if(!rst_n)
			clk_n<=0;
		else if (cnt_n<(N>>1))  
			clk_n<=0;
		else 
			clk_n<=1;                //得到的分频时钟正周期比负周期多一个clk时钟
	end

    assign clkout = (N==1)?clk:(N[0])?(clk_p&clk_n):clk_p;      //条件判断表达式
                                                                //当N=1时,直接输出clk
                                                                //当N为偶数也就是N的最低位为0,N(0)=0,输出clk_p
                                                                //当N为奇数也就是N最低位为1,N(0)=1,输出clk_p&clk_n。正周期多所以是相与
endmodule   

测试文件,进行功能仿真时需要编写testbench测试文件。verilog里的testbench文件和源文件一样也是.v文件,仿真能让我们更直观的观察信号波形

  `timescale 1ns/100ps                             //仿真时间单位/时间精度,时间单位要大于或者等于时间精度
 
          module divide_tb();                              //测试文件也是一个module,因为用于仿真所以无需输入输出信号
 
          reg    clk,rst_n;                                //需要产生的激励信号定义,激励信号需要过程块产生所以定义为reg型变量
          wire   clkout;                                   //需要观察的输出信号定义,定义为wire型变量
 
          //初始化过程块
          initial 
	  begin 
		 clk = 0;
		 rst_n = 0;
		 #25                                      //#表示延时25个时间单位
		 rst_n = 1;                               //产生了一个初始25ns低电平,然后变高电平的复位信号
	  end
 
          always #10 clk = ~clk;                          //每隔10ns翻转一次clk信号,也就是产生一个时钟周期20ns的clk,频率为50MHz  
 
          //module调用例化格式
          divide  #(.WIDTH(4),.N(11))  u1 (                         //#后面的()中为参数传递,如果不传递参数就是所调用模块中的参数默认值
                                                                   //divide表示所要例化的module名称,u1是我们定义的例化名称,必须以字母开头
						.clk	(clk),     //输入输出信号连接。 .clk表示module本身定义的信号名称;(clk)表示我们在这里定义的激励信号
						.rst_n	(rst_n),   //在testbench里定义的信号名称可以与所要调用module的端口信号名称不同
						.clkout	(clkout)   
					  );
         endmodule

引脚分配
小脚丫上的系统时钟连接到FPGA的C1脚,时钟为12MHz。你可以通过仿真波形观察分频时钟(注意仿真的时间是有限的,所以分频时钟频率需要较高)。如果我们想通过眼睛观察LED的闪烁,那么需要设置参数N和WIDTH得到一个频率较低的时钟(例如N=12000000,WIDTH=24,分频时钟周期为1秒)。

信号 引脚
clk C1
rst_n L14
clkout N13

小结:学习并了解了fpga的发展历程,对本次有关的stepfpga的使用以及lattice的diamond开发的IDE的verilog的一些简单应用。以及对功能编写测试文件进行波形测试以及将程序烧入小脚丫并实现功能。

猜你喜欢

转载自blog.csdn.net/qq_33946416/article/details/95794841