一、基本运算符
1.1、逻辑运算符与按位运算符
区别:
- 按位运算符是逐位进行逻辑运算,输出位数与输入位数一致
- 逻辑运算符进行逻辑运算,不关注输入的某一位而是将输入作为整体进行逻辑操作,输出位数为1,非0即1;
列举:
- 与:& 按位与;&& 逻辑与;
- 或:| 按位或; || 逻辑或;
- 非:~ 按位非; ! 逻辑非;
1.2、缩位运算符
灵活使用缩位运算符,可以简化代码;
如:
- &a 可实现a各位间的与运算,即&a等价与a(0)&a(1)&...&a(n)
- | a 可实现a各位间的或运算,即|a等价与a(0)|a(1)|...|a(n)
1.3、位拼接运算符
拼接功能:将reg/wire型变量首位连接,形成一个更大位宽的变量;
如:
a = 2'b10;
b = 3'b010;
{a,b}=5'b10010;
迭代功能:把一个变量复制多次,组成一个更大位宽的变量;(实际仍为拼接功能的一个特例:拼接元素相同)
如:a = 2'b10;
有{4{a}},即{a,a,a,a}
注意:
- 要保证迭代的完整性:{{4{a}},b}({4{a}}为迭代功能,括号不能少;即不能写为{ 4{a},b} )
- 位拼接运算符还可以用于常量操作:{ {4{1'b1}},2'b10}
1.4、逻辑移位运算符与算数移位运算符
列举:
<< 逻辑左移运算符;<<< 数字左移运算符;
>> 逻辑右移运算符;>>> 数字右移运算符;
区别:逻辑移位运算符不关心符号位;逻辑左移右端补零,逻辑右移左端补零;
数字左移位运算符不关心符号位,与逻辑左移一样;数字右移运算符关心符号位,左端补符号位;
如:
1010101010,其中[]是添加的位
逻辑左移一位:010101010[0]
算数左移一位:010101010[0]
逻辑右移一位:[0]101010101
算数右移一位:[1]101010101
//======================================================================
// --- 名称 : 基本运算符
// --- 作者 : 又菜又爱玩
// --- 日期 : 2023-10-25
// --- 描述 : 一些常用基本运算符,包括:算数运算符、关系运算符、逻辑运算符、逻辑等式运算符、三目条件运算符、位运算符、移位运算符、位拼接运算符
//======================================================================
module Example_Operation
(
input [3:0] a ,
input [3:0] b ,
input [3:0] c ,
output [3:0] c1 ,
output [3:0] c2 ,
output [5:0] c3 ,
output [3:0] c4 ,
output [3:0] c5 ,
output [3:0] d1 ,
output [3:0] d2 ,
output [3:0] d3 ,
output [3:0] d4 ,
output [3:0] e1 ,
output [3:0] e2 ,
output [3:0] e3 ,
output [3:0] f1 ,
output [3:0] f2 ,
output [3:0] d11 ,
output [3:0] c11 ,
output [3:0] c22 ,
output [3:0] c33 ,
output [3:0] c44 ,
output [3:0] c55 ,
output [3:0] c66 ,
output [3:0] e11 ,
output [3:0] e22 ,
output [7:0] f11 ,
output [7:0] f22
);
//== waveform1 算数运算符
//======================================================================
assign c1 = a + b; //加
assign c2 = a - b; //减
assign c3 = a * b; //乘
assign c4 = a / b; //除
assign c5 = a % b; //求余
//== waveform2 关系运算符
//======================================================================
assign d1 = a > b; //大于
assign d2 = a < b; //小于
assign d3 = a >= b; //大于等于
assign d4 = a <= b; //小于等于
//== waveform3 逻辑运算符
//======================================================================
assign e1 = !a; //非
assign e2 = a && b; //与
assign e3 = a || b; //或
//== waveform4 逻辑等式运算符
//======================================================================
assign f1 = a == b; //判断相等
assign f2 = a != b; //判断不等
//== waveform5 三目条件运算符
//======================================================================
assign d11 = a ? b : c; //a为真,则d11 = b
//a为假,则d11 = c
//== waveform6 位运算符
//======================================================================
assign c11 = ~a; //取反
assign c22 = a & b; //按位与
assign c33 = a | b; //按位或
assign c44 = a ^ b; //按位异或
assign c55 = a ~^ b; //按位同或
assign c66 = a ^~ b; //按位同或
//== waveform7 移位运算符
//======================================================================
assign e11 = a << b; //左移
assign e22 = a >> b; //右移
//== waveform8 位拼接运算符
//======================================================================
assign f11 = {a , b}; //拼接a和b,a和b位宽均为4,f11位宽为8
assign f22 = {2'd2{b}}; //拼接b低2位
endmodule
二、三人表决器
Verilog对电路功能的描述有三种方式:结构化描述、数据流描述、行为级描述。三种描述方式抽象级别不同,各有优缺点,相辅相成,需要配合使用
2.1、结构化描述
结构化描述的抽象级别最低,是最接近实际硬件结构的描述方式。
特点:
- 直接采用结构化描述语句描述,需要描述实现功能所需数字电路的逻辑关系,极其复杂;
- 结构化描述语句常用于层次化模块间的调用、以及ip核的例化等;
电路抽象:
要按照结构化描述来实现这一功能,首先应进行电路抽象。即先抽象出用何种电路实现这一功能,才能进行随后的描述。
经过卡诺图化简,拟采用与、非组合逻辑实现这一功能,即:
O = AB+AC+BC
实现代码:
//======================================================================
// --- 名称 : 三人表决器
// --- 作者 : 又菜又爱玩
// --- 日期 : 2023-10-26
// --- 描述 : 使用结构化描述,描述硬件电路实现三人表决器
//======================================================================
module Example_Structure
(
input A , //模块的输入端口A
input B , //模块的输入端口B
input C , //模块的输入端口C
output L //模块的输出端口L
);
//== 信号定义
//======================================================================
wire AB,BC,AC; //内部信号声明AB,BC,AC
and U1(AB,A,B); //与门(A,B信号进入)(A与B信号即AB输出)
and U2(BC,B,C); //与门 同上
and U3(AC,A,C); //与门 同上
or U4(L,AB,BC,AC); //或门 同上
endmodule
2.2、数据流描述
概念:
数据流描述抽象级别较高,不再需要清晰的刻画具体的数字电路,而比较直观的表达底层逻辑。其又称为寄存器传输级(RTL)描述。
特点:
- 从数据的变换和传送角度描述模块
- 抽象级别适中,即显示的表达了模块的行为,又隐式的刻画了模块的电路结构;
实现代码:
//======================================================================
// --- 名称 : 三人表决器-数据流描述方式
// --- 作者 : 又菜又爱玩
// --- 日期 : 2023-10-26
// --- 描述 : 数据流方式进行Verilog编写,实现三人表决器,两人即以上同意时输出1,其它输出0
//======================================================================
module Example_Dataflow
(
input A , //模块的输入端口A
input B , //模块的输入端口B
input C , //模块的输入端口C
output L //模块的输出端口L
);
assign L = ((!A) & B & C) | (A & (!B) & C) | (A & B & (!C)) | (A & B & C);
endmodule
2.3、行为描述方式
概念:
行为级描述抽象级别最高,概括能力最强
特点:
- 概括能力及强,不关注电路实现,只描述数据逻辑。
- 抽象级别高,综合效率低,电路可控性差;
实现代码:
//======================================================================
// --- 名称 : 三人表决器-行为描述方式
// --- 作者 : 又菜又爱玩
// --- 日期 : 2023-10-26
// --- 描述 : 行为方式进行Verilog编写,实现三人表决器,两人即以上同意时输出1,其它输出0
//======================================================================
module Example_Behavior
(
input A , //模块的输入端口A
input B , //模块的输入端口B
input C , //模块的输入端口C
output reg L //模块的输出端口L
);
always @(A,C,B)begin //敏感列表只需要A、B、C,也可以写成always @(*)
case({A,B,C}) //注意{A,B,C}是位拼接,合成一条总线
3'b000: L = 1'b0;
3'b001: L = 1'b0;
3'b010: L = 1'b0;
3'b011: L = 1'b1;
3'b100: L = 1'b0;
3'b101: L = 1'b1;
3'b110: L = 1'b1;
3'b111: L = 1'b1;
default:L = 1'bx; //default不要省略
endcase
end
endmodule
3、模块化设计实现半加器
实现代码:
TOP:
//======================================================================
// --- 名称 : 基本运算符
// --- 作者 : 又菜又爱玩
// --- 日期 : 2023-10-26
// --- 描述 : 模块化实现半加器
//======================================================================
module Example_Module
(
input a ,
input b ,
output s ,
output c
);
//== 实例化 与门
//======================================================================
Example_yumen yumen_module
(
.yumen_a(a),
.yumen_b(b),
.yumen_c(c)
);
//== 实例化 异或
//======================================================================
Example_yihuo yihuo_module
(
.yihuo_a(a),
.yihuo_b(b),
.yihuo_s(s)
);
endmodule
YIHUO:
module Example_yihuo
(
input yihuo_a ,
input yihuo_b ,
output yihuo_c
);
assign yihuo_c = yihuon_a ^ yihuo_b;
endmodule
YUMEN:
module Example_yumen
(
input yumen_a ,
input yumen_b ,
output yumen_c
);
assign yumen_c = yumen_a && yumen_b;
endmodule
4、8-1数据选择器
实现代码:
//======================================================================
// --- 名称 : 8-1数据选择器
// --- 作者 : 又菜又爱玩
// --- 日期 : 2023-10-26
// --- 描述 : 输入8个数据D0-D7,根据另一个输入,对输出进行选择D0-D7。
//======================================================================
module Digital_Selector
(
input D0 ,
input D1 ,
input D2 ,
input D3 ,
input D4 ,
input D5 ,
input D6 ,
input D7 ,
input [ 2:0] A ,
output reg [ 7:0] Y
);
always @(*)begin
case(A)
3'b000 : Y = D0;
3'b001 : Y = D1;
3'b010 : Y = D2;
3'b011 : Y = D3;
3'b100 : Y = D4;
3'b101 : Y = D5;
3'b110 : Y = D6;
3'b111 : Y = D7;
default: Y = 1'b0;
endcase
end
endmodule
此处有误:输出Y应为1bit,因为输入为8个1bit的数据,输出Y应为选择其中一个进行输出
5、8-3编码器
实现代码:
//======================================================================
// --- 名称 : 8-3编码器
// --- 作者 : 又菜又爱玩
// --- 日期 : 2023-10-27
// --- 描述 : 对输入的8个数字进行编码,成为3为二进制数。
//======================================================================
module Digital_Encoder
(
input [7:0] I,
output [2:0] A
)
//== case判断I,8位数转为3位数
//======================================================================
always @(*)begin
case(I)
8'b0000_0001 : A = 3'b000;
8'b0000_0010 : A = 3'b001;
8'b0000_0100 : A = 3'b010;
8'b0000_1000 : A = 3'b011;
8'b0001_0000 : A = 3'b100;
8'b0010_0000 : A = 3'b101;
8'b0100_0000 : A = 3'b110;
8'b1000_0000 : A = 3'b111;
default: A = 3'b000;
endcase
end
/*
//== if...else优先级写法,优先级从上到下
//======================================================================
always @(*)begin
if(I[7] == 1'b0) A = 3'b000;
else if(I[6] == 1'b0) A = 3'b001;
else if(I[5] == 1'b0) A = 3'b010;
else if(I[4] == 1'b0) A = 3'b011;
else if(I[3] == 1'b0) A = 3'b100;
else if(I[2] == 1'b0) A = 3'b101;
else if(I[1] == 1'b0) A = 3'b110;
else if(I[0] == 1'b0) A = 3'b111;
else A = 3'b000;
end
*/
endmodule
6、3-8译码器
实现代码:
//======================================================================
// --- 名称 : 3-8译码器
// --- 作者 : 又菜又爱玩
// --- 日期 : 2023-10-28
// --- 描述 : 对输入的三位二进制数进行译码
//======================================================================
module Digital_Decoder (
input [2:0] A,
output [7:0] I
);
always @(*) begin
case(A)
3'b000:I = 8'b0000_0001;
3'b001:I = 8'b0000_0010;
3'b010:I = 8'b0000_0100;
3'b011:I = 8'b0000_1000;
3'b100:I = 8'b0001_0000;
3'b101:I = 8'b0010_0000;
3'b110:I = 8'b0100_0000;
3'b111:I = 8'b1000_0000;
default:I = 8'b0000_0000;
endcase
end
endmodule
7、D触发器
实现代码:
//======================================================================
// --- 名称 : D触发器
// --- 作者 : 又菜又爱玩
// --- 日期 : 2023-10-26
// --- 描述 :
//======================================================================
module Digital_Data_Flip_Flop
(
input clk ,
input rst ,
input D ,
output Q
);
always @(posedge clk or negedge rst)
begin
if(!rst)
Q <= 1'b0;
else
Q <= D;
end
endmodule
8、4bit移位寄存器
实现代码:
//======================================================================
// --- 名称 : 4bit移位寄存器
// --- 作者 : 又菜又爱玩
// --- 日期 : 2023-10-26
// --- 描述 : 4bit移位寄存器
//======================================================================
module Digital_Shift_Reg (
input clk ,
input rst ,
input data_in ,
input data_en ,
output reg [3:0] data_out ,
output reg [3:0] data_out_n
);
//== 时序逻辑,寄存data_out_n的值,所以看起来比data_out_n慢一拍
//======================================================================
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
data_out <= 4'b0;
else
data_out <= data_out_n;
end
//== 组合逻辑,不断移位
//======================================================================
always @(*)begin
if(data_en)
data_out_n = {data_out[2:0],data_in};
else
data_out_n = data_out;
end
/*----------------------------------------------------------------------
--补充:如果要实现循环右移,则写成[data_in,data_out[3:1]]
----------------------------------------------------------------------*/
endmodule
9、反馈回环的正误解析
实现代码:
//======================================================================
// --- 名称 : 反馈回环的正误解析
// --- 作者 : 又菜又爱玩
// --- 日期 : 2023-10-26
// --- 描述 : 反馈回环的正误解析
//======================================================================
//== 错误写法:data_out既是条件又是结果
//======================================================================
module Example_Feedback
(
input data_in1 ,
input data_in2 ,
output data_out
);
//data_out是最终结果,可又是形成条件
assign data_out = (data_in2) ? data_in1 : (~data_out | data_in1);
endmodule
/*
//== 正确写法:用data_out_r寄存一下,再给data_out
//======================================================================
module Example_Feedback
(
input clk ,
input rst_n ,
input data_in1 ,
input data_in2 ,
output data_out
);
//信号定义
reg data_out_r ;
//时序逻辑,寄存结果
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
data_out_r <= 1'b0;
else
data_out_r <= (data_in2) ? (data_in1) : (~data_out_r | data_in1);
end
//寄存后的结果再输出
assign data_out = data_out_r;
endmodule
*/
10、阻塞赋值与非阻塞赋值
实现代码:
TOP:
module Example_Block
(
input clk ,
input block_in ,
output block_out1 ,
output block_out2 ,
output no_block_out1 ,
output no_block_out2
);
//block模块例化
block block_init
(
.clk (clk ),
.block_in (block_in ),
.block_out1 (block_out1 ),
.block_out2 (block_out2 )
);
//no_block模块例化
no_block no_block_init
(
.clk (clk ),
.no_block_in (block_in ),
.no_block_out1 (no_block_out1 ),
.no_block_out2 (no_block_out2 )
);
endmodule
BLOCK:
module block
(
input clk ,
input block_in ,
output reg block_out1 ,
output reg block_out2
);
always @(posedge clk)begin
block_out1 = block_in;
block_out2 = block_out1;
end
endmodule
NO_BLOCK:
module block
(
input clk ,
input block_in ,
output reg block_out1 ,
output reg block_out2
);
always @(posedge clk)begin
block_out1 = block_in;
block_out2 = block_out1;
end
endmodule
这些基础数字电路的Verilog描述应该非常熟悉,才能够为后面的学习打下扎实的基础。