fpga实操训练(小功能到模块开发)

Posted 嵌入式-老费

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了fpga实操训练(小功能到模块开发)相关的知识,希望对你有一定的参考价值。

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

        有过c、java编程语言经验的朋友,是否还记得曾经自己是如何学习编程语言的?一开始的时候,为了学习语法,我们练习的代码可能都比较少。主要是一些简单的逻辑判断、循环和小算法之类的内容。等到这一切都ok之后,就开始慢慢编写一些复杂的功能了。这个时候,如果再将所有的代码集中在同一个源文件当中,就会显得非常臃肿了。我至今还记得,当初硕士论文中设计的代码,就是全部放在一个文件中的,前前后后大约有5000行左右。

        回到今天的主题,其实编写verilog也是一样的。一方面,曾经的小功能可以变成独立的模块,方便我们将来调用;另外一方面,对于相似的逻辑内容,也可以抽象成单独的一个模块来处理;最后,也是很重要的一点,我们要学会利用别人开发好的模块,这样可以快速拓展自己的能力边界,这点有点类似于使用第三方SDK。只是,纯软件模块和verilog不一样的地方,就是每一次模块实例化都是实实在在的电路,这和函数调用几乎不花成本是完全不一样的。

        为了说明如何做一个基于模块开发的项目,我们可以写一个简单的计时工具。即,上电后,数码管开始计时,每1s计时一次,一共可以累计到99999秒,再重新恢复为0。

1、准备进位代码


module add_bit(clk, rst,in, value, over);

input in;
input clk;
input rst;
output[3:0] value;
output over;

wire in;
wire clk;
wire rst;
reg[3:0] value;
reg over;

