FPGA/verilog 学习笔记(4)—— 模块设计和示例

本文深入探讨了Verilog HDL的模块设计与测试方法,涵盖了数据流、行为、结构及混合设计风格,提供了多种模块实例,包括加法器、计数器、译码器、寄存器等,以及详细的testbench示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、模块和设计风格

1. 模块

  • verilog中的基本描述单位是模块
  • 模块的描述包括以下两个部分
    • 某个设计的功能或结构
    • 模块与其他外部模块的通信端口
  • 描述模块的方法
    • 使用开关原语、门级原语等,对设计的结构进行描述
    • 使用连续赋值语句(assign)对设计的数据流进行描述
    • 使用过程性结构(always、initial等)对设计的时序行为进行描述
    • 在模块内可以引用另一个模块

2. 描述设计的四种风格

(1)数据流风格

  • 使用数据流风格对设计进行建模的基本机制是采用连续赋值语句assgin
  • 语法:assign [delay] LHS_net = RHS_expression;
    • 右边表达式的使用的操作数无论何时发生变化,右值都将被重新计算,并在指定的延迟后赋予左边的线网类型变量。如果没有规定延时,默认延时为0
  • 可以把这理解为一个连接的导线,导线右边任意时刻的变化都会传递到左边

(2)行为风格

  • 设计的行为功能通过以下过程性结构进行描述
    • initial语句:此语句内仅在程序最初运行一次
    • always语句:此语句总是在循环执行中
  • 所有的initialalways语句都在0时刻并发执行
  • 只有变量类型能在这两种语句中被赋值。这种类型的变量数据在被赋值前保持原有值不变。
  • 可以把这理解为一个时序部件,需要时钟信号控制
  • 这种风格比较抽象

(3)结构风格描述

  • 在verilog中,可以用以下四种构造对电路结构进行描述
    • 内建门级原语(基元):在门级描述电路(如andxoror等内建基元门)
    • 开关级原语(基元):在晶体管级描述电路
    • 用户定义的原语(基元):在门级描述电路
    • 模块实例:创建层次结构描述电路
  • 用线网可以连接各个基元和模块
  • 这个层面上,有点像画电路图,最贴近底层

(4)混合设计风格的描述

  • 在模块中,结构的构造和行为的构造可以自由混合
  • 模块描述中可以包括
    • 门示实例的引用 (触发always和initial)
    • 模块实例的引用
    • 连续赋值语句assign(触发always和initial)
    • always语句、initial语句(驱动门和开关)
    • 其他语句

二、各种模块的示例和testbench

(1)一位加法器

  • 模块
module add1(a,b,cin,sum,cout);
	input a,b,cin;
	output sum,cout;

	wire q = a&b;	
	wire g = a^b;

	//assign {cout,sum} = a + b + cin;
	assign sum = cin ^ g;
	assign cout = cin & g | q;
endmodule
  • testbench
module add1_tb;

reg a,b,cin;
wire sum,cout;

initial begin
	a = 1'b0; b = 1'b0; cin = 1'b0;	#100
	a = 1'b0; b = 1'b0; cin = 1'b1;	#100
	a = 1'b0; b = 1'b1; cin = 1'b0;	#100
	a = 1'b0; b = 1'b1; cin = 1'b1;	#100
	a = 1'b1; b = 1'b0; cin = 1'b0;	#100
	a = 1'b1; b = 1'b0; cin = 1'b1;	#100
	a = 1'b1; b = 1'b1; cin = 1'b0;	#100
	a = 1'b1; b = 1'b1; cin = 1'b1;	
end

add1 myAdd1(
	.a(a),
	.b(b),
	.cin(cin),
	.sum(sum),
	.cout(cout)
);

endmodule

(2)16位计数器

  • 模块
module counter16(clk,reset,d);
	input clk,reset;
	output reg[0:3] d;	

	always@(posedge clk)	
		if(reset)
			d <= 0;
		else
			d <= d+1;
	
endmodule
  • testbench
