在做前面的练习的时候深深感到了基础知识的不足,有必要好好补充一下
通过对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输出值来赋值。
M1表示的是A+B+C’,M2表示的是A+B’+C,M5表示的是A’+B+C’,M7表示的是A’+B’+C’;(M是大写,是最大项)
函数通过关键词 function 和 endfunction 定义,不允许输出端口声明(包括输出和双向端口) ,但可以有多个输入端口。函数定义的语法如下:
function [range] function_id;
input_declaration
other_declarations
procedural_statement
endfunction
其中,function 语句标志着函数定义结构的开始;[range]参数指定函数返回值的类型或位宽,是一个可选项,若没有指定,默认缺省值为 1 比特的寄存器数据;function_id 为所定义函数的名称,对函数的调用也是通过函数名完成的,并在函数结构体内部代表一个内部变量,函数调用的返回值就是通过函数名变量传递给调用语句;input_declaration 用于对寒暑各个输入端口的位宽和类型进行说明,在函数定义中至少要有一个输入端口;endfunction为函数结构体结束标志。
(2)函数至少要有一个输入端口;不能包含输出端口和双向端口;
(3) 在函数结构中, 不能使用任何形式的时间控制语句 (#、 wait 等) , 也不能使用 disable中止语句;
(4)函数定义结构体中不能出现过程块语句(always 语句) ;
(5)函数内部可以调用函数,但不能调用任务。**
和任务一样,函数也是在被调用时才被执行的,调用函数的语句形式如下:
func_id(expr1, expr2, …, exprN)
其中,func_id 是要调用的函数名,expr1, expr2, …exprN是传递给函数的输入参数列表,该输入参数列表的顺序必须与函数定义时声明其输入的顺序相同。
普通的电路,以及常规的逻辑门都有一个共性,那就是输出直接依赖于输入,当输入消失的时候,输入也跟着不存在了。触发器不同,当它触发的时候,输出会发生变化。但是,当输入撤销之后,输出依然能够维持。
“异步”输入信号和时钟信号无关,是指输入信号变为有效状态,器件的状态就改变;
“同步”输入信号和时钟信号有关,实际上输入信号和时钟信号进行了与运算或者与非运算,输入信号和时钟信号的运算结果是有效的,器件的状态才会改变。
异步置数:与时钟脉冲CP没有任何关系,只要异步置数控制端出现置数信号,并行数据便立刻被置入。
同步置数:输入端获得置数信号后,只是为置数创造了条件,还需要再输入一个计数脉冲CP,计数器才能将预置数置入。
通常的做法是将计数器加1,再将结果除以n(n为最大值),余数为结果。这种计数器用途很广,如循环队列。
counter = (counter + 1)%n;
相比来讲,使用比较的方法来实现的话效率会更高:
counter = counter + 1;
if( counter >= n )
{counter = 0;}
一个数字系统中往往需要多种频率的时钟脉冲作为驱动源,这样就需要对FPGA的系统时钟(频率较高)进行分频。比如在进行流水灯、数码管动态扫描设计时不能直接使用系统时钟(太快而肉眼无法识别),或者需要进行通信时,由于通信速度不能太高(由不同的标准限定),这样就需要对系统时钟分频以得到较低频率的时钟。
分频器主要分为偶数分频、奇数分频、半整数分频和小数分频
在对时钟要求不是很严格的FPGA系统中,分频通常都是通过计数器的循环计数来实现的。
偶数分频最为简单,很容易用模为N的计数器实现50%占空比的时钟信号,即每次计数满N(计到N-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的计数,计数到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] count
always @(posedge clk)
begin
if(count==2'b10)
count<=2'b0;
clk_3<=~clk_6;
else
count<=count+2'b1;
// 偶数分频器示例,20分频即N=10,占空比50%
module Fre_div_even(
input clk,
input rst_n,
input [3:0] N, // N = 分频倍数/2
output 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
end
end
endmodule
// 奇数分频器示例,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
end
end
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
end
end
assign clk_out = clk_n | clk_p;
endmodule
// 半分频器示例,以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
begin
if(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
end
end
assign clk_out = clk_div_odd ^ clk_rev_div;
endmodule
16就是16最高位
六分频
十分频
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)begin
cnt<=1'b0;
out_clk<=1'b0;end
else if(cnt==2'b10)begin
cnt<=1'b0;
out_clk<=~out_clk;end
else
cnt<=cnt+1'b1;
endmodule
`timescale 1ns/1ns
`define clock_period 20
module test_tb;
reg clk,rst_n;
wire out_clk;
test test1(clk,rst_n,out_clk);
initial clk=1'b0;
always #(`clock_period/2)clk=~clk;
initial begin
rst_n=1'b0;
#100
rst_n=1'b1;
#2000
$stop;
end
endmodule