always@(posedge clk or negedge rst)
	if(!rst)
		value <= 4'd0;
	else if(in) begin
		if(value == 4'd9)
			value <= 4'd0;
		else 
			value <= value + 1'b1;
	end

always@(posedge clk or negedge rst)
	if(!rst)
		over <= 1'b0;
	else if(over)
		over <= 1'b0;
	else if(in == 1 && value == 4'd9)
		over <= 1'b1;
		
endmodule

        所谓进位代码,就是看有没有进位传上来,有没有进位送出去,当前应该显示的数据是什么。有了这么一个进位,其实就构成了基本的6位数显示方法。

2、数码管的显示

        前面的章节,我们说过。一个6节的数码管,会有6个sel信号,8个数据信号。但是,6个数码管每一个都有自己的数字,这应该如何处理?想来想去,唯一可行的办法就是通过快速刷新,利用人的视觉残留效应,误以为数码管的显示是连贯的。


module seg_select(clk, rst, seg_sel);

input clk;
input rst;
output[5:0] seg_sel;

wire clk;
wire rst;
reg[5:0] seg_sel;

reg[31:0] sel_count;

// select data
always@(posedge clk or negedge rst)
	if(!rst)
		sel_count <= 32'b0;
	else if(sel_count != 32'd4_9999)
		sel_count <= sel_count + 1;
	else
		sel_count <= 32'b0;

always @(posedge clk or negedge rst)
	if(!rst)
		seg_sel <= 6'b011111;
	else if(sel_count == 32'd4_9999)
		seg_sel <= seg_sel[0],seg_sel[5:1];

endmodule

3、数码管的解码

        相比较而言,数码管的解码就很简单的。主要是根据需要显示的数字,点亮数码管上面不同部分的led灯。


module seg_decode(clk, rst, seg_sel, num0, num1, num2, num3, num4, num5, seg_data);

input clk;
input rst;
input[5:0] seg_sel;
input[3:0] num0;
input[3:0] num1;
input[3:0] num2;
input[3:0] num3;
input[3:0] num4;
input[3:0] num5;
output[7:0] seg_data;

wire clk;
wire rst;
wire[5:0] seg_sel;
wire[3:0] num0;
wire[3:0] num1;
wire[3:0] num2;
wire[3:0] num3;
wire[3:0] num4;
wire[3:0] num5;

reg[3:0] num;
reg[7:0] seg_data;


always@(*)
	case(seg_sel)
		6'b011111: 
			num <= num0;

		6'b101111:
			num <= num1;

		6'b110111:
			num <= num2;

		6'b111011:
			num <= num3;

		6'b111101:
			num <= num4;
	
		6'b111110:
			num <= num5;

		default:
			num <= 4'd0;
	endcase

// decode data
always@(posedge clk or negedge rst)
	if(!rst)
		seg_data <= 8'b1100_0000;
	else
		case (num)
			4'd0: 
				seg_data <= 8'b1100_0000;				
			4'd1: 
				seg_data <= 8'b1111_1001;
			4'd2: 
				seg_data <= 8'b1010_0100;
			4'd3: 
				seg_data <= 8'b1011_0000;
			4'd4: 
				seg_data <= 8'b1001_1001;
			4'd5: 
				seg_data <= 8'b1001_0010;
			4'd6:
				seg_data <= 8'b1000_0010;
			4'd7:
				seg_data <= 8'b1111_1000;
			4'd8:	
				seg_data <= 8'b1000_0000;
			4'd9:
				seg_data <= 8'b1001_0000;
				
			default: 
				seg_data <= 8'b1100_0000;
		endcase
endmodule

4、模块的整合

        有了上面3个模块的编写,下面就可以开始他们的整合了,一起构建一个完整的功能模块。整个过程并不复杂,大家看一下代码就了解相关的流程了。

        模块中的调用顺序是这样的,

        代码的组成是这样的,


module seg_test(clk, rst, seg_sel, seg_data);

input clk;
input rst;
output[5:0] seg_sel;
output[7:0] seg_data;

wire clk;
wire rst;
wire[5:0] seg_sel;
wire[7:0] seg_data;

reg[31:0] count;

wire[3:0] num0;
wire in0;
wire over0;

wire[3:0] num1;
wire in1;
wire over1;

wire[3:0] num2;
wire in2;
wire over2;

wire[3:0] num3;
wire in3;
wire over3;

wire[3:0] num4;
wire in4;
wire over4;

wire[3:0] num5;
wire in5;
wire over5;


// from seg selector

seg_select seg_select0(
	.clk(clk),
	.rst(rst),
	.seg_sel(seg_sel)
	);


// from basic		
		
always@(posedge clk or negedge rst)
	if(!rst)
		count <= 32'b0;
	else if(count != 32'd4999_9999)
		count <= count + 1;
	else
		count <= 32'b0;


// use add_bit

add_bit add_bit0(
	.clk(clk),
	.rst(rst),
	.in(count == 32'd4999_9999),
	.value(num0),
	.over(over0)
	);		
		

add_bit add_bit1(
	.clk(clk),
	.rst(rst),
	.in(over0),
	.value(num1),
	.over(over1)
	);	
	
add_bit add_bit2(
	.clk(clk),
	.rst(rst),
	.in(over1),
	.value(num2),
	.over(over2)
	);	
	
add_bit add_bit3(
	.clk(clk),
	.rst(rst),
	.in(over2),
	.value(num3),
	.over(over3)
	);	
	
add_bit add_bit4(
	.clk(clk),
	.rst(rst),
	.in(over3),
	.value(num4),
	.over(over4)
	);	
	
add_bit add_bit5(
	.clk(clk),
	.rst(rst),
	.in(over4),
	.value(num5),
	.over(over5)
	);	
		

// choose num	
	
seg_decode seg_deocode0(
	.clk(clk),
	.rst(rst),
	.seg_sel(seg_sel),
	.num0(num0),
	.num1(num1),
	.num2(num2),
	.num3(num3),
	.num4(num4),
	.num5(num5),
	.seg_data(seg_data)
	);

endmodule


5、RTL显示

        综合完毕后,如果大家有兴趣需要看下RTL综合后的效果,可以选择“RTL Viewer”,

        如图所示,上图中绿色的部分就是子模块。如果需要了解里面的细节,点击模块左上方的田字格即可,类似于这样,

6、pin脚bind

7、所有这些都完成值之后,就可以开始实验了

题外话:

        很多时候,模块的拆解整合是需要反复进行的,并不意味着马上就可以做对。来来去去、反反复复都是常有的事情。不过还是要相信自己,每一次的思考、每一次的修正都会让自己有所改进的。

以上是关于fpga实操训练(小功能到模块开发)的主要内容,如果未能解决你的问题,请参考以下文章

fpga实操训练(从模块到系统开发)

fpga实操训练(从模块到系统开发)

fpga实操训练(系统开发和硬件接口)

fpga实操训练(系统开发和硬件接口)

fpga实操训练(锁相环pll)

fpga实操训练(锁相环pll)