FPGA学习---数电及verilog知识补充
数电及verilog知识补充
在做前面的练习的时候深深感到了基础知识的不足,有必要好好补充一下
1数据选择器
八选一数据选择器
通过对3位地址线的控制,是8为二进制数据只有一路送到输出上。因为8个中只能有一个到达,因此叫数据选择器。
8选1数据选择器数据选择器的典型应用:
采用8选1数据选择器74LS151可实现任意三输入变量的组合逻辑函数。作出函数F的功能表,将函数F功能表与8选1数据选择器的功能表相比较,可知:
1、将输入变量C、B、A作为8选1数据选择器的地址码A2、A1、A0。
2、使8选1数据选择器的各数据输入D0~D7分别与函数F的输出值一一相对应。
即:A2A1A0=CBA,D0=D7=0,D1=D2=D3=D4=D5=D6=1。
则8选1数据选择器的输出Q便实现了函数。
采用具有n个地址端的数据选择实现n变量的逻辑函数时, 应将函数的输入变量加到数据选择器的地址端(A),选择器的数据输入端(D)按次序以函数F输出值来赋值。
2 逻辑函数
M1表示的是A B C’,M2表示的是A B’ C,M5表示的是A’ B C’,M7表示的是A’ B’ C’;(M是大写,是最大项)
3function使用
1.函数的定义
函数通过关键词 function 和 endfunction 定义,不允许输出端口声明(包括输出和双向端口) ,但可以有多个输入端口。函数定义的语法如下:
function [range] function_id;
input_declaration
other_declarations
procedural_statement
endfunction
其中,function 语句标志着函数定义结构的开始;[range]参数指定函数返回值的类型或位宽,是一个可选项,若没有指定,默认缺省值为 1 比特的寄存器数据;function_id 为所定义函数的名称,对函数的调用也是通过函数名完成的,并在函数结构体内部代表一个内部变量,函数调用的返回值就是通过函数名变量传递给调用语句;input_declaration 用于对寒暑各个输入端口的位宽和类型进行说明,在函数定义中至少要有一个输入端口;endfunction为函数结构体结束标志。
注意
**(1)函数定义只能在模块中完成,不能出现在过程块中;
(2)函数至少要有一个输入端口;不能包含输出端口和双向端口;
(3) 在函数结构中, 不能使用任何形式的时间控制语句 (#、 wait 等) , 也不能使用 disable中止语句;
(4)函数定义结构体中不能出现过程块语句(always 语句) ;
(5)函数内部可以调用函数,但不能调用任务。**
2 调用
和任务一样,函数也是在被调用时才被执行的,调用函数的语句形式如下:
func_id(expr1, expr2, …, exprN)
其中,func_id 是要调用的函数名,expr1, expr2, …exprN是传递给函数的输入参数列表,该输入参数列表的顺序必须与函数定义时声明其输入的顺序相同。
4 三位二进制减法计数器
5 四位寄存器
6 D触发器
普通的电路,以及常规的逻辑门都有一个共性,那就是输出直接依赖于输入,当输入消失的时候,输入也跟着不存在了。触发器不同,当它触发的时候,输出会发生变化。但是,当输入撤销之后,输出依然能够维持。
7 同步清零和异步清零
“异步”输入信号和时钟信号无关,是指输入信号变为有效状态,器件的状态就改变;
“同步”输入信号和时钟信号有关,实际上输入信号和时钟信号进行了与运算或者与非运算,输入信号和时钟信号的运算结果是有效的,器件的状态才会改变。
8 同步置数和异步置数
异步置数:与时钟脉冲CP没有任何关系,只要异步置数控制端出现置数信号,并行数据便立刻被置入。
同步置数:输入端获得置数信号后,只是为置数创造了条件,还需要再输入一个计数脉冲CP,计数器才能将预置数置入。
9 模n计数器
通常的做法是将计数器加1,再将结果除以n(n为最大值),余数为结果。这种计数器用途很广,如循环队列。
counter = (counter 1)%n;
相比来讲,使用比较的方法来实现的话效率会更高:
counter = counter 1;
if( counter >= n )
{counter = 0;}
10 分频器
一个数字系统中往往需要多种频率的时钟脉冲作为驱动源,这样就需要对FPGA的系统时钟(频率较高)进行分频。比如在进行流水灯、数码管动态扫描设计时不能直接使用系统时钟(太快而肉眼无法识别),或者需要进行通信时,由于通信速度不能太高(由不同的标准限定),这样就需要对系统时钟分频以得到较低频率的时钟。
分频器主要分为偶数分频、奇数分频、半整数分频和小数分频
在对时钟要求不是很严格的FPGA系统中,分频通常都是通过计数器的循环计数来实现的。
分类
偶数分频(2N)
偶数分频最为简单,很容易用模为N的计数器实现50%占空比的时钟信号,即每次计数满N(计到N-1)时输出时钟信号翻转。
奇数分频(2N 1)
使用模为2N 1的计数器,让输出时钟在X-1(X在0到2N-1之间)和2N时各翻转一次,则可得到奇数分频器,但是占空比并不是50%(应为 X/(2N 1))。
得到占空比为50%的奇数分频器的基本思想是:将得到的上升沿触发计数的奇数分频输出信号CLK1,和得到的下降沿触发计数的相同(时钟翻转值相同)奇数分频输出信号CLK2,
最后将CLK1和CLK2相或之后输出,就可以得到占空比为50%的奇数分频器。原理图如下:
用Quartus II 得到的占空比为50%的9分频时钟输出信号outclk如下:
半整数分频(N-0.5)
基本设计思想为:首先进行模N的计数,计数到N-1时输出时钟翻转;而且在计数返回到0时,输出时钟再次翻转。
所以,只要使计数值N-1保持半个时钟周期,即可实现N-0.5分频时钟。那么如何保持半个时钟周期呢?
因为计数器是上升沿触发计数,如果在计数值=N-1时把计数器的触发时钟翻转,则时钟的下降沿就变成了上升沿。即计数值=N-1时,时钟马上翻转,
则计数值保持半个时钟周期后,会遇到上升沿而使计数值归0. 然后计数器以翻转了的时钟继续计数,在产生N-0.5个分频周期后,时钟再次翻转。
2.5分频的时序示意图如下:
怎样才能够使计数器的触发时钟在N-1时翻转呢? 由半整数分频器的原理图可知,将输出时钟二分频后和输入时钟相异或就可使触发时钟翻转。
半整数分频器原理图如下:
用Quartus II 实现的2.5分频时序图如下:
由通用分频器电路组成图可以看到,半整数分频器是由整数分频器加上二分频和异或门而构成的。
那么,如果使用元件例化的思想就可以得到通用的分频(即可选择整数分频和半整数分频)。
如果想要得到任意分频,则可以参考CrazyBingo 的 利用DDS原理的 教你什么才是真正的任意分频。
说人话版本
分频器还是比较简单的,一般的思路是:每数几个时钟就输出一个时钟。最简单的当数二分频器了,每当时钟上升沿(或下降沿)就把输出翻转一下。
module divider2(clk_in, clk_out);input clk_in;output clk_out;reg clk_out;initial clk_out <= 1'b0;always@(posedge clk_in) clk_out <= ~clk_out;endmodule
如果是6分频则是一个周期=6个clk周期,
reg[1:0] countalways @(posedge clk) begin if(count==2'b10) count<=2'b0; clk_3<=~clk_6; else count<=count 2'b1;
说人话进阶版本
1. 偶数分频器
- 偶数分频器的实现较为简单,用计数器即可实现
// 偶数分频器示例,20分频即N=10,占空比50% module Fre_div_even( input clk,input rst_n,input [3:0] N, // N = 分频倍数/2output reg clk_out ); reg [3:0] cnt; always @(posedge clk or negedge rst_n)begin if(!rst_n) begin cnt <= 4'b0; clk_out <= 1'b0; end else begin if(cnt == N-1) begin clk_out <= ~clk_out; cnt <= 4'b0; end else begin cnt <= cnt 4'b1; end endend endmodule
2. 奇数分频器
- 奇数分频相对于偶数分频较为复杂,尤其是占空比为50%的奇数分频,可以采用错位相和的方法,即分别用上升沿和下降沿产生2:3占空比的N分频时钟,并将输出进行或运算得到
// 奇数分频器示例,5分频即N=5,占空比50% module Fre_div_odd( input clk, input rst_n,input [3:0] N, // N分频 output clk_out ); reg clk_n;reg clk_p;reg [3:0] cnt_p;reg [3:0] cnt_n; always @(posedge clk or negedge rst_n)begin if(!rst_n) begin clk_p <= 1'b0; cnt_p <= 4'b0; end else begin if(cnt_p == N-1) begin clk_p <= ~clk_p; cnt_p <= 4'b0; end else if(cnt_p == (N-1)/2) begin clk_p <= ~clk_p; cnt_p <= cnt_p 1; end else begin cnt_p <= cnt_p 1; clk_p <= clk_p; end endend always @(negedge clk or negedge rst_n)begin if(!rst_n) begin clk_n <= 1'b0; cnt_n <= 4'b0; end else begin if(cnt_n == N-1) begin clk_n <= ~clk_n; cnt_n <= 4'b0; end else if(cnt_n == (N-1)/2) begin clk_n <= ~clk_n; cnt_n <= cnt_n 1; end else begin cnt_n <= cnt_n 1; clk_p <= clk_p; end endend assign clk_out = clk_n | clk_p; endmodule
3.半分频器
- 在实际工程中,经常遇到需要半分频的情况,例如时钟晶振为25MHz,而我们需要用到2MHz的时钟信号,就需要进行12.5分频。
- 半分频无法实现50%占空比,因为实现需要得到时钟信号的0.25周期,是无法实现的,因此只能使占空比尽量接近50%。
- 半分频可以在奇数分频器额基础上实现,利用已经得到的奇数分频时钟信号将原时钟信号的后半段翻转;然后再对翻转后的波形计数,得到的波形与奇数分频得到的波形进行异或后即可得到需要的半分频时钟信号
- 波形图如下:
// 半分频器示例,以2.5分频为例,即N=2 module Fre_div_half( input clk, input rst_n,input [3:0] N, // 即实现N 0.5分频 output clk_out ); wire clk_div_odd;wire clk_rev; /* 按照上文的实现方法搭建奇数分频 N_odd = 2 * N 1 具体实现省略 奇数分频后得到clk_div_odd */Fre_div_odd Fre_div_odd( .clk(clk), .rst_n(rst_n),.N(2*N 1), .clk_out(clk_div_odd) ); assign clk_rev = clk_div_odd ? ~clk : clk; reg [3:0] cnt_rev;reg clk_rev_div; always @(posedge clk_rev or negedge rst_n)begin if(!rst_n) begin clk_rev_div <= 0; cnt_rev <= 0; end else beginif(cnt_rev == N/2)begin clk_rev_div <= ~clk_rev_div;cnt_rev <= cnt_rev 1;end else if(cnt_rev == N) begin cnt_rev <= 0; end else begin cnt_rev <= cnt_rev 1; clk_rev_div <= clk_rev_div; end endend assign clk_out = clk_div_odd ^ clk_rev_div; endmodule
B站老师
2的次方分频
16就是16最高位
2偶数分频
六分频
十分频
自己写的并验证了的
偶数倍分频
六分频
module test(clk,rst_n,out_clk);input clk,rst_n;output reg out_clk;reg [1:0]cnt;always@(posedge clk or negedge rst_n)if(rst_n==1'b0)begincnt<=1'b0;out_clk<=1'b0;endelse if(cnt==2'b10)begincnt<=1'b0;out_clk<=~out_clk;endelse cnt<=cnt 1'b1;endmodule
`timescale 1ns/1ns`define clock_period 20module test_tb;reg clk,rst_n;wire out_clk;testtest1(clk,rst_n,out_clk);initial clk=1'b0;always #(`clock_period/2)clk=~clk;initial beginrst_n=1'b0;#100rst_n=1'b1;#2000$stop;endendmodule