能实现四位二进制数全加的数字电路模块,称之为四位全加器。
所谓全加器就是就是带进位(低位向上进位 和 向高位进位)的加法器.其一位全加器的真值表如下表所示:
对于多位的全加器可以使用加法器来实现,例如下面这个四位全加器,高位的进位则可以通过位拼接符来进位.
这是四位全加器的代码:
module a(
input [3:0] a,
input [3:0] b,
input CI,
output [3:0] sum,
output CO
);
assign{CO,sum}=a+b+CI;
endmoule
根据地址信号的要求,将一路数据分配到指定输出通道上去的电路,称为数据分配器。
其模型如图所示:
相当于是一对多,一个通道输入对应多个通道输出,在选择信号下分别分配给不同的的通道,在UART串口协议的接受中,将PC机发过来的一位一位的数据存在相对应的数组当中 及 在写URAT多字节发送模块在每一个字发送完成后将下一个字符的2进制码依次送给单字节,让其发送都是数据分配器的应用.
8位数据分配器的代码如下所示:
module a(
input [3:0] a,
input clk,
input rst_n,
output reg [7:0] out
);
always@(posedge clk or negedge rst_n)
begin
if(~rst_n)
out<=8'b0000_0000;
else
case(a)
3'b000:out[0]=1'b1;
3'b001:out[1]=1'b1;
3'b010:out[2]=1'b1;
3'b011:out[3]=1'b1;
3'b100:out[4]=1'b1;
3'b101:out[5]=1'b1;
3'b110:out[6]=1'b1;
3'b111:out[7]=1'b1;
endcase
end
endmodule
module a(
input [4:0]in,
output out
);
always@(*)
begin
for(i=0;i<=4,i++)
if(in[i])
sum++;
else sum=sum;
if(sum>=3) out=1'b1;
else out=1'b0;
end
endmodule
当然此题也可以用写真值表画卡诺图来解决,但当投票人数变多,卡诺图便显得没有这种方法简单了.
BCD码可分为有权码和无权码两类:有权BCD码有8421码、2421码、5421码,其中8421码是最常用的;无权BCD码有余3码,余3循环码等。
画四个真值表可其得其四个逻辑函数,代码如下:
module a(
input [3:0]a,
output[3:0]Y
);
always@(*)
begin
Y[3]=a[3]|a[2]&a[1]|a[2]&a[0];
Y[2]=a[3]|a[2]&a[1]|a[2]&!a[0];
Y[1]=a[3]|!a[2]&a[1]|a[2]&!a[1]&a[0];
Y[0]=a[0];
end
endmodule
除了用这种画真值表的办法之外,通过观察8421和2421的码表可发现8421BCD码在小于等于5之前和2421码是完全相同的,在大于5之后只要再给输入的8421码加6即可得到2421码,读者可以想一想为什么及6是怎么得来的。
module a(
input [3:0] a,
output out
);
always(*)
begin
if(a>3'd5)
out=1'b1;
else out=1'b0;
end
endmoudle
因为8421码其实就是二进制码,故可以直接拿来用,对于其他的位权码如2421码,5421码可以通过直接用乘法来乘他的位权,但由于开发板上乘法器很少,资源很宝贵,故一般不采用。所以对于其他的位权码或者无权码可以将其先转化成8421码,再直接拿过来用。
代码如下所示:
module ljys(
input [3:0]a,
input [3:0]b,
input [2:0]A,
output reg JG
);
always@(*)
begin
case(A)
3'b000: JG=a&b; //与运算
3'b001:JG=a|b; //或
3'b010:JG=a^b; //异或
3'b011:JG=!(a&b); //与非
3'b100:JG=!(a|b); //或非
3'b101:JG=!(!a&b|a&!b); //异或非
endcase
end
endmodule
这里异或既可以用verilog中的语法中的 ^ 双目运算符来写,也可以用与或非!a&b|a&!b表达,同样的还有同或可以用 ^~表达
数据选择器 根据给定的输入地址代码,从一组输入信号中选出指定的一个送至输出端的组合逻辑电路。
而m1+m3则是函数的最小项表达式
利用逻辑函数的基本公式,可以把任意一个逻辑函数化成若干个最小项之和的形式,称为最小项表达式。
其模型如下图所示:
表示多对一,相当于有多路输入经过选择信号的选择某一路给输出,在UART串口协议的发送模块中,将要发送的8位数据位一位一位的发送出去就是数据选择器的一个应用。
该题代码如下:
module a(
input [1:0] a,
input [3:0] b,
output reg out
);
always@(*)
begin
case(a)
2'b00: out=b[0];
2'b01: out=b[1];
2'b10: out=b[2];
2'b11: out=b[3];
end
endmoudle
module ab(
input [1:0]a,
output out
);
wire [3:0]b;
a a1(
.a(a)
.b(b)
.out(out)
);
assign b=4'b1010;
这里实现逻辑函数则是通过实例化上一步的数据选择器来实现的,而之所以把b设置为0101,是因为逻辑函数为M1+M3,即A’B和AB,故函数即为B,故当a[0]=1时,out为1,故b应为4’b1010.
译码是编码的逆过程,在编码时,每一种二进制代码,都赋予了特定的含义,即都表示了一个确定的信号或者对象。把代码状态的特定含义“翻译”出来的过程叫做译码,实现译码操作的电路称为译码器。
代码如下所示:
module a(
input [2:0] a,
input [7:0] Y
output [7:0]out
);
always@(*)
begin
case(a)
3'b000: out=Y[0];
3'b001: out=Y[1];
3'b010: out=Y[2];
3'b011: out=Y[3];
3'b100: out=Y[4];
3'b101: out=Y[5];
3'b110: out=Y[6];
3'b111: out=Y[7];
end
endmodule
module ab(
input [2:0]a,
output[7:0]out
);
wire [7:0]Y;
a aa(
.a(a),
.Y(Y),
.out(out)
);
assign Y=8'b1010_0110;
endmoule
这里ab模块为外包的模块,a模块为实例化在ab模块内的,从而做到a模块不变,ab模块的参量改变结果就改变。而Y的值1010_0110是因为最小项函数为m1+m2+m5+m7,对应Y[1],Y[2],Y[5],Y[7]为1,所以Y的值就为1010_0110.
这里牵扯一个数码管为共阳还是共阴,就如同字面意思一样,共阳即所有LED的阳极接在一起,共阴就是所有的阴极接在一起,如下图所示:
最直接的区别则为共阳极为0表示亮,共阴极为1表示亮——段led若为共阴极表示数字1则为0000_0110。
因笔者使用的开发板为赛灵思的basys 2开发板,其数码管为共阳,则代码如下(笔者省去了第8位即无小数点位):
module led_16(
input [3:0] a,
output reg [6:0] Y
);
always@(a)
begin
case(a)
4'b0000:Y=7'b1000_000;
4'b0001:Y=7'b1111_001;
4'b0010:Y=7'b0100_100;
4'b0011:Y=7'b0110_000;
4'b0100:Y=7'b0011_001;
4'b0101:Y=7'b0010_010;
4'b0110:Y=7'b0000_010;
4'b0111:Y=7'b1111_000;
4'b1000:Y=7'b0000_000;
4'b1001:Y=7'b0010_000;
4'b1010:Y=7'b0001_000;
4'b1011:Y=7'b0000_011;
4'b1100:Y=7'b1000_110;
4'b1101:Y=7'b0100_001;
4'b1110:Y=7'b0000_110;
4'b1111:Y=7'b0001_110;
endcase
end
endmodule
这里显示数码管因为只写了段选没有写位选(这个问题笔者后面在讲多功能时钟的时候再详谈)故会出现所有的数码管一起亮同一个数字的情况,如下图所示:
代码如下所示:
module a(
input [3:0] a,
input [7:0] Y,
output reg out
);
always(*)
begin
case(a)
3'b000: out=Y[0];
3'b001: out=Y[1];
3'b010: out=Y[2];
3'b011: out=Y[3];
3'b100: out=Y[4];
3'b101: out=Y[5];
3'b110: out=Y[6];
3'b111: out=Y[7];
endcase
end
endmodule
module ab(
input[3:0]a,
input D,
output out
);
wire [7:0]Y;
a a1(
.a(a),
.Y(Y),
.out(out)
);
assign Y={1'b0,D,1'b0,D,1'b1,D,1'b0,D};
endmodule
这里因为题目要求使用8选一数据选择器实现,但数据选择只有三个地址端即数据选择端,但题目中的逻辑函数变量有四个,这里就要用到数电书中的降维法。
降维的目的是,增加了D输出,而不是单纯的1和0进行输出,而利用ABC三个变量进行选择。ABC此时可以看做地址,按照地址找到相应的输出数据。这就实现了数据选择器的功能。
同理还可以把ABC再进行降维成AB和C。
所谓优先编码器,即有优先级的编码器,允许同时在几个输入端有输入信号,编码器按输入信号排定的优先顺序,只对同时输入的几个信号中优先权最高的一个进行编码。
代码如下所示:
module BMQ_83(
input[7:0] date,
output reg [2:0] out
);
always@(*)
begin
if (date[7]==0) out=3'b000;
else if(date[6]==0) out=3'b001;
else if(date[5]==0) out=3'b010;
else if(date[4]==0) out=3'b011;
else if(date[3]==0) out=3'b100;
else if(date[2]==0) out=3'b101;
else if(date[1]==0) out=3'b110;
else if(date[0]==0) out=3'b111;
else if(date==8'b11111111) out=3'b111;
end
endmodule
这里是用了if-else语句来实现最左侧优先级最高,如比第五位高位请均无0,而第五位为0,那么不管第五位后(第六位…)为任意数字,结果均为
3’b010。这里提一下,编码是译码的逆过程,8线-3线编码器逆过来则为3-8译码器。
触发器是时序电路中最基础的组成单位,而相对应的在组合逻辑当中最基础的组成单位则是与,或,非门。
触发器是一个具有记忆功能的,具有两个稳定状态的信息存储器件,是构成多种时序电路的最基本逻辑单元,也是数字逻辑电路中一种重要的单元电路。
D触发器在时钟脉冲CP的前沿(正跳变0→1)发生翻转,触发器的次态取决于CP的脉冲上升沿到来之前D端的状态,即次态=D。因此,它具有置0、置1两种功能。
其真值表如下所示:
可以看出来Q的次态不受Q的上一个状态影响,即D是多少,Qr+1便为多少。
其代码如下:
module CFQ_D(
input D,
input clk,
output reg Q
);
always@(posedge clk)
begin
if(D==0) Q=0;
if(D==1) Q=1;
end
约翰逊计数器又称扭环计数器,是一种用n位触发器来表示2n个状态的计数器。它与环形计数器不同,后者用n位触发器仅可表示n个状态。约翰逊计数器的状态表中,相邻两组代码只可能有一位二进制代码不同,故在计数过程中不会产生错误的译码信号。鉴于上述优点,约翰逊计数器在同步计数器中应用比较广泛。
因为约翰逊计数器有8个状态,故需要4个D触发器来对其进行实现。
代码如下:
module ab(
input clk,
input rst_n,
output reg[ 3:0] out
);
CFQ_D Z1(
.clk(clk),
.D(D1),
.Q(Q1)
);
CFQ_D Z2(
.clk(clk),
.D(Q1),
.Q(Q2)
);
CFQ_D Z3(
.clk(clk),
.D(Q2),
.Q(Q3)
);
CFQ_D Z4(
.clk(clk),
.D(Q3),
.Q(Q4)
);
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
D1<=1'b1;
else if (~Q1&~Q2&~Q3&~Q4)
D1<=1'b0;
else if(Q1&Q2&Q3&Q4)
D1<=1'b1;
end
endmodule
代码中所用到的D触发器为上道题写过的,笔者在此处不再赘述。
除了使用上述用4个D触发器串起来实现约翰逊计数器外;还可以使用移位的办法来得到约翰逊计数器,即将初始值赋为4’b0000,然后如果最低位为0,则将最高位一直赋为1,将上一个值的高三位赋给低三位,否则则将最高位赋为0,将高三位赋给第三位,这样便得到了约翰逊计数器;还可以通过阻塞赋值和两个变化点的判断条件相结合,来得到约翰逊计数器。
代码如下:
module led(
input clk,
input rat_n,
output reg[7:0]led
);
reg [25:0] cnt;
always@(posedge clk or negedge rat_n)
begin
if(rat_n==1'b0)
cnt<= 26'd0; //设置初值
else if (cnt==26'd50_000-1'b1) //当达到了49_999_999时进行清零
cnt<=26'd0;
else
cnt<= cnt+1'b1;
end
always@(posedge clk or negedge rat_n)
begin
if(rat_n==1'b0)
led<=8'b0000_0001;
else if(cnt == 26'd50_000-1'b1)
begin
led[7]<= led[0]; //将上一秒的最高位传给下一秒的最低位
led[6:0]<= led[7:1]; //将上一秒的高6位传给下一秒的低6位
end
else
led <= led;
end
endmodule
这里因为开发板的晶振时钟为50M的(basys 2),所以如果直接进行变化的话,因为变化的速度太快,会导致人的眼睛无法分辨,而呈现出每个LED灯一直都亮的情况,故要使用计数器进行分频,这里计数器记50M下,即为1s.若使用约翰逊计数器 则把计数部分进行替换即可。
module(
input [3:0]a,
input b,
output [3:0] c
);
always(*)
begin
if(!a)
c=a+2'b11;
else c=a-2'b11;
end
这道题笔者这样写代码是用了余三码等于8421码加3转化而来的机制,当然这道题也可以通过画真值表写出来。
应当注意的是此处M1 * M2 * M5 * M7与之前的最小项函数了,其中间是,为最大项函数。
代码如下:
module a(
input [2:0] a,
input [7:0] Y,
output b
);
always@(*)
begin
case(a)
3'b000: b=Y[0];
3'b001: b=Y[1];
3'b010: b=Y[2];
3'b011: b=Y[3];
3'b100: b=Y[4];
3'b101: b=Y[5];
3'b110: b=Y[6];
3'b111: b=Y[7];
end
endmodule
module ab(
input [2:0] a,
output b
);
wire [7:0] Y;
a a1(
.a(a),
.Y(Y),
.b(b)
);
assign Y=8'b0101_1001
endmodule
最大项表达式刚好与最小值相反,最小项写1,最大项写0
代码如下:
module a(
input clk,
input rst_n,
output flag
);
reg [2:0] cnt;
always@(posedge clk or negedge rst_n)
begin
if(!rest)
cnt<=3'b111;
else if(cnt==3'b000)
cnt<=3'b111;
else cnt<=cnt-3'b001;
endmoudule
移位寄存器是一种在若干相同时间脉冲下工作的以触发器为基础的器件,数据以并行或串行的方式输入到该器件中,然后每个时间脉冲依次向左或右移动一个比特,在输出端进行输出。
代码如下:
module jwJCQ(
input clk,
input data_in,
input rdt_n,
output reg [3:0]q
);
always@(posedge clk or negedge rst_n)
begin
if(!rest)
q<=4'b0;
else
begin
q[3]<=data_in;
q[2:0]<=q[3:1];
end
end
在URAT协议中接受模块接收UART发送过来的串口数据时,从1到0为数据起始位,这里检测下降沿就用到了移位寄存器。
代码如下:
module a(
input clk,
input clr,
output reg [2:0]q
);
wire [1:0] D;
assign D[0]=q[0]^q[1];
assign D[1]=~q[1];
always@(posedge clk or negedge clr)
begin
if(!clr)
q<=2'd00;
else q<=D;
end
因为在D触发器中q<=D,所以输出q是由D确定的,则D的函数则是通过画上一状态的q的真值表确定的。
代码如下所示:
module fenpin5(
input clk,
input clr,
input set
output q
);
reg q;
mo5 U1(
.clk(clk),
.rst_n(clr),
.f(f)
);
always@(posedge clk or negedge clr)
begin
if(!clr) //异步清零
q<=0;
else if(set==1) //同步置数
q<=1;
else begin
if(f==1'b1)
q<=1;
else q<=0;
end
end
endmodule
module mo5(
input clk,
input rst_n,
output f
);
reg [2:0] cnt;
always(posedge clk or negedge rst_n)
begin
if(~rst_n)beigin
cnt<=1'b0;f=1'b0; end
else if(cnt==3'b100) begin
cnt<=1'b0; f=1'b1; end
else cnt<=cnt+1'b1;
end
endmodule
module JSQ74LS161(
input clk,
input clr,
input CTr,
input CTp,
input set,
input [3:0]D,
output reg [3:0]Q
);
reg[3:0]q;
always@(posedge clk or negedge clr )
begin
if(!clr) //当clr=0时不管时钟啊,或者seta,或者D啊 q全为0即异步清零
Q<=1'b0;
else
begin
if(set==0) //当clr==1且set=0 当时钟上升沿来的时候给他D的值 就是同步置数
Q<=D;
else if(CTr&CTp==1) //当clr=set=1,CTr=CTp=1时 进行计数功能
begin
if(q==4'b1111)
q<=0;
else q<=q+1'b1;
end
else if(CTr&CTp==0) //当CTr或CTp有一个为0时,不管时钟是否为上升沿都对计数进行保持
begin
q<=q;
end
end
end
endmodule
module JSQ74LS163(
input clk,
input clr,
input ENT,
input ENP,
input set,
input [3:0]D,
output reg [3:0]Q,
output reg RCO //与161不同的地方 有个RCO的输出
);
reg[3:0]q;
always@(posedge clk )
begin
if(!clr) //当clr=0时需要时钟是上升沿同步清零(与161的不同)
begin
Q <= 1'b0;
RCO <= 1'b0; //只有当计数满一轮且ENT为1时,RCO为1,其它时候均为0.
end
else if(set==0) //当clr==1且set=0 当时钟上升沿来的时候给他D的值 就是同步置数
Q<=D;
else if(ENT&ENP==1) //当clr=set=1,CTr=CTp=1时 进行计数功能
begin
if(q==4'b1111)
begin
q<=0;
RCO<=1'b1; //计数满了一轮且ENT为1时,RCO为1
end
else q<=q+1'b1;
end
else if(ENT&ENP==0) //当CTr或CTp有一个为0时,不管时钟是否为上升沿都对计数进行保持
begin
q<=q;
end
end
endmodule
**
**