2024年1月23日发(作者:)
实验一 ISE工具的使用流程--拨码开关控制LED实验
1.1 实验目的
1.学会ISE的基本开发流程和常用功能的使用,本实验直接使用新建一个拨码开关控制led
实验来作为设计文件,通过ise 综合、映射、布局布线后,生成FPGA位流配置文件,通过JTAG口对开发板上FPGA进行配置。
2学会最基本拨码开关和led 工作原理。
1.2 实验原理
1. 实验开发板的拨码开关向上拨动时处于低电平,向下处于高电平,用此来控制LED灯。
2. LED灯的的一端已经接高电平,另一端接FPGA的IO口,因此当IO输出低电平是便可点亮LED灯,否则LED为暗。
3. 按键默认为高电平,按键按下时接地为低电平来检测按键的按下的复位信号。
1.3 实验步骤
1. 打开ISE应用程序,进入图形化界面
图表 1
2. 点击File->New project,在弹出的对话框中设定工程和工程路径,用HDL源码,NEXT
1
图表 2
3. 选定器件和封装,点击NEXT.
图表 3
4. 在工程中创建源文件,选择New Source.,选中Verilog Module,输入源文件名称
图表 4
2
5. 可在弹出的对话框中输入信号的输入输出定义,也可暂时不定义
图表 5
6.点击下一步,点击finish,然后自动回到 creat a new source 对话框,点击下一步,再击下一步,然后点击finish 。
图表 6
6. 点击设计的源文件,然后整个界面如图所示
图表 7
3
7. 将鼠标置于输入输出的下方,点击工具栏中的,然后进入各子目录选择如下,这是一个快捷操作模板。右键use in file,可在源文件中看到已经添加相应的模板,然后修改一些端口、添加内容完善整个工程设计。
图表 8
8. 写源文件的代码如下
module key_led(clk, key, reset_n, led);
input clk;
input key;
input reset_n;
output led;
reg led;
// Usage of asynchronous resets may negatively impact FPGA resources
// and timing. In general faster and smaller FPGA designs will
// result from not using asynchronous resets. Please refer to
// the Synthesis and Simulation Design Guide for more information.
always @(posedge clk or negedge reset_n)
if (!reset_n) begin
led <= 1;
end
else begin
if(key==0)
led <=0;
else
led <=1;
end
endmodule
4
可用design Utilities点击create schematic symbol下观看所生成的原理图来分析电路的性能。
9. 添加约束文件,source for 选择在 synthesis….,选中源文件,右键new source,选择如图所示,输入文件名,点击next,然后finish.可以看到已经添加到工程中。
图表9
10. 添加测试文件,在source for 选择在behavioral….,new source,然后选择Verilog test fixture,建立测试文件。Next,选择关联key_led,点击完成
5
图表 10
11.编写测试文件代码如下:选中测试文件,在processes中的modelsim simulator 中的下拉菜单中双击simulate….这样就可以仿真了。(注意在仿真之前要进行xilinx library库的编辑,方法见附录1)
module key_led_tb_v;
// Inputs
reg clk;
reg key;
reg reset_n;
// Outputs
wire led;
// Instantiate the Unit Under Test (UUT)
key_led uut (
.clk(clk),
.key(key),
.reset_n(reset_n),
.led(led)
);
always #5 clk=~clk;
initial begin
// Initialize Inputs
clk = 0;
key = 1;
reset_n = 1;
// Wait 100 ns for global reset to finish
#100 key=0;
#100 reset_n=0;
#100 reset_n=1;
#100 key=1;
#100 $stop;
// Add stimulus here
6
end
endmodule
图表 11
12.管脚分配,回到sythesis…下,选中ucf 文件,在process中双击打开 assign package
Pins,按照管脚分配图进行相应配置,然后保存。
图表 12
13.回到主界面选中HDL设计文件,双击generate…,然后修改到成功没有错误为止。如下图所示
图表 13
7
14.生成Bit文件,在generate….下拉菜单中,双击第二个,进行JTAG下载配置(先进行实验板上的下载硬件配置连接)。然后会提示成功与否
图表 14
15.进行开发板验证。实验步骤结束
1.4 实验结果分析
1. 仿真如图所示
图表 15
2. 通过实验板验证可以看到,当讲拨码开关拨向ON时,灯亮,拨向OFF时暗,当不论在
8
3.
4.
5.
1)
2)
3)
4)
附录1:xilinx library 编译方法两种(选其中一种):
(一)、在ISE Project Navigator中编译仿真库
1、打开文件: 安装路径:Modeltech_6.2b,选中文件夹中的配置文件 ,右击,将文件属性中的只读去掉,变成可写入的,接着用写字板程序打开;
2、在sources窗口中选择设计的目标器件 xc3s500e-4pq208;在processes窗口中会出现design
Utilties,选择Compile HDL Simulation Libraries项。单击右键,在下拉菜单中选中Properties命令,打开对话框如图所示。
什么状态下,按下复位键则灯立即变暗,直至松手。因此简单本设计符合设计预期的要求。
通过本实验,了解了最基本的硬件知识和建立一个工程,代码编写、综合、约束、分析、调试、下载观察等过程。
通过本次实验,进一步了解了FPGA工程使用的一个简单流程,在实验过程中需要认真做好每一个环节,配合实验说明书找到解决方法。
实验过程中遇到的问题和解决方法:
生成的bit 文件后,下载实验现象没有变化:可能保存的路径错了,导致屡试屡败
无法正常显示窗口:修改桌面系统主题,该回经典xp等默认主题,这是由于EDK与操作系统的一些不兼容造成的。
综合错误:修改成可综合代码。
有警告:可稍微看一下警告的内容,但这是正常现象。
9
3、Process Properties按如图选择,单击ok按钮关闭对话框。
4、双击Compile HDL Simulation Libraries,程序会自动完成对库文件的编译。
5、观看是否在文件中加入以下部份。
; Verilog Section
unisims_ver = 库文件所在地址/unisims_ver
uni9000_ver = 库文件所在地址/uni9000_ver
simprims_ver = 库文件所在地址/simprims_ver
xilinxcorelib_ver = 库文件所在地址/xilinxcorelib_ver
aim_ver = 库文件所在地址/abel_ver/aim_ver
cpld_ver = 库文件所在地址/cpld_ver
; VHDL Section
unisim = 库文件所在地址/unisim
simprim = 库文件所在地址/simprim
xilinxcorelib = 库文件所在地址/xilinxcorelib
aim = 库文件所在地址/abel/aim
pls = 库文件所在地址/abel/pls
cpld = 库文件所在地址/cpld
6、再将设为只读即可
7、可采用modelsim进行仿真了。
(二)、另一种方法
1. 在桌面上点击开始――运行――输入cmd,进入dos.
2. 在dos下输入:compxlib –s mti_se –arch all –l all –dir %xilinx%/lib –w
3. 等编译完成后便可在安装目录下的lib中生成库,将生成的库加入modelsim中。
1) 方法:打开文件: 安装路径:Modeltech_6.2b,选中文件夹中的配置文件 ,右击,将文件属性中的只读去掉,变成可写入的,接着用写字板程序打开;
2) 在中加入一下内容(库文件所在地址为 -> 安装路径:Xilinx92ilib,
10
比如有的同学是装在D盘,那就是:D:Xilinx92ilib , 这一步完成后将再设为只读)
; Verilog Section
unisims_ver = 库文件所在地址/unisims_ver
uni9000_ver = 库文件所在地址/uni9000_ver
simprims_ver = 库文件所在地址/simprims_ver
xilinxcorelib_ver = 库文件所在地址/xilinxcorelib_ver
aim_ver = 库文件所在地址/abel_ver/aim_ver
cpld_ver = 库文件所在地址/cpld_ver
; VHDL Section
unisim = 库文件所在地址/unisim
simprim = 库文件所在地址/simprim
xilinxcorelib = 库文件所在地址/xilinxcorelib
aim = 库文件所在地址/abel/aim
pls = 库文件所在地址/abel/pls
cpld = 库文件所在地址/cpld
11
实验二 用Core Generator生成DCM并进行仿真验证
2.1 实验目的
学会用core generate 生成dcm ,并使用modelsim进行仿真。
2.2 实验原理
是数字时钟管理器,它在xilinx的FPGA的设计中应用广泛,dcm 可以产生输入时钟的一倍频、二倍频,或M/D倍频等功能,也可以产生90、180、270度相移的时钟,例如FPGA输入时钟为66M,而我们的系统需要133M的时钟,则可以用到DCM二倍频。
2.编译库,将库文件加入modelsim中,然后可以进行联合仿真调试。
2.3 实验步骤
1.在桌面上点击开始-》程序—》xilinx ise10.1—ISE》accesseries—》Core generator,界面如下:
图表 16
2.单击file->new project,系统出现如下画面,然后选择工程路径和名字。然后next,设置工程的基本参数:part参数设置,generation参数,advanced,然后点击OK。
12
图表 17
3.完成新建工程向导后,按顺序展开FPGA features and design-》clocking-》spartan_3e,以看到DCM_SP,双击single DCM_SP。
13
可
图表 18
4.双击Single DCM_SP后,进入dcm 生成向导,设定名称为dcm_test.,点击ok后,进入下一界面进行如下图的配置。
图表 19
5.下一步进入下图所示的界面,设定完成后点击next.
区域一:DCM_SP的基本模型,根据实际情况在需要的输入输出上打勾;
区域二:设定输入DCM的时钟频率,或者是周期,根据需要选择一种;
区域三:设定DCM的输出时钟和输入时钟的相对位移,一般情况下不进行设置;
区域四:选择输出时钟源,是FPGA外部输入,还是内部时钟信号,一般第一级的dcm 是外部输入,二级、三级dcm 是前一级的dcm 输出,是内部信号;
区域五:时钟反馈类型,是外部还是内部反馈,一般是直接使用DCM的clk0或者clk2x做内部反馈,特殊情况需要外部输入的时钟进行反馈,但很少使用;
区域六:设定clkdv的分频数,即相对于clkin的分频数,如图选择的值为5,则50Mhz的输入进行五分频,那么clkdv输出为10Mhz。如果没有勾clkdv输出,则不需要进行参数设置;
14
区域七:反馈类型选择,可以是时钟的1或者2分频。
区域八:时钟duty cycle的矫正,默认选上,可以更好的占空比为50%的时钟信号。
图表 20
6.下一步进入clock buffer 的设置,默认使用个咯罢了global buffer。点击next
图表 21
7.下一步设置的是clkfx的时钟输出,一般采用设置M和D的值来控制clkFX的输出,计算公式为clkfx=clkin*(m/d),输入预计好的M、D值,点击calculate即可得到clkfx的输出
15
频率,如果最开始没有选择clkfx输出,则可以跳过。
图表 22
8.点击next,点击finish,完成DCM的生成。完成后的界面如图所示。
图表 23
9.在ise 里面新建工程,将生成的dcm_test拷贝到工程目录下,并添加到工程文件中。
建完后如下图所示
16
图表 24
观看综合后的原理图方法为:
17
10.给dcm_test添加测试文件,具体过程可参考实验一,建完后修改补充测试文件如下(
这里的dcm为高电平复位):
`timescale 1ns / 1ps
`define clk_cycle 15
////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
// Create Date: 18:42:43 01/02/2010
// Design Name: dcm_test
// Module Name: E:/xilinx/project/lab2/dcm_11M/dcm_11M_tb.v
// Project Name: dcm_11M
// Target Device:
// Tool versions:
// Description:
// Verilog Test Fixture created by ISE for module: dcm_test
// Dependencies:
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
///////////////////////////////////////////////////////////////////////////////
module dcm_11M_tb_v;
// Inputs
reg CLKIN_IN;
reg RST_IN;
// Outputs
wire CLKDV_OUT;
wire CLKFX_OUT;
wire CLKIN_IBUFG_OUT;
wire CLK0_OUT;
wire LOCKED_OUT;
// Instantiate the Unit Under Test (UUT)
18
dcm_test uut (
.CLKIN_IN(CLKIN_IN),
.RST_IN(RST_IN),
.CLKDV_OUT(CLKDV_OUT),
.CLKFX_OUT(CLKFX_OUT),
.CLKIN_IBUFG_OUT(CLKIN_IBUFG_OUT),
.CLK0_OUT(CLK0_OUT),
.LOCKED_OUT(LOCKED_OUT)
);
always #(`clk_cycle/2) CLKIN_IN=~CLKIN_IN;
initial begin
// Initialize Inputs
CLKIN_IN = 0;
RST_IN = 0;
// Wait 100 ns for global reset to finish
#500 RST_IN = 1;
#500 $stop;
end
#100 RST_IN = 0 ;
endmodule
11.添加管脚时许约束文件,先建立约束文件,然后双击creat timingconstraints—》global—》双击clock——in的period,如图所示进行配置,然后保存退出。
19
图表 25
12.调用modelsim进行联合仿真,实验结束,可对DCM设置参数进行修改不断改变波形。
图表 26
13.综合后几个常用的工具,如观察RTL级的电路结构和门级结构的原理图
20
图表 27
2.4 实验结果分析
1.调用modelsim的仿真结果如下
图表 28
结果分析:由于本实验在区域六选择的是6分频,clkfx也是六分频,无位相移动因此可以从波形很直观的看出实验结果。即clkfx和clkdv都是六分频和clkin是同相位的
21
2.为了得到其他的频率输出可以对DCM参数进行修改,如将clkdv改成3分频后的仿真波形。可以看到clkdv输出变成了3分频,而clkfx不做改变
图表 29
3.将clk180选中,将clkfx180选中,两者都是六分频,仿真波形如图所示,从图形可以看出实验结果与预期的相符合。Clkfx180和clk180与clkfx和clkdv是反相关系。
图表 30
4. 实验心得和体会:通过本次实验操作,初步了解了利用DCM产生我们所需要的波形的基本流程,也对DCM产生波形的利用有了更进一步的了解。为下下一步的进一步深入
22
学习起到一个好的开始。
5. 实验过程中存在的问题和解决方法
1) 无法在ise 里面调用modelsim进行仿真:可能是没有编译xilinx库文件,或者编译后并没有在modelsim中进行添加。若还是不行,先进行常规仿真,看是否有软件有问题,有问题进行再次重装。
2) 修改了DCM参数后为何仿真波形没有改变:首先确认是否是将生成的dcm_test.v拷贝至工程目录再添加,还是直接从dcm 工程里面的文件添加到ise工程文件中,拷贝的需要再次拷贝更新后的文件到工程中再添加,若是后者只要更新工程即可,若更改了输入输出需要对测试文件重新关联。
3) 如何更直观的去查看自己写出来的代码综合后的电路图:ise提供了可观察综合后的RTL级电路结构和门级的电路结构原理图,可以让设计人员通过更直观的的原理图来观察自己所写代码的原理图,这样更能加深对硬件描述语言的了解。
23
实验三 按键检测实验
3.1 实验目的
1.学会鉴别信号的上升沿和下降沿,熟练掌握信号的检测。
2.学会按键检测原理,熟练掌握按键信号的检测,掌握和理解按键抖动对信号检测的影响。
3.2 实验原理
3.2.1 信号检测原理
信号的采集就是对信号的上升沿和下降沿进行采集,上图是一个信号enable,它变为高电平几个周期后再变为低电平。首先对信号的enable进行一拍时钟采样,也就是一个时钟周期的延迟,经过处理后的信号enably_dly是enable的一个延迟一个时钟周期后的信号,如图所示。
图表 31
3.2.2 键盘检测原理
按键检测原理是:当按键按下时,输入电平由高变低,按键松开后,输入电平有低变高,如此会产生一个下降沿和一个上升沿,只要扑捉到这两个沿,就可以确定按键按下。
3.2.3 键盘防抖原理
在按键被按下的瞬间,由于物理上的原因,导致按键在被按键的瞬间的过程中其实是处于高电平和低电平之间进行抖动,加上本实验的时钟频率很高,容易产生信号采集错误,导致检测失败,无法得到正确的结果。若要正确防抖,需要在检测到按键上升沿后延迟一小段信号在进行上升沿和下降沿检测。
3.3 实验步骤
1.建立ise 工程,建立按键检测文件和测试文件如图所示:
24
图表 32
键盘检测程序如下:
module key_scan(clk,reset_n,key,led);
input clk;
input reset_n;
input key;
output led;
reg key_dly;
reg key_dly1;
reg key_pose_flag;
reg key_nege_flag;
reg key_pulse;
reg led;
always @(negedge reset_n or posedge clk)
begin
if(reset_n==1'b0)
key_dly<=1'b0;
else
key_dly<=key;
end
always @(negedge reset_n or posedge clk)
begin
if(reset_n==1'b0)
key_dly1<=1'b0;
else
key_dly1<=key_dly;
end
always @(negedge reset_n or posedge clk)
begin
if(reset_n==1'b0)
25
key_nege_flag<=1'b0;
else
if(key_dly==1'b0&&key_dly1==1'b1)
key_nege_flag<=1'b1;
else if(key_pulse==1'b1)
key_nege_flag<=1'b0;
end
always @(negedge reset_n or posedge clk)
begin
if(reset_n==1'b0)
key_pose_flag<=1'b0;
else
if(key_nege_flag==1'b1)
begin
if(key_dly==1'b1&&key_dly1==1'b0)
key_pose_flag<=1'b1;
else if(key_pulse==1'b1)
key_pose_flag<=1'b0;
end
end
always @(negedge reset_n or posedge clk)
begin
if(reset_n==1'b0)
key_pulse<=1'b0;
else
if(key_pose_flag==1'b1&&key_nege_flag==1'b1)
key_pulse<=~key_pulse;
else
key_pulse<=key_pulse;
end
always @(posedge clk or negedge reset_n)
begin
if(reset_n==1'b0)
led <=1'b1;
else
if(key_pulse==1'b1)
led <=~led;
end
endmodule
26
测试文件:
`include"./key_scan.v"
`timescale 1ns/1ns
module key_scan_t;
reg clk,key,reset_n;
wire led;
key_scan test(.clk(clk),.key(key),.reset_n(reset_n),.led(led));
always #1 clk=~clk;
initial
begin
reset_n=0;key=1;clk=0;
#10 reset_n=0;
#10 reset_n=1;
#10 key=0;
#10 key=1;
#10 key=0;
#10 reset_n=0;
#10 reset_n=1;
#10 key=1;
#10 key=0;
#10 key=1;
#10 key=0;
#10 key=1;
#10 key=0;
#10 key=1;
#10 key=0;
#10 key=1;
#100 $stop;
end
endmodule
2.对键盘检测文件进行综合和仿真。综合结果如图所示
图表 33
门级的原理图如下:
27
图表 34
3.在ise 工程中添加ucf 文件,完成管脚分配布局布线生成FPGA的bit文件,下载到实验板进行检测
图表 35
4.下载到实验板进行实验,然后修改至正常检测。
3.4 实验结果分析
1 .键盘检测仿真波形
28
图表 36
波形分析:
通过波形我们可以看出,在经过检测键盘按下的下降沿和上升沿得到上升标志和下降标志延迟了将近2个时钟周期,在键盘松手后,产生一个时钟周期的脉冲信号使灯的状太相反,如波形所示,本程序能较好的完成了键盘的检测。
2. 心得与体会:通过本次实验,使我进一步明白了键盘检测的原理,通过实验想象进行更直观的学习键盘检测。同时通过本次实验,让我充分感受到在进行实验板调试之前的功能仿真的重要性。信号的检测使我不会再滥用信号的边沿触发,纠正了我进行代码便携的一大误区,同时也加深理解了硬件语言和软件语言的区别。
3. 实验过程中存在的问题和解决方法。
1)综合无法通过提示混合信号赋值:大有可能是因为进行了在统一always模块当中进行了阻塞和非阻塞同时赋值的情况,这是不可综合的。
2)按键按下后,灯就一直亮着,只能按下复位键让其变暗:这是因为实验指导书的范例程序没有对灯点亮信号进行翻转,只要检测到一次键盘按下便进行点亮保持。
3)发现键盘有时感觉不灵敏:这是由于按键在按下的过程中会发生抖动的现象,加上实验开发板的时钟频率比较大,因此需要加上一段延迟才能更准确的消除抖动,因此本实验需要在后续的学习和实践中进一步的晚上,才能写出一个较完美的键盘检测消抖程序。
实验四 LCD数字秒表
29
4.1 实验目的
1. 学会比较大型的数字系统的设计方法和步骤
2. 掌握LCD的控制协议和时序工作状态
3. 掌握可综合代码的编写和仿真
4. 掌握利用状态机来描述接口协议和状态转移控制
5. 熟练掌握分频器的设计
6. 掌握整个系统设计的初步能力和方法
4.2 实验原理
1. LCD工作时序如下图所示,并有相对应的时间约束,由于本实验只用到写时序,因此只介绍读的工作时序。下图是整个LCD的写数据和命令的控制时许,在本实验中需要编写一个lcd_ctrl模块文件来描述此时序要求来发送数据。
图表 37
2. LCD工作控制原理,在本实验中需要将数据写道LCD的内部的寄存器当中,达到控制显示的要求。本实验开发板中由于选择四位的传送模式,因此须在初始化过程的最开始进行设定其中需要在开机后进行LCD初始化操作,然后再不断的进行数据传输扫描显示,初始过程可参考如下:
30
图表 38
3. LCD接口功能定义如下:
图表 39
4.本试验程序的设计方法和流程
1)本实验程序将整个LCD数字秒表系统分成三大模块,分别是LCD时序发生控制模块,数据和命令读写控制状态模块,数字时钟信号产生模块,数据和命令读写控制模块调用其他
31
两个模块实现整个数字系统,系统框图如下:
Pauselcd_rs_inlcd_rdcelcd_rs时钟发生模块MinuSec数据控制发送模块lcd_wrcelcd_db_inLCD时序控制模块lcd_rwlcd_dbMslcd_acklcd_en
图表 40
2)时钟发生器程序设计流程
时钟发生器由两部分构成,一个是功能键盘,一个是时钟计数器,的时钟由外部晶振提供,第一级产生周期为10ms的时钟进行ms计数,产生百位和十位,百位十位采用十进制计数方式便于数据的输出,分别产生4位的数据,每一个数字为的4位与30H拼接成8位,构成ASCII输出。同时长生S时钟,由秒时钟产生分钟时钟,采用和ms相类似的计数方式,输出都是八位二进制数。流程如下:
键盘检测外部时钟是否暂停计数暂停10ms计数器10毫秒时钟产生毫秒百位和十位输出秒时钟产生秒的十位和个位输出分钟时钟产生图表 41分钟的十位和个位输出
3)LCD时序控制模块的设计
LCD时序控制主要是为了产生和LCD接口协议和时序相吻合的时序,根据时序图可以得出如下的状态流程:
32
idlerstsetupenableholdfinish
图表 42
设计流程:
Idle读写使能与否?否Setup建立时间完成与否?否时钟计数器Enable使能时间完成与否?否Hold保持时间完成与否否Finish产生应答信号图表 43
4)数据发送控制模块
本模块起到协调各模块的作用来控制写数据和写命令,状态机如图所示,首先进行初始化,然后进行数据传输数字跑表的显示,循环进行扫描显示。
33
idlecur_state~1function_setwaitsdisp_ondisp_clearentry_mode_setputcharfinish
图表 44
设计流程如下:
IdleFunction_setDis_onwaitDis_clearEntry_model_setputcharfinish
图表 45
5. LCD数据传送格式。
本实验程序设计将数据一次十位数据在读写使能触发后,锁存进入LCD时序模块,防止过程数据变化带来的读写数据错误。低八位为控制命令数据或者显示命令数据,第九位是控制写命令还是写数据的RS信号,第十位控制是读数据还是写数据,由于本实验只涉及写数据,因此整个过程默认为低电平及写数据。
34
在LCD初始化后,状态机便进入数字跑表的数据显示传送。在这个传送的过程中将要显示的8个字符组合成一个字符串首位串接10'b及在第五个位置开始显示,当显示一串完毕后,再次写入控制数据返回到写显示第一个字符的地方重新刷新显示。整个字符串内容如下: 2'b00,8'h84,2'b01,minu_h,2'b01,minu_l,2'b01,8'h3a,
2'b01,sec_h,2'b01,sec_l,2'b01,8'h3a,2'b01,ms_h,2'b01,ms_l,2'b00,8'h84
6. 键盘检测
键盘检测与实验三的原理相似,这里不再详述
7. 状态机流程控制
本实验共有两个状态机,状态机的转移由相对应的标志信号来控制举例如下:
对于function_set状态来说,当进入这个状态后,将此状态需要延迟的时钟信号付给计数器,生成function_set_ok标志信号,然后转向wait状态,开始发出发送数据命令,在nibble标志位的作用下先发送高四位,LCD时序控制模块产生发送完毕应答信号后进入发送低四位,当再次接到接到应答信号时,转入下一状态。具体可参考附录程序。
4.3 实验步骤
1. 先在modelsim进行程序的设计,分模块进行编写和测试,如键盘检测、时钟发生器、时序控制模块、读写数据发送模块等并分别进行测试,将模块合成一个系统进行代码编写测试。
然后按照LCD时序和预期状态机来观察波形是否符合设计要求,并不断进行代码修改。
2. 在ise 里面间建好工程,添加已经功能仿真通过的程序源文件。
如下图所示
图表 46
3.进行代码综合,修改代码,并进行再次调试,综合和功能通过后,查看门级电路结构
下图为整个结构图
35
图表 47
4. 建立约束文件进行、管脚分配布局布线,生成bit文件
图表 48
5. 通过JTAG口下载实验代码进行实验观察,之后进行实验程序的改进。
36
4.4 实验结果分析
1. 仿真结果波形功能验证:
1)下图显示的刚开始的时候,初始化后即将06H命令传送完后第一次扫描输出的结果如图所示:84H,30h,30h,3ah,30h,30h,3ah,30h,30h,84h。验证了输出的可靠性。
图表 49
2)如图显示的是数字秒表显示为00:00:07的状态
图表 50
4) 如图显示的是一个扫描周期的LCD显示00:01:12
37
图表 51
2. 仿真波形的时序验证,及LCD时序模块的波形,由于一直处于写模式,所以lcd_rw一直为低电平,当触发时能信号wrce来临后,整个读写时序开始形成,经图形可验证时序控制模块正常工作。
图表 52
4. 时钟发生器的波形仿真,从时钟计数器的波形可以看出,时钟计数正常工作。
图表 53
5. 周期性变化的高八位和低四位数据交换标志,以及8位传送结束应答信号
图表 54
6. 其余的众多信号都可以通过波形得到验证。
7. 实验过程中遇到的问题和解决方法
1) 功能仿真通过了,但LCD还是无法正常显示:首先检查LCD是否正常工作,其次检验
38
程序的各类接口约束时间与LCD手册是否相符
2) LCD重复多次显示多段显示时钟:这可能是由于没有进行写地址为操作造成的
3) 时钟走的比预期慢很多,在40S的时候出现很奇怪的现象,时钟卡住,走的很慢:在这个问题上卡了很久很久,一直通过波形寻找答案,最后发现原来是时钟发生器的程序设计和键盘进行接口时发生混乱造成的。
4) 状态机一直无法跳入下一个状态:首先找出跳入下一状态的条件,检查程序文件,分析是否错误,进行仿真,查看波形。将每一个信号与预期进行对应,找出问题的根源。
5) 开机后秒表不是从零开始:这是由于在复位后和开机后默认时钟发生器工作,而LCD需要进行一段时间的初始化,因此才导致这个问题的存在。解决方法有两种:第一种是设置两个异步复位信号,一个使系统复位,一个是时钟的复位信号;第二种方法是将系统开机和复位后默认时钟是停止的,只是比较简答和快捷的做法,只有按下开始/暂停的功能键,时钟才进入运行。
6) 提示大量的警告:可以忽略也可以进行查看
7) 还未解决的一问题:在这次设计的过程中,我曾在modelsim进行功能仿真通过后,在ise 里面调用modelsim进行仿真,可是没有出错,但功能一直没实现,全部显示为零。
8. 实验心的和体会:
这是本学期学完逻辑设计和硬件描述语言后第一次真正意义上的代码编写和测试,在这个短短的时间里,让我体会到数字系统的魅力时,更让我深深的感受到:只有投入常人不想投入的时间,才会作出常人做不到的事情。在本次实验中有一下几点收获和体会
1) 通过编写小的程序模块,综合进行查看门及电路结构会加深对硬件语言的了解,如阻塞和非阻塞,优先级等
2) 只要功能仿真通过了,那么真个设计也就差不多了,这是我在本次试验中体会最深刻的一点,反反复复的仿真、查看波形验证时序直至得到自己想要的结果。在仿真时分模块进行仿真会使设计简单化,然后再进行总体仿真。在这次仿真验证的过程中也进一步熟悉了modelsim的使用技巧,如查看内部寄存器波形等
3) 将理论上的硬件和实际硬件联系起来,才能设计出更稳定的HDL模型
4) 在编写代码之前有一个和软件非常类似的过程就是程序设计的流程,这会大大简化在设计过程中的工作量,同时会使得思路清晰明了
5) 只有不断的实践练习,才能不断的提高和掌握理论知识。
39
附录:实验四程序清单
//数据读写发送顶层模块
module test_lcd_fsm
(
rst_n , // Active low
clk , // 100 MHz
pause,
lcd_rs ,
lcd_rw ,
lcd_en ,
lcd_db
);
input pause;
input rst_n ; //
Active low
input clk ; // 100
MHz
output lcd_rs ;
output lcd_rw ;
output lcd_en ;
output [7:4] lcd_db ;
parameter thd_40ms = 60000,
thd_2ms = 2000,
thd_39us = 6;
parameter idle = 3'b000,
function_set = 3'b001,
waits = 3'b010,
disp_on = 3'b011,
disp_clear = 3'b100,
entry_mode_set = 3'b101,
putchar = 3'b110,
finish = 3'b111;
parameter db_function_set =
10'b00_0010_1100;
parameter db_disp_on =
10'b00_0000_1111;
parameter db_disp_clear =
10'b00_0000_0001;
40
parameter db_entry_mode_set =
10'b00_0000_0110;
reg [15:0] ns_cnt;
reg us_pulse;
reg [15:0] us_cnt;
reg us_cnt_overflow;
reg [2:0] cur_state;
reg [2:0] next_state;
reg function_set_ok;
reg disp_on_ok;
reg disp_clear_ok;
reg entry_mode_set_ok;
reg char_ok;
reg char_ok_sft;
reg string_ok;
reg [79:0] string;
reg putchar_en;
reg putchar_en_sft;
reg [3:0] putchar_step;
reg send_cmd;
reg send_cmd_sft;
reg [3:0] lcd_db_in;
reg nibble_swap;
reg nibble_swap_sft;
reg dataL_ack;
reg lcd_wrce;
reg [9:0] bus2ip_data;
wire lcd_rs_in;
wire lcd_ack;
wire rst;
wire lcd_en;
wire lcd_rw;
wire lcd_rs;
wire pause,clk;
wire [7:0]
minu_h,minu_l,sec_h,sec_l,ms_h,ms_l;
lcd_ctrl lcd_ctrl_init
(
.rst (rst) , // Active
high
.clk (clk), // 100
MHz
.lcd_rs_in(lcd_rs_in) ,
.lcd_db_in(lcd_db_in) ,
.lcd_rdce(1'b0) ,
.lcd_wrce (lcd_wrce),
.lcd_ack(lcd_ack) ,
.lcd_rs (lcd_rs) ,
.lcd_rw (lcd_rw),
.lcd_en (lcd_en),
.lcd_db (lcd_db)
);
lcd_clock
lcd_clock_init(.pause(pause),.clk(clk),.rst_n(rst_n),.minu_h(minu_h),
.minu_l(minu_l),.sec_h(sec_h),.sec_l(sec_l),.ms_h(ms_h),.ms_l(ms_l));
assign lcd_rs_in=bus2ip_data[8];
assign rst = ~rst_n;
/*---------------------------------------------------------------------
-- 1us counter generation and controll
----------------------------------------------------------------------*/
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
ns_cnt <= 99;
else
if ( cur_state == idle )
ns_cnt <= 99;
else
if ( ns_cnt == 0 )
ns_cnt <= 99;
else
ns_cnt <= ns_cnt -
1'b1;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
us_pulse <= 'b0;
else
if ( ns_cnt == 1 )
us_pulse <= 1'b1;
else
us_pulse <= 1'b0;
41
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
us_cnt <= 0;
else
case ( cur_state )
idle:
us_cnt <= thd_40ms;
function_set,
entry_mode_set, disp_on, putchar:
us_cnt <= thd_39us;
disp_clear:
us_cnt <= thd_2ms;
waits:
if ( us_pulse ==
1'b1 )
us_cnt <=
us_cnt - 1'b1;
default:
us_cnt <= 0;
endcase
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
us_cnt_overflow <= 1'b0;
else
if ( us_cnt == 1 )
us_cnt_overflow <= 1'b1;
else
us_cnt_overflow <= 1'b0;
end
/*---------------------------------------------------------------------
-- state control state machine
----------------------------------------------------------------------*/
always @ ( * )
begin
if ( rst == 1'b1 )
next_state <= idle;
else
case ( cur_state )
idle:
next_state <=
function_set;
function_set:
if ( dataL_ack ==
1'b0 )
next_state <=
function_set;
else
next_state <=
waits;
disp_on:
if ( dataL_ack ==
1'b0 )
next_state <=
disp_on;
else
next_state <=
waits;
disp_clear:
if ( dataL_ack ==
1'b0 )
next_state <=
disp_clear;
else
next_state <=
waits;
waits:
if ( us_cnt_overflow
== 1'b0 )
next_state <=
waits;
else if
( function_set_ok == 1'b1 )
next_state <=
disp_on;
else if ( disp_on_ok
== 1'b1 )
next_state <=
disp_clear;
else if
( disp_clear_ok == 1'b1 )
next_state <=
42
entry_mode_set;
else
next_state <=
putchar;
entry_mode_set:
if ( dataL_ack ==
1'b0 )
next_state <=
entry_mode_set;
else
next_state <=
waits;
putchar:
if ( string_ok ==
1'b1 )
next_state <=
finish;
else
next_state <=
waits;
finish:
next_state <= putchar;
default:
next_state <= idle;
endcase
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
cur_state <= idle;
else
cur_state <= next_state;
end
/*---------------------------------------------------------------------
-- state machine control
----------------------------------------------------------------------*/
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
function_set_ok <= 1'b0;
else
if ( cur_state == function_set )
function_set_ok <= 1'b1;
else if ( cur_state == disp_on )
function_set_ok <= 1'b0;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
disp_on_ok <= 1'b0;
else
if ( cur_state == disp_on )
disp_on_ok <= 1'b1;
else if ( cur_state ==
disp_clear )
disp_on_ok <= 1'b0;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
disp_clear_ok <= 1'b0;
else
if ( cur_state == disp_clear )
disp_clear_ok <= 1'b1;
else if ( cur_state ==
entry_mode_set )
disp_clear_ok <= 1'b0;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
entry_mode_set_ok <= 1'b0;
else
if ( cur_state ==
entry_mode_set )
entry_mode_set_ok <=
1'b1;
else if ( cur_state == putchar )
entry_mode_set_ok <=
1'b0;
end
43
/*---------------------------------------------------------------------
-- putchar data controll
----------------------------------------------------------------------*/
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
send_cmd <= 1'b0;
else
if ( cur_state == idle || cur_state
== waits )
send_cmd <= 1'b0;
else
send_cmd <= 1'b1;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
send_cmd_sft <= 1'b0;
else
send_cmd_sft <= send_cmd;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
lcd_wrce <= 1'b0;
else
if ( ( send_cmd == 1'b1 &&
send_cmd_sft == 1'b0 ) ||
( putchar_en == 1'b1 &&
putchar_en_sft == 1'b0 ) ||
( char_ok == 1'b1 &&
char_ok_sft == 1'b0 ) ||
( nibble_swap == 1'b0
&& nibble_swap_sft == 1'b1 )
)
lcd_wrce <= 1'b1;
else if ( lcd_ack == 1'b1 )
lcd_wrce <= 1'b0;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
nibble_swap <= 1'b1;
else
if ( send_cmd == 1'b1 &&
send_cmd_sft == 1'b0 )
nibble_swap <= 1'b1;
else if ( lcd_ack == 1'b1 )
nibble_swap <=
~nibble_swap;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
nibble_swap_sft <= 1'b0;
else
nibble_swap_sft <=
nibble_swap;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
dataL_ack <= 1'b0;
else
if ( nibble_swap == 1'b0 &&
lcd_ack == 1'b1 )
dataL_ack <= 1'b1;
else
dataL_ack <= 1'b0;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
bus2ip_data <= 'b0;
else
case ( cur_state )
function_set:
bus2ip_data <=
db_function_set;
disp_on:
bus2ip_data <=
db_disp_on;
disp_clear:
bus2ip_data <=
db_disp_clear;
entry_mode_set:
44
bus2ip_data <=
db_entry_mode_set;
putchar:
bus2ip_data <=
{ 2'b01, string[79:72]};
endcase
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
lcd_db_in <= 4'h0;
else
if ( nibble_swap == 1'b1 )
lcd_db_in <=
{ bus2ip_data[7:4] };
else
lcd_db_in <=
{ bus2ip_data[3:0]};
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
putchar_en <= 1'b0;
else
if ( putchar_step == 4'b1001 )
putchar_en <= 1'b0;
else if ( cur_state == putchar )
putchar_en <= 1'b1;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
putchar_en_sft <= 1'b0;
else
putchar_en_sft <= putchar_en;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
putchar_step <= 4'h0;
else
if ( putchar_en == 1'b0 )
putchar_step <= 4'h0;
else if ( dataL_ack == 1'b1 )
putchar_step <=
putchar_step + 1'b1;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
string <=
{8'h84,minu_h,minu_l,8'h3a,sec_h,sec_l,8'h3a,ms_h,ms_l,8'h84};
else
if ( cur_state == finish )
string <=
{8'h84,minu_h,minu_l,8'h3a,sec_h,sec_l,8'h3a,ms_h,ms_l,8'h84};
else if ( putchar_en == 1'b1
&& dataL_ack == 1'b1 )
string <=
{ string[(79-8):0], 8'h00 };
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
char_ok <= 1'b0;
else
if ( cur_state == putchar )
char_ok <= 1'b0;
else if ( putchar_en == 1'b1
&& cur_state == waits )
char_ok <= 1'b1;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
char_ok_sft <= 1'b0;
else
char_ok_sft <= char_ok;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
string_ok <= 1'b0;
else
45
if ( cur_state ==
idle||cur_state==finish )
string_ok <= 1'b0;
else if ( putchar_step ==
4'b1001 )
string_ok <= 1'b1;
end
endmodule
//LCD时序控制模块
`timescale 1 ns / 10 ps
module lcd_ctrl
(
input rst
, // Active high
input clk
, // 100 MHz
input lcd_rs_in ,
input [7:4] lcd_db_in ,
input
lcd_rdce ,
input
lcd_wrce ,
output reg lcd_ack ,
output reg lcd_rs
,
output reg lcd_rw
,
output reg lcd_en
,
output reg [7:4] lcd_db
);
parameter tsu_40ns = 7,
tw_450ns = 63,
tw_230ns = 31,
tc_500ns = 63,
tc_1000ns = 127,
thd_10ns = 3;
parameter idle = 3'b000,
setup = 3'b001,
enable = 3'b010,
hold = 3'b011,
finish = 3'b100;
reg [2:0] cur_timing;
reg [2:0] next_timing;
reg tsu_over;
reg tw_over;
reg thd_over;
reg tc_over;
reg [7:0] tsu_cnt;
reg [7:0] tw_cnt;
reg [7:0] thd_cnt;
reg [7:0] tc_cnt;
reg lcd_wrce_sft;
reg lcd_rdce_sft;
/*---------------------------------------------------------------------
-- Opb bus interface controll
----------------------------------------------------------------------*/
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
begin
lcd_wrce_sft <= 1'b0;
lcd_rdce_sft <= 1'b0;
end
else
begin
lcd_wrce_sft <= lcd_wrce;
lcd_rdce_sft <= lcd_rdce;
end
end
46
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
lcd_ack <= 1'b0;
else
if ( tc_over == 1'b1 )
lcd_ack <= 1'b1;
else
lcd_ack <= 1'b0;
end
/*---------------------------------------------------------------------
-- State machine controll
----------------------------------------------------------------------*/
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
tsu_cnt <= 8'h00;
else
if ( (lcd_wrce == 1'b1 &&
lcd_wrce_sft == 1'b0) ||
(lcd_rdce == 1'b1 &&
lcd_rdce_sft == 1'b0)
)
tsu_cnt <= tsu_40ns;
else if ( cur_timing == setup )
tsu_cnt <= tsu_cnt - 1'b1;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
tw_cnt <= 8'h00;
else
if ( lcd_wrce == 1'b1 &&
lcd_wrce_sft == 1'b0 )
tw_cnt <= tw_230ns;
else if ( lcd_rdce == 1'b1 &&
lcd_rdce_sft == 1'b0 )
tw_cnt <= tw_450ns;
else if ( cur_timing == enable )
tw_cnt <= tw_cnt - 1'b1;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
thd_cnt <= 8'h00;
else
if ( tw_over == 1'b1 )
thd_cnt <= thd_10ns;
else if ( cur_timing == hold )
thd_cnt <= thd_cnt - 1'b1;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
tc_cnt <= 8'h00;
else
if ( lcd_wrce == 1'b1 &&
lcd_wrce_sft == 1'b0 )
tc_cnt <= tc_500ns;
else if ( lcd_rdce == 1'b1 &&
lcd_rdce_sft == 1'b0 )
tc_cnt <= tc_1000ns;
else if ( cur_timing == enable ||
cur_timing == hold || cur_timing == finish )
tc_cnt <= tc_cnt - 1'b1;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
tsu_over <= 1'b0;
else
if ( tsu_cnt == 8'h01 )
tsu_over <= 1'b1;
else
tsu_over <= 1'b0;
end
always @ ( posedge rst or posedge clk )
begin
47
if ( rst == 1'b1 )
tw_over <= 1'b0;
else
if ( tw_cnt == 8'h01 )
tw_over <= 1'b1;
else
tw_over <= 1'b0;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
thd_over <= 1'b0;
else
if ( thd_cnt == 8'h01 )
thd_over <= 1'b1;
else
thd_over <= 1'b0;
end
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
tc_over <= 1'b0;
else
if ( tc_cnt == 8'h01 )
tc_over <= 1'b1;
else
tc_over <= 1'b0;
end
/*---------------------------------------------------------------------
-- Timing control state machine
----------------------------------------------------------------------*/
always @ ( * )
begin
if ( rst == 1'b1 )
next_timing <= idle;
else
case ( cur_timing )
idle:
if ( (lcd_wrce == 1'b1
&& lcd_wrce_sft == 1'b0) ||
(lcd_rdce
== 1'b1 && lcd_rdce_sft == 1'b0)
always @ ( posedge rst or posedge clk )
begin
if ( rst == 1'b1 )
setup;
idle;
1'b1 )
enable;
setup;
hold;
enable;
1'b1 )
finish;
hold;
idle;
finish;
end
) cur_timing <= idle;
next_timing <= else
cur_timing <= next_timing;
else end
next_timing <=
/*----------------------------------------------- setup: ----------------------
if ( tsu_over == -- Lumex LCD control signal generate
-------------------------------------------------- next_timing <= --------------------*/
always @ ( posedge rst or posedge clk )
else begin
next_timing <= if ( rst == 1'b1 )
lcd_db <= 4'h0;
enable: else
if ( tw_over == 1'b1 ) if ( lcd_wrce == 1'b1 )
next_timing <= lcd_db[7:4] <=
lcd_db_in[7:4];
else end
next_timing <=
always @ ( posedge rst or posedge clk )
hold: begin
if ( thd_over == if ( rst == 1'b1 )
lcd_rs <= 1'b0;
next_timing <= else
if ( cur_timing == setup )
else lcd_rs <= lcd_rs_in;
next_timing <= else if ( cur_timing == finish )
lcd_rs <= 1'b0;
finish: end
if ( tc_over == 1'b1 )
next_timing <= always @ ( posedge rst or posedge clk )
begin
else if ( rst == 1'b1 )
next_timing <= lcd_rw <= 1'b0;
else
default: if ( cur_timing == setup )
next_timing <= idle; lcd_rw <= ( ~lcd_wrce &
endcase lcd_rdce ) ;
else if ( cur_timing == finish )
lcd_rw <= 1'b0;
48
end
always @ ( posedge rst or posedge
clk )
begin
if ( rst == 1'b1 )
lcd_en <= 1'b0;
else
if ( cur_timing == enable )
lcd_en <= 1'b1;
else
lcd_en <= 1'b0;
end
endmodule
//数字时钟产生模块
module
lcd_clock(pause,clk,rst_n,minu_h,minu_l,sec_h,sec_l,ms_h,ms_l);
input pause,rst_n,clk;
output [7:0] minu_h;
output [7:0] minu_l;
output [7:0] sec_h;
output [7:0] sec_l;
output [7:0] ms_h;
output [7:0] ms_l;
wire [7:0] minu_h;
wire [7:0] minu_l;
wire [7:0] sec_h;
wire [7:0] sec_l;
wire [7:0] ms_h;
wire [7:0] ms_l;
reg [3:0] hour_digital_h;
reg [3:0] hour_digital_l;
reg [3:0] minu_digital_h;
reg [3:0] minu_digital_l;
reg [3:0] sec_digital_h;
reg [3:0] sec_digital_l;
49
reg [3:0] ms_digital_h;
reg [3:0] ms_digital_l;
reg [27:0] clk_count1;
reg [18:0] clk_count2;
reg pause_en;
reg pause_en_dly;
reg pause_start;
reg pause_start_en;
reg ms_clk;
reg sec_clk;
reg minu_clk;
reg hour_clk;
assign hour_h={4'h3,hour_digital_h};
assign hour_l={4'h3,hour_digital_l};
assign minu_h={4'h3,minu_digital_h};
assign minu_l={4'h3,minu_digital_l};
assign sec_h={4'h3,sec_digital_h};
assign sec_l={4'h3,sec_digital_l};
assign ms_h={4'h3,ms_digital_h};
assign ms_l={4'h3,ms_digital_l};
/*---------------------------------------------------------------------
-- clock counter generation and controll
----------------------------------------------------------------------*/
/*********************pause_key_check*****************/
always @(negedge rst_n or posedge clk)
begin
if(rst_n==1'b0)
pause_en<=1'b1;
else
pause_en<=pause;
end
always @(negedge rst_n or posedge clk)
begin
if(rst_n==1'b0)
pause_en_dly<=1'b1;
else
发布评论