一、实验要求
基本实验内容
1 、 设计一个可控分频器, clk_in 为分频器时钟输入 (50MHz ,已固定连接在 PIN_90) , sel 为选择开关, clk_out[1:0] 为分频器信号输出。 当 sel=0 时, clk_out[0]=sn[3:0]Hz , clk_out[1]=sn[3:0]/2Hz ; 当 sel=1 时, clk_out[0]=sn[3:0]Hz , clk_out[1]=sn[3:0]/4Hz 。 2 、 clk_out[0] 的占空比 D=40% ;( D = tH/T , tH 为高电平时间, T 为周期),其它自定。 3 、 说明。 sn 为学号, sn[0] 表示取十进制学号的末位, sn[3:0] 表示取十进制学号的最后 4 位,若学号最后 4 位为 0 ,则学号最后 4 位按 4321 取值。例如,学号 sn=2017112345 , sn[3:0]=2345 ; sn=2017100000 , sn[3:0]=4321 4 、 实验测试 用导线将 clk_out[0] 接入实验箱的逻辑分析仪通道 CH0 ,测试 sel 不同取值时 clk_out[0] 实际输出频率及占空比,若与要求不相符(频率误差须小于 1‰ ,占空比误差 小于 1‰ ),修改电路程序使之符合要求。 改变连线,将 clk_out[1] 接入 CH0 ,测试 sel 不同取值时 clk_out[1] 实际输出频率及 占空比,若与要求不相符(误差须小于 1‰ ),修改电路程序使之符合要求。预习要求
1 、 写出设计思路。 2 、 由于 FPGA 只能进行整数计数来分频,会存在除不尽的情况而只能四舍五入取整数, 请根据实际使用的分频系数计算因为取整导致的误差。 3 、 自学 ModelSim 仿真方法,用 ModelSim 对实验电路进行仿真(需使用标尺功能测 量输出信号频率 / 周期、占空比),并将仿真代码及仿真结果截图打印。 4 、 自行安排所用引脚,列出引脚锁定分配表(信号名 -> 主板器件名 -> 引脚号)二、实验实现
1、思路:
本实验根据要求有以下信号:1:sel选择信号,2:clk_in时钟频率输入信号,3:clk_out依据学号后四位实现的不同频率的时钟输出信号。实现功能:在选择不同的sel时输出依据学号后四位实现的不同占空比的clk_out[0]与clk_out[1]。
所以设置计数器解决:根据时钟信号,每上升一次计数器加一。通过计算得到各个条件下clk_out的频率,计算相对于时钟的倍数周期,得到周期内计数器计数,则占空比就是这些计数中的40%设置为1,其余设置为0。
举个例子:如果你的学号是1000,clk_out[0]对于50MHz,你的频率是时钟的1/50000,那周期就是时钟的50000倍,则用计数器表示,就是计数50000代表clk_out[0]的一个周期,而占空比40%,就是计数50000中的40%为高电平,其余为低电平。
2、代码实现
module ########(clk_in, sel, clk_out);
input clk_in;
input sel;
output reg [1:0] clk_out=2'b00;
reg [31:0] counter = 0;
// 将学号的数值作为参数传入
parameter sn = 12'd2312; // 学号的一部分
//sn的作用是确定周期,对于该实验,clkin为50MHZ,周期为20NS,记作T
//sel为0,CLKOUT0的输出频率为2312HZ,周期为21626T,0.4为8650
//sel为1,CLKOUT0的输出频率为2312HZ,周期为21626T,0.4为8650
//对于clk0的占空比为0.4,则在21626次的数字变化中实现约8650次的高电平
//clk1同理
parameter T1 = 50000000/sn;
parameter T1_high = T1*4/10;
//自定义out1的占空比为70%
parameter T2 = 50000000/sn*2;
parameter T2_high = T2*7/10;
parameter T3 = 50000000/sn*4;
parameter T3_high = T3*7/10;
always @(posedge clk_in) begin
counter<=counter+1;
if(sel==0) begin
if(counter%T1 < T1_high)
clk_out[0]<=1;
else
clk_out[0]<=0;
if(counter%T2 < T2_high)
clk_out[1]<=1;
else
clk_out[1]<=0;
end
if(sel==1) begin
if(counter%T1 < T1_high)
clk_out[0]<=1;
else
clk_out[0]=0;
if(counter%T3 < T3_high)
clk_out[1]<=1;
else
clk_out[1]<=0;
end
end
endmodule
基本计数原理已经阐述过了,此外在计数过程中,使用取余对计数器超出周期的计数做了处理。
3、误差分析
举个例子就可以看懂了,不赘述
①计算 T1
计算公式: T1=50000000/2312≈21626.17ns
取整: T1′=round(T1)=21626ns
误差: E1=T1′−T1=21626−21626.17≈−0.17ns
②计算 T2
计算公式: T2=50000000/(2312⋅2)≈10813.08ns
取整: T2′=round(T2)=10813ns
误差: E2=T2′−T2=10813−10813.08≈−0.08ns
③计算 T3
计算公式: T3=50000000/(2312⋅4)≈5406.54ns
取整: T3′=round(T3)=5407ns
误差: E3=T3′−T3=5407−5406.54≈0.46ns
(计算相对误差除真实值得出百分比即可)
4、test代码编写
test编码就是modulesim的编译运行代码,其中实现了对信号的操控。
就是一个.v文件,内置输入值,然后把我们要仿真的文件作为模块调用,通过modulesim显示结果,“#”设置信号触发长度,always实现信号的频率
`timescale 1 ns/ 1 ns
module ############();
// constants
// general purpose registers
// test vector input registers
reg clk_in;
reg sel;
// wires
wire [1:0] clk_out;
// assign statements (if any)
仿真文件模块名 i1 (
// port map - connection between master ports and signals/registers
.clk_in(clk_in),
.clk_out(clk_out),
.sel(sel)
);
initial
begin
// code that executes only once
// insert code here --> begin
sel=1'b0;
clk_in=1'b0;
#5000000
sel=1'b1;
// --> end
$display("Running testbench");
end
always
// optional sensitivity list
// @(event1 or event2 or .... eventn)
begin
// code executes for every event on sensitivity list
// insert code here --> begin
#10
clk_in=~clk_in;
// --> end
end
endmodule
5、modulesim使用
首先打开modulesim,File - new - project 创建工程
输入文件名称
选择add,添加仿真文件和它的TEST文件
编译所有文件
添加仿真文件,并双击它
进入后,添加全部仿真信号
如下选择,开始仿真模拟
拖动滑轮到中间,点击仿真图,ctrl+滑轮适当缩小尺寸
点击左下角绿色小加号,添加时间查看点
把查看点放到合适的位置,也就是能展现你的clk_out[0],clk_out[1]占空比的一些查看点,比如我的这样的:
通过频率等于周期分之一,程序计算无误的话,得出来的clk_out[0]的频率正好为你的学号后四位。
6、引脚设置
如下设置,先看波形对不对,占空比差不多对就可以
三、实验室进行时
实验室主要的要求就是通过仪器记录波形图,LED0和LED1分别是IO0和IO1,两个接到CH0和CH1记录波形图。
由于仪器只能自动计算CH0的频率,占空比,所以out[0],out[1]要分别接到CH0上记录实验数据(记得挑SEL)。
最后分析误差即可