`timescale 1ns/1ns	
module counter16_tb;

reg clk,rst;
wire[3:0] out;
integer i;


initial begin
	i = 0;
	clk = 1'b0;
	rst = 1'b1;
	#100
	rst = 1'b0;	
end

always begin
	#10 
	clk = ~clk;
	
	i = i+1;
	if(i == 10) begin
		rst = ~rst;
		i = 0;
	end
		
end

counter16 myCounter(clk,rst,out);

endmodule

(3)3-8译码器

  • 模块
module dec3_8(a,y);
    input [2:0] a;
    output[7:0] y;

    assign y[0] = (a==3'b000);
    assign y[1] = (a==3'b001);
    assign y[2] = (a==3'b010);
    assign y[3] = (a==3'b011);
    assign y[4] = (a==3'b100);
    assign y[5] = (a==3'b101);
    assign y[6] = (a==3'b110);
    assign y[7] = (a==3'b111);

endmodule

/*-----------------其他实现方式------------------

1. 使用case语句
module dec3_8(a,y);
    input [2:0] a;
    output[7:0] y;

    reg[7:0] yy;
    assign y = yy;      //yy在always块种被赋值,必须是reg型变量
    always@(a) begin    //电平触发,a变化就会触发执行
        case(a)
            3'b000: yy = 8'b0000_0001;
            3'b001: yy = 8'b0000_0010;
            3'b010: yy = 8'b0000_0100;
            3'b011: yy = 8'b0000_1000;
            3'b100: yy = 8'b0001_0000;
            3'b101: yy = 8'b0010_0000;
            3'b110: yy = 8'b0100_0000;
            3'b111: yy = 8'b1000_0000;
        endcase
    end
endmodule

2. 行为抽象级别更高
module dec3_8(a,y);
    input[2:0]  a;
    output[7:0] y;

    assign y = 1<<a;
endmodule

*/
  • testbench
module dec3_8_tb;
    reg[2:0]    s;
    wire[7:0]   y;

    initial begin
        s = 3'b0;      
    end

    always #10 s=s+1;
    
    dec3_8 dec(
        .a(s),
        .y(y)
    );

endmodule

(4)可复位寄存器

  • 模块
//可复位寄存器
module floper(clk,rst,d,q);
    input clk,rst;
    input[3:0] d;
    output reg[3:0] q;

    always @(posedge clk or negedge rst)
        if(!rst)    
        	q <= 4'b0;  
        else        
        	q <= d;

endmodule         

  • testbench
module floper_tb;
    reg clk,rst;
    reg[3:0] d;
    wire[3:0] q;

    initial begin
        clk = 1'b0;
        rst = 1'b0;
        d = 4'b1010;

        #30 rst = 1'b1;         //30ns后取消复位,这时正好clk上升沿,直接写入1010
    end 

    initial #60 d = 4'b0101;    //60ns后写入值改变,此时clk是下降沿不写,等到70ns时写入0101

    always #10 clk = ~clk; 
    
    floper f(
        .clk(clk),
        .rst(rst),
        .d(d),
        .q(q)
    );

endmodule

(5)(带参数)二选一多选器

  • 模块
module mux2(a,b,s,y);	
	input s;
	input [7:0]a,b;
	output[7:0]y;

	assign y = (s==0)?a:b;
endmodule

/*------------ 带位宽参数的写法------------------
module mux2_para #(parameter WIDTH=8)(a,b,s,y);
    input s;
    input[WIDTH-1:0] a,b;
    output[WIDTH-1:0] y;

    assign y = (s==0) ? a:b;
endmodule
*/
  • testbench
`timescale 1ns/1ns	
module mux2_tb;

reg clk;
reg[7:0] a,b;
wire[7:0] out;

initial begin
	a = 8'b0;
	b = 8'b1111_1111;
	
	clk = 1'b0;
end

always #10 clk=~clk;

mux2 myMux2(
	.a(a),
	.b(b),
	.s(clk),
	.y(out)
);

endmodule

(6)四选一多选器

  • 模块
module mux4(d0,d1,d2,d3,s,y);
    input[3:0] d0,d1,d2,d3;
    input[1:0] s;
    output[3:0] y;

    wire[3:0]   low,high;
    
    //先选出低位为s[0]的
    mux2 lowmux(d0,d1,s[0],low);
    mux2 highmux(d2,d3,s[0],high);
    
    //再选出高位为s[1]的
    mux2 finalmux(low,high,s[1],y);
endmodule

  • testbench
