再过一段时间就要进入考试密集阶段了,所以最近总结博文更新得勤一些。后面的话还是需要暂停自学进度的。而且我发现由于我的计算错误,实际上这个系列只需要六篇文章就可以谈到串口通信的内容(完成我最初的计划),但是呢Flag既然已经立好,我预备在Ep.7的时候仔细分析一下我在这次数电系统设计课程结课汇报中,与同班同学@JXC合作的一个项目开发思路&代码。作为我FPGA入门系列的一个总结。
具体这篇文章分为两个相对前文复杂一点的实验,均与板载Led、Rst复位按键和key0-key3四个电键的操作有关。其一为按键对应流水灯点亮的一种模式设计,其二是简易自动售货机的设计。此外本章结合了Altera公司的CycloneⅣ FPGA芯片、正点原子公司设计的新起点开发板和QuartusⅡ设计综合软件与Robei的协同使用。个人认为Robei在代码与testbench的验证方面比较好用,而管脚约束、综合、生成电路图、下载验证方面还是需要依靠QuartusⅡ来玩。
一、流水灯设计:
依照以下步骤进行操作,即可完成流水灯设计的全过程,详细的代码内容依惯例贴在文章最后。
- 打开Robei,依据上篇文章提到的创建module模块文件的方式,创建一个单输入单输出口的light模块,然后填入以下代码,并运行和debug。如图所示
- 使用Create test功能创建testbench文件、保存、连线、写入以下代码,并再保存,运行debug后,产生仿真文件。如图所示
- 查看仿真波形,与真值表或者原理描述等对应即可验证是否软件级设计成功,如图所示
- 打开QuartusⅡ,注意需要先注册程序。选择新建new project wizard,选择一个全英文的文件夹路径,暂不加入任何文件,芯片选择方面通过filter搜索选择EP4CE10F17C8芯片(此处每个人自己的芯片不同型号也不同,对应选择即可),仿真工具可以不选(因为我们已经在Robei里面仿真过了),最后finish完成创建即可
- 再新建new,选择VerilogHDL file,在新建的白板文件里面复制粘贴我们在Robei里面已经写好的代码,点击工具栏倒是第十个Start Analysis & Synthesis,系统会自行编译,如图所示
- 编译完成后,点选Assignments中的Pin planner,进行程序中各个输入、输出信号的管脚分配,这就是说,我们需要把这些抽象的代码描述的信号接口,选择清楚实际在开发板上是用哪个接口来表示这个符号。99%的开发板在购买时厂家会给管脚分配的查阅表,想要用哪个按键、数码管、灯泡、传感器、排针等等就把对应的管脚号数填入到Location那一栏即可。我这里的流水灯因为是用到了四个电键,所以分别绑定了F9、E10、C11、D11四个LED管脚和四个按键管脚,别的程序或者板卡需要自己参照填写。如图所示
- 完成管脚分配后关闭界面,点击倒数第十一个Start Compilation按键,进行综合,综合完成后再Fitter栏中的Technology map viewer处可以看到电路的实际模型图如下图所示
- 点击Program Device,选择Hardware也即硬件为USB-blaster,这里需要提前将开发板和下载器USB-blaster买好,安装好驱动,并且打开开发板并将USB-blaster与FPGA电脑都连接好。之后选择add_file,选择output_files文件夹,选择对应的sof文件(Vivado对应的下载文件为.bit后缀)。如下图所示。最后点击Start即开始下载程序到FPGA中去
- 完成后,FPGA便开始依照我们程序设定开始工作了,这是我下载后板卡的样子,再没有任何按键的情况下,L4\L3两个灯亮,和程序的初衷相符。也可以按key0-key3我们同样也用到的别的按键和Rst_n复位按键,如果仿真部分没有出错,此处应当是得到和预期完全一致的效果。如图所示
以上1-9的九个步骤即是用最浅显简单的描述方式讲明了【CPLD(大规模集成电路)/FPGA芯片硬件设计】从设计师的脑海里初想法或者工程师们的手稿到最终拿出成品的步骤。虽然实例再简单不过,但是化繁为简也是我们一直需要追求的东西。例如笔者最近有在接触学习的Python AI(人脸识别等)硬件设计和信号处理系统硬件设计,其本质过程也无非上面9点,如果能将代码、电路、板级的编写、修改、验证变得和流水灯设计一样简单,那是一件多么振奋人心的事情!
二、自动售货机设计:
1、原理和思路分析:
自动售货机的信号定义:clk:时钟输入;reset:系统复位信号;half_dollar:代表投入5角硬币;one_dollar:代表投入1元硬币;half_out:表示找零信号;dispense:表示机器售出一瓶饮料。当reset=0时,售货机处于工作状态,此时连续往售货机中投硬币(可以是5角也可以是一元),投入最后一枚硬币时,如果之前投入的硬币总和为2.5元,则可以取走一瓶饮料,如果少于2.5元则继续投币,如果为3元则显示可以取出一瓶饮料,而且找零显示信号为高电平。具体的if语句思路参照表格如下,这里需要感谢@FPGA攻城狮的表格绘制
2、实际操作过程的九个步骤图例:
具体操作步骤都已经在上述流水灯中进行详解详细,以下模仿这个九步法,同时依据上述的自动售货机原理,实现这个设计。此程序的设计仍然是遵从Robei和QuartusⅡ结合的开发方式。
事实上我们点按了key1交钱之后,L1和L4强光量起,L2和L3由于没有控制弱光量起,这正是我们需要的结果。
以下是我们这次用到的两个实验的相关代码,由于Robei的存在,实际编写的时候关于模块变量声明的部分代码会被软件自动给出,仅需写核心操作部分的代码,这是我们不选择完全用QuartusⅡ设计到底的原因。
//流水灯
module light(
swt,
led);
//---Ports declearation: generated by Robei---
input [3:0] swt;
output [3:0] led;
wire [3:0] swt;
wire [3:0] led;
//----Code starts here: integrated by Robei-----
assign led[0]=~swt[0];
assign led[1]=swt[1]&~swt[2];
assign led[3]=swt[2]&swt[3];
assign led[2]=(swt[1]&~swt[2])|(swt[2]&swt[3]);
endmodule //light
//自动售货机
module sell(
clk,
rst,
one_dollar,
half_dollar,
half_out,
dispense);
//---Ports declearation: generated by Robei---
input clk;
input rst;
input one_dollar;
input half_dollar;
output half_out;
output dispense;
wire clk;
wire rst;
wire one_dollar;
wire half_dollar;
reg half_out;
reg dispense;
//----Code starts here: integrated by Robei-----
parameter idle=0,half=1,one=2,one_half=3,two=4;
reg[2:0] D;
always @(posedge clk)
begin
if(!rst)
begin
dispense=0;
half_out=0;
D=idle;
end
case(D)
idle:
if(!half_dollar)
D=half;
else if(!one_dollar)
D=one;
half:
if(!half_dollar)
D=one;
else if(!one_dollar)
D=one_half;
one:
if(!half_dollar)
D=one_half;
else if(!one_dollar)
D=two;
one_half:
if(!half_dollar)
D=two;
else if(!one_dollar)
begin
dispense=1;
D=idle;
end
two:
if(!half_dollar)
begin
dispense=1;
D=idle;
end
else if(!one_dollar)
begin
dispense=1;
half_out=1;
D=idle;
end
endcase
end
endmodule //sell
//流水灯testbench
module light_tb();
reg [3:0] switches;
wire [3:0] leds;
//----Code starts here: integrated by Robei-----
initial
begin
#5 switches=4'b1111;
#5 switches=4'b1110;
#5 switches=4'b1101;
#5 switches=4'b1010;
#5 switches=4'b1011;
#5 switches=4'b0110;
#5 switches=4'b0101;
#5 switches=4'b0110;
#10 $finish;
end
initial begin
$dumpfile ("D:/Robei/RobeiJoey_Project/light/light_tb.vcd");
$dumpvars;
end
//---Module instantiation---
light light1(
.swt(switches),
.led(leds));
endmodule //light_tb
//自动售货机testbench
module sell_tb();
reg clk;
reg rst;
reg one_dollar;
reg half_dollar;
wire half_out;
wire dispense;
//----Code starts here: integrated by Robei-----
initial begin
one_dollar=0;
half_dollar=0;
rst=0;
clk=0;
#100 rst=1;
repeat(2)@(posedge clk);
#2 one_dollar=1;
repeat(1)@(posedge clk);
#2 one_dollar=0;
repeat(2)@(posedge clk);
#2 one_dollar=1;
repeat(1)@(posedge clk);
#2 one_dollar=0;
repeat(2)@(posedge clk);
#2 one_dollar=1;
repeat(1)@(posedge clk);
#2 one_dollar=0;
#20 rst=0;
#100 rst=1;
repeat(2)@(posedge clk);
#2 one_dollar=1;
repeat(1)@(posedge clk);
#2 one_dollar=0;
repeat(2)@(posedge clk);
#2 one_dollar=1;
repeat(1)@(posedge clk);
#2 one_dollar=0;
repeat(2)@(posedge clk);
#2 half_dollar=1;
repeat(1)@(posedge clk);
#2 half_dollar=0;
#20 rst=0;
#5 $finish;
end
always #10 clk=~clk;
initial begin
$dumpfile ("D:/Robei/RobeiJoey_Project/sell/sell_tb.vcd");
$dumpvars;
end
//---Module instantiation---
sell sell1(
.clk(clk),
.rst(rst),
.one_dollar(one_dollar),
.half_dollar(half_dollar),
.half_out(half_out),
.dispense(dispense));
endmodule //sell_tb