天天看點

FPGA/verilog 學習筆記(4)—— 子產品設計和示例一、子產品和設計風格二、各種子產品的示例和testbench

文章目錄

  • 一、子產品和設計風格
    • 1. 子產品
    • 2. 描述設計的四種風格
      • (1)資料流風格
      • (2)行為風格
      • (3)結構風格描述
      • (4)混合設計風格的描述
  • 二、各種子產品的示例和testbench
    • (1)一位加法器
    • (2)16位計數器
    • (3)3-8譯碼器
    • (4)可複位寄存器
    • (5)(帶參數)二選一多選器
    • (6)四選一多選器
    • (7)寄存器堆示例(計算機組成原理)
    • (8)ALU示例1(計算機組成原理)
    • (9)ALU示例2(計算機組成原理)

一、子產品和設計風格

1. 子產品

  • verilog中的基本描述機關是子產品
  • 子產品的描述包括以下兩個部分
    • 某個設計的功能或結構
    • 子產品與其他外部子產品的通信端口
  • 描述子產品的方法
    • 使用開關原語、門級原語等,對設計的結構進行描述
    • 使用連續指派語句(assign)對設計的資料流進行描述
    • 使用過程性結構(always、initial等)對設計的時序行為進行描述
    • 在子產品内可以引用另一個子產品

2. 描述設計的四種風格

(1)資料流風格

  • 使用資料流風格對設計進行模組化的基本機制是采用連續指派語句

    assgin

  • 文法:

    assign [delay] LHS_net = RHS_expression;

    • 右邊表達式的使用的操作數無論何時發生變化,右值都将被重新計算,并在指定的延遲後賦予左邊的線網類型變量。如果沒有規定延時,預設延時為0
  • 可以把這了解為一個連接配接的導線,導線右邊任意時刻的變化都會傳遞到左邊

(2)行為風格

  • 設計的行為功能通過以下過程性結構進行描述
    • initial

      語句:此語句内僅在程式最初運作一次
    • always

      語句:此語句總是在循環執行中
  • 所有的

    initial

    always

    語句都在0時刻并發執行
  • 隻有變量類型能在這兩種語句中被指派。這種類型的變量資料在被指派前保持原有值不變。
  • 可以把這了解為一個時序部件,需要時鐘信号控制
  • 這種風格比較抽象

(3)結構風格描述

  • 在verilog中,可以用以下四種構造對電路結構進行描述
    • 内建門級原語(基元):在門級描述電路(如

      and

      xor

      or

      等内建基元門)
    • 開關級原語(基元):在半導體級描述電路
    • 使用者定義的原語(基元):在門級描述電路
    • 子產品執行個體:建立層次結構描述電路
  • 用線網可以連接配接各個基元和子產品
  • 這個層面上,有點像畫電路圖,最貼近底層

(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

           

繼續閱讀