【Verilog实战】生成38译码器和全加器
3-8译码器
Logisim evolution仿真
逻辑真值表
i0 | i1 | i2 | o0 | 01 | o2 | o3 | o4 | o5 | o6 | o7 |
---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
0 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
分析上述真值表,可以发现输入是三位的二进制数字,38译码器将其转化为十进制数字。如想要得到5,即o5对应输出端口为1,输入则是101。
Verilog实战
打开Quartus,采用Verilog编程(if-else或者case)设计一个3-8译码器,生成RTL原理电路图;
写法一
运用case语句声明所有情况,即每个输入对应的输出,直接生成RTL原理电路图。
module decode38(
output reg [7:0] out, //由于后面采用always语句,此处赋值必须为reg
input [2:0] in
);
always
case (in)
0:out=8'b00000001;
1:out=8'b00000010;
2:out=8'b00000100;
3:out=8'b00001000;
4:out=8'b00010000;
5:out=8'b00100000;
6:out=8'b01000000;
7:out=8'b10000000;
endcase
endmodule
写法二
module decode38(
output reg [7:0] out,
input [2:0] in
);
integer i;
always
for(i=0;i<7;i=i+1)
out[i]=(in==i);
endmodule
可以看出,不一样的语言描述,会画出不一样的原理图。
注意事项
1)Verilog 综合生成的3-8译码器电路原理图与原始设计电路存在一定差异,尤其是不详细描写其内部结构后,差异尤其明显,但是仿真测试生成的结果都与真值表一致。
2) Verilog代码设计的3-8译码器模块的输出信号之所以定义为reg类型而不用默认wire(导线)类型,是因为后面运用了过程语句always,因此被赋值的只能说reg变量。改成wire型(即把 output reg [7:0] out 改为 output [7:0] out)后,相应地也要将alway语句改为连续赋值语句assign。
全加器
一位全加器
先用Verilog的门级描述方式写一个“1位全加器”, 生成RTL电路,与Logisim的“1位全加器”进行对比。
Logisim的“1位全加器”图样如下:
Verilog代码:
1.功能级写法
module fulladder_1 {A,B,Cin,Sum,Cout};
input A,B,Cout;
output Sum,Cout;
assign {Cout,Sum}=A+B+Cin;
endmodule
2行为级写法
module fulladder_1(
input a,b,cin,
output sum,cout
);
assign sum=a^b^cin;
assign cout=(a&b)|(a&cin)|(b&cin);
endmodule
四位全加器
module FA(
input x,y,cin,
output f,cout
);
assign f=x^y^cin;
assign cout=(x&y)|(x&cin)|(y&cin);
endmodule
module CRA(
input [3:0]x,y,
input cin,
output [3:0]f,
output cout
);
wire [4:0]c;
assign c[0]=cin;
FA fa0(x[0],y[0],c[0],f[0],c[1]);
FA fa1(x[1],y[1],c[1],f[1],c[2]);
FA fa2(x[2],y[2],c[2],f[2],c[3]);
FA fa3(x[3],y[3],c[3],f[3],c[4]);
assign cout=c[4];
endmodule
采用Verilog设计一个8位全加器模块。
```c
module FA(
input x,y,cin,
output f,cout
);
assign f=x^y^cin;
assign cout=(x&y)|(x&cin)|(y&cin);
endmodule
module FA_8(
input [7:0]x,y,
input cin,
output [7:0]f,
output cout
);
wire [8:0]c;
assign c[0]=cin;
FA fa0(x[0],y[0],c[0],f[0],c[1]);
FA fa1(x[1],y[1],c[1],f[1],c[2]);
FA fa2(x[2],y[2],c[2],f[2],c[3]);
FA fa3(x[3],y[3],c[3],f[3],c[4]);
FA fa4(x[4],y[4],c[4],f[4],c[5]);
FA fa5(x[5],y[5],c[5],f[5],c[6]);
FA fa6(x[6],y[6],c[6],f[6],c[7]);
FA fa7(x[7],y[7],c[7],f[7],c[8]);
assign cout=c[8];
endmodule
16位ALU
Verilog设计
module ALU_16#(
parameter n=16
)(
input [n-1:0] opA, // 输入操作数A,长度为n
input [n-1:0] opB, // 输入操作数B,长度为n
input [3:0] S, // 输入选择信号S,长度为4
input wire M, // 输入M,控制加/减操作
input wire Cin, // 输入进位信号Cin
output reg[n-1:0] DO, // 输出结果DO,长度为n
output wire C, // 输出进位信号C
output wire V, // 输出溢出信号V
output wire N, // 输出负数信号N
output wire Z // 输出零信号Z
);
reg [n-1:0] p; // 中间变量p
reg [n-1:0] g; // 中间变量g
reg [n:0] carry; // 进位寄存器carry
always@(opA,opB,S,M,Cin)
begin
carry[0]=Cin; // 进位寄存器的第一个位等于Cin
g={n{S[3]}}&opA&opB | {n{S[2]}}&opA&(~opB) | {n{(~M)}}; // 计算g
p=~({n{S[3]}})&opA&opB | {n{S[2]}}&opA&(~opB) | {n{S[1]}}&(~opA)&opB | {n{S[0]}}&(~opA)&(~opB); // 计算p
carry[n:1]=g | (p & carry[n-1:0]); // 更新进位寄存器carry
DO=p^carry[n-1:0]; // 计算结果DO
end
assign C=carry[n]; // 连接进位信号C
assign V=(opA[n-1]&opB[n-1]&~DO[n-1]) | (~opA[n-1]&~opB[n-1]&DO[n-1]); // 计算溢出信号V
assign N=DO[n-1]; // 连接负数信号N
assign Z=(!(DO))?1:0; // 计算零信号Z
endmodule
RTL电路
实验总结
Verilog的许多编程思路同C语言相似,语法也相近。要格外注意使用过程语句编程时,输出端口要注意reg赋值。