写在前面
万万没想到最后去了FPGA岗位,但是FPGA只在研一学过,确实忘得差不多了,因此从头把东西过亿边
这是某本书上的第一章节,感觉写的还是挺不错的,大概看了一下让我回想起很多知识,个人感觉比较适合学习了Verilog语法和数电之后上板的同学
1. 原理介绍
1.1 数码管
数码管按段分可分为七段数码管和八段数码管,区别就是八段数码管多了个小数点
常见的数码管有两种:共阴数码管和共阳数码管
共阴数码管就是高电平亮,低电平灭;共阳数码管就是低电平亮,高电平灭。
1.2 译码器
七段数码管译码器的结构图如下,4个输入变量能够组合成十六进制数字0-F,那么相应的输出就可以通过七个输出变量,表示成数字,从而达到译码的效果。
这里译码器有两种写法,一种比较复杂,就是通过对应的真值表或者卡诺图写出h的逻辑表达式,写起来比较繁琐,就直接放代码,通过逻辑门(assign赋值语句
)实现译码器
assign h[6] = (~b[3] & ~b[2] & ~b[1]) | (~b[3] & b[2] & b[1] & b[0]) | (b[3] & b[2] & ~b[1] & ~b[0]);
assign h[5] = (~b[3] & ~b[2] & ~b[0]) | (~b[3] & ~b[2] & b[1]) | (~b[3] & b[1] & ~b[0]) |(b[3] & b[2] & ~b[1] & b[0]);
assign h[4] = (~b[3] & b[0]) | (~b[3] & b[2] & ~b[1]) | (b[3] & ~b[2] & ~b[1] & b[0]);
assign h[3] = (~b[2] & ~b[1] & b[0]) | (~b[3] & b[2] & ~b[1] & ~b[0]) | (b[2] & b[1] & b[0] ) | (b[3] & ~b[2] &b[1] & ~b[0]);
assign h[2] = (~b[3] & ~b[2] & b[1] & ~b[0]) | (b[3] & b[2] & ~b[1] & ~b[0]) | (b[3] & b[2] & b[1]);
assign h[1] = (~b[3] & b[2] & ~b[1] & b[0]) | (& b[2] & b[1] & ~b[0]) | (b[3] & b[2] & ~b[0]) | (b[3] & b[1] & b[0]);
assign h[0] = (~b[3] & ~b[2] & ~b[1] & b[0]) | (~b[3] & b[2] & ~b[1] & ~b[0]) | (b[3] & b[2] & ~b[1] & b[0] | b[3] & ~b[2] & b[1] & b[0]);
当然,这样不仅要推导,写起来就很麻烦
所以还可以用case条件语句
实现
case (b)
4`b0000: h = 7`b1000000;
4`b0001: h = 7`b1111001;
4`b0010: h = 7`b0100100;
4`b0011: h = 7`b0110000;
4`b0100: h = 7`b0011001;
4`b0101: h = 7`b0010010;
4`b0110: h = 7`b0000010;
4`b0111: h = 7`b1111000;
4`b1000: h = 7`b0000000;
4`b1001: h = 7`b0011000;
4`b1010: h = 7`b0001000;
4`b1011: h = 7`b0000011;
4`b1100: h = 7`b1000110;
4`b1101: h = 7`b0100001;
4`b1110: h = 7`b1000110;
4`b1111: h = 7`b1001110;
endcase
1.3 D触发器
D触发器的特性方程Qn+1 = D,Qn+1为次态,D为输入信号,都是数电上的东西,Verliog HDL代码为:
module dff(
input clk,
intpu d,
output reg q
);
always @(posedge clk)
q <= d;
endmudle;
而通常会用到复位信号来设定电路的起始值或者做清楚动作,因此D触发器根据复位的不同分为两种:异步复位D触发器和同步复位D触发器
异步复位D触发器:
“异步”指的是和工作时钟不同步的意思,即寄存器的复位不关心时钟的有效沿来不来,只要检测到复位信号有效,就立即执行复位操作。
module DFF_async_rst(
input clk,
input rst_n,
intpu d,
output reg q
);
always @(posedge clk,negedge rst_n)
if(~rst_n) //复位信号rst_n的优先级比时钟信号clk高
q <= 1'b0;
else
q <= d;
endmudle;
同步复位D触发器
“同步”是和工作时钟同步的意思,即复位信号只有在时钟的有效沿到来时,才能有效,否则无法完成复位任务。
module DFF_async_rst(
input clk,
input rst_n,
intpu d,
output reg q
);
always @(posedge clk) //always语句只有一个触发条件
if(~rst_n) //复位信号rst_n需要在clk的上升沿触发下才能起作用
//当rst_n=0,并且clk上升沿到来时,Q才会被清零
q <= 1'b0;
else
q <= d;
endmudle;
1.4 寄存器
寄存器是数字系统中用来存储二进制数据的逻辑部件。1个触发器可以存储一位二进制数据,存储n位二进制数据的寄存器需要用n个触发器组成。
当然,上述说法是表现在硬件上,在代码方便只是定义了输入输出的位宽:
module reg4bits(
input clk,
input rst_n,
input en;
intpu [3:0] d,
output reg [3:0] q
);
always @(posedge clk,negedge rst_n)
if(~rst_n)
q <= 4'b0000;
else if(en) //增加了使能端
q <= d;
else
q <= q;
endmudle;
1.5 计数器
计数器不仅可以用于对脉冲进行计数,还可用于分频、定时、产生节拍脉冲以及其他时序信号。
同步二进制加(减)计数器
module counter_up(
input clk,
input rst_n,
input en,
output reg [3:0] q
);
always @(posedge clk or negedge rst_n)
begin
if(~rst_n)
q <= 4'b0000; //复位,计数清零
else if(en) //计数器使能,开始计数
q <= q + 1'b1; //加计数
//q <= q - 1'b1; //减计数,原理都是一样的
else //计数器未使能,计数器保持不变
q <= q;
end
endmodule
模m计数器
计数器运行时总是从某个起始状态开始,以此经过所有不重复的状态后完成一次循环。把一次循环所包含的状态数称为计数器的模,用M表示。
module counter_m #(
parameter n = 4
parameter m = 16
)(
input clk,
input rst_n;
input en;
output reg [3:0] cnt; //计数器计数值
output carry_out; //计数器进位输出
);
always @(posedge clk or negedge rst_n)
begin
if(!rst_n) //复位,计数值清零
cnt <= 4'b0000;
else if(en) //计数器使能端,开始计数
begin
if (cnt == m-1) //计数值达到m-1,计数值清零
cnt <= 4'b0000;
else
cnt <= cnt + 1'b1;
end
else
cnt <= cnt;
end
assign carry_out = (cnt == m-1); //计数值达到m-1,计数器进位输出1
endmodule