module mux4_tb;

    integer i;
    reg[1:0] s;
    reg[3:0] i0,i1,i2,i3;
    wire[3:0] out;

    initial begin
        i0 = 4'b0;
        i1 = 4'b0101;
        i2 = 4'b1010;
        i3 = 4'b1111;
        i = 0;
        s = 2'b0;
    end

    always begin
        #10 
        i = i + 1;
        if(i > 3)
            i = 0;
        s = i;
    end

    mux4 myMux4(
        .d0(i0),
        .d1(i1),
        .d2(i2),
        .d3(i3),
        .s(s),
        .y(out)
    );
endmodule

(7)寄存器堆示例(计算机组成原理)

  • 模块
module regFile(clk,reset,s,wEn,din,dout);
    input clk,reset,wEn;			//时钟,复位,写使能
    input[1:0]  s;      			//从regFile的四个单位中选择
    input[7:0]  din;    
    output[7:0] dout;

    reg[7:0]    R[0:3]; 			//regFile的存储空间,4个字节 

    assign dout = R[s]; 			//把R[s]连接到dout,内部信号传出来通常用assign,非时序电路,用=

    always @(posedge clk or negedge reset)  
    begin
            if(!reset)  begin
                R[0] <= 0;      	//给寄存器写值,这是时序电路,用<=      
                R[1] <= 1;
                R[2] <= 2;
                R[3] <= 3;
            end
            else if (wEn)   
                R[s] <= din;

        end
endmodule
  • testbench
module regFile_tb;
    reg clk,rst,wEn;
    reg[1:0]    s;
    reg[7:0]    in;
    wire[7:0]   out;

    initial begin 
        rst = 1'b0;
        wEn = 1'b0;
        clk = 1'b0;
        s = 2'b0;
        in = 8'b1111_1111;
    end

    always #10 clk = ~clk;

    always @(posedge clk) begin
        in = in+1;
        s = in % 4;
        //从第四个clk上升沿开始写入数据,由于读是非时序电路assign,写是时序电路<=,每次都是读出旧数据,写入新数据(下个周期读出)
        if(in>=4) begin 
            rst = 1'b1;
            wEn = 1'b1;
        end
    end

    regFile rf(
        .clk(clk),
        .reset(rst),
        .s(s),
        .wEn(wEn),
        .din(in),
        .dout(out)
    );

endmodule

(8)ALU示例1(计算机组成原理)

  • 模块
module ALU_1(op,A,B,result);
    input[2:0]  op;
    input[7:0]  A,B;
    output[7:0] result;

    reg[7:0]    result;
    always @(op or A or B) begin
       if(op == 3'b000)         result = A+B;
       else if(op == 3'b001)    result = A-B;
       else if(op == 3'b010)    result = A&B;
       else if(op == 3'b011)    result = A|B;
       else if(op == 3'b100)    result = ~A;
       else                     result = 8'b0;
    end

endmodule
  • testbench
module ALU_1_tb;
    reg[7:0] A,B;
    reg[2:0] op;
    wire[7:0] res;

    initial begin
        A = 8'b1010_1010;
        B = 8'b0101_0101;
        op = 3'b0;
    end

    always #10 op = op+1;

    ALU_1 alu(
        .op(op),
        .A(A),
        .B(B),
        .result(res)
    );

endmodule

(9)ALU示例2(计算机组成原理)

  • 模块
module ALU_2(op,A,B,ci,result,co);
    input[2:0]  op;
    input[7:0]  A,B;
    input ci;
    
    output[7:0] result;
    output co;

    reg[7:0] result;
    reg co;
    
    always @(op or A or B) begin
        case(op)
            3'd0: {co,result} = A + B;
            3'd1: {co,result} = A + B + ci;
            3'd2: {co,result} = A - B - ci;
            3'd3: result = A & B;
            3'd4: result = A | B;
            3'd5: result = A ^ B;
            3'd6: result = ~A;
            default: begin  
                co = 0;
                result = 8'd0;
            end
        endcase        
    end

endmodule
  • testbench
module ALU_2_tb;
    reg[7:0] A,B;
    reg[2:0] op;
    reg ci;
    wire[7:0] res;
    wire co;


    initial begin
        A = 8'b1010_1010;
        B = 8'b0101_0101;
        ci = 1;
        op = 0;
    end

    always #10 op = op+1;

    ALU_2 alu(
        .op(op),
        .A(A),
        .B(B),
        .ci(ci),
        .result(res),
        .co(co)
    );

endmodule

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云端FFF

所有博文免费阅读,求打赏鼓励~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值