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实操训练(小功能到模块开发)的主要内容,如果未能解决你的问题,请参考以下文章