基本数字电路的Verilog写法

一、基本运算符

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应为选择其中一个进行输出

8-1数据选择器

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

8-3编码器 case

8-3编码器 if else

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

3-8译码器

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

D触发器

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

4bit移位寄存器

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描述应该非常熟悉,才能够为后面的学习打下扎实的基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值