国产chinesehdxxxx野外,国产av无码专区亚洲av琪琪,播放男人添女人下边视频,成人国产精品一区二区免费看,chinese丰满人妻videos

Verilog 流水線

2022-05-18 10:23 更新

關(guān)鍵詞:流水線,乘法器

硬件描述語言的一個突出優(yōu)點(diǎn)就是指令執(zhí)行的并行性。多條語句能夠在相同時鐘周期內(nèi)并行處理多個信號數(shù)據(jù)。

但是當(dāng)數(shù)據(jù)串行輸入時,指令執(zhí)行的并行性并不能體現(xiàn)出其優(yōu)勢。而且很多時候有些計(jì)算并不能在一個或兩個時鐘周期內(nèi)執(zhí)行完畢,如果每次輸入的串行數(shù)據(jù)都需要等待上一次計(jì)算執(zhí)行完畢后才能開啟下一次的計(jì)算,那效率是相當(dāng)?shù)偷?。流水線就是解決多周期下串行數(shù)據(jù)計(jì)算效率低的問題。

流水線

流水線的基本思想是:把一個重復(fù)的過程分解為若干個子過程,每個子過程由專門的功能部件來實(shí)現(xiàn)。將多個處理過程在時間上錯開,依次通過各功能段,這樣每個子過程就可以與其他子過程并行進(jìn)行。

假如一個洗衣店內(nèi)洗衣服的過程分為 4 個階段:取衣、洗衣、烘干、裝柜。每個階段都需要半小時來完成,則洗一次衣服需要 2 小時。

考慮最差情況,洗衣店內(nèi)只有一臺洗衣機(jī)、一臺烘干機(jī)、一個衣柜。如果每半小時送來一批要洗的衣服,每次等待上一批衣服洗完需要 2 小時,那么洗完 4 批衣服需要的時間就是 8 小時。

圖示如下:


對這個洗衣店的裝備進(jìn)行升級,一共引進(jìn) 4 套洗衣服的裝備,工作人員也增加到 4 個,每個人負(fù)責(zé)一個洗衣階段。所以每批次的衣服,都能夠及時的被相同的人放入到不同的洗衣機(jī)內(nèi)。由于時間上是錯開的,每批次的衣服都能被相同的人在不同的設(shè)備與時間段(半小時)內(nèi)洗衣、烘干和裝柜。圖示如下。


可以看出,洗完 4 批衣服只需要 3 個半小時,效率明顯提高。

其實(shí),在 2 小時后第一套洗衣裝備已經(jīng)完成洗衣過程而處于空閑狀態(tài),如果此時還有第 5 批衣服的送入,那么第一套設(shè)備又可以開始工作。依次類推,只要衣服批次不停的輸入,4 臺洗衣設(shè)備即可不間斷的完成對所有衣服的清洗過程。且除了第一批次洗衣時間需要 2 小時,后面每半小時都會有一批次衣服清洗完成。

衣服批次越多,節(jié)省的時間就越明顯。假如有 N 批次衣服,需要的時間為 (4+N) 個半小時。

當(dāng)然,升級后洗衣流程也有缺點(diǎn)。設(shè)備和工作人員的增加導(dǎo)致了投入的成本增加,洗衣店內(nèi)剩余空間也被縮小,工作狀態(tài)看起來比較繁忙。

和洗衣服過程類似,數(shù)據(jù)的處理路徑也可以看作是一條生產(chǎn)線,路徑上的每個數(shù)字處理單元都可以看作是一個階段,會產(chǎn)生延時。

流水線設(shè)計(jì)就是將路徑系統(tǒng)的分割成一個個數(shù)字處理單元(階段),并在各個處理單元之間插入寄存器來暫存中間階段的數(shù)據(jù)。被分割的單元能夠按階段并行的執(zhí)行,相互間沒有影響。所以最后流水線設(shè)計(jì)能夠提高數(shù)據(jù)的吞吐率,即提高數(shù)據(jù)的處理速度。

流水線設(shè)計(jì)的缺點(diǎn)就是,各個處理階段都需要增加寄存器保存中間計(jì)算狀態(tài),而且多條指令并行執(zhí)行,勢必會導(dǎo)致功耗增加。

下面,設(shè)計(jì)一個乘法器,并對是否采用流水線設(shè)計(jì)進(jìn)行對比。

一般乘法器設(shè)計(jì)

前言

也許有人會問,直接用乘號 ?*? 來完成 2 個數(shù)的相乘不是更快更簡單嗎?

如果你有這個疑問,說明你對硬件描述語言的認(rèn)知還有所不足。就像之前所說,Verilog 描述的是硬件電路,直接用乘號完成相乘過程,編譯器在編譯的時候也會把這個乘法表達(dá)式映射成默認(rèn)的乘法器,但其構(gòu)造不得而知。

例如,在 FPGA 設(shè)計(jì)中,可以直接調(diào)用 IP 核來生成一個高性能的乘法器。在位寬較小的時候,一個周期內(nèi)就可以輸出結(jié)果,位寬較大時也可以流水輸出。在能滿足要求的前提下,可以謹(jǐn)慎的用 ?*? 或直接調(diào)用 IP 來完成乘法運(yùn)算。

但乘法器 IP 也有很多的缺陷,例如位寬的限制,未知的時序等。尤其使用乘號,會為數(shù)字設(shè)計(jì)的不確定性埋下很大的隱瞞。

很多時候,常數(shù)的乘法都會用移位相加的形式實(shí)現(xiàn),例如:

A = A<<1 ;       //完成A * 2
A = (A<<1) + A ;   //對應(yīng)A * 3
A = (A<<3) + (A<<2) + (A<<1) + A ; //對應(yīng)A * 15

用一個移位寄存器和一個加法器就能完成乘以 3 的操作。但是乘以 15 時就需要 3 個移位寄存器和 3 個加法器(當(dāng)然乘以 15 可以用移位相減的方式)。

有時候數(shù)字電路在一個周期內(nèi)并不能夠完成多個變量同時相加的操作。所以數(shù)字設(shè)計(jì)中,最保險(xiǎn)的加法操作是同一時刻只對 2 個數(shù)據(jù)進(jìn)行加法運(yùn)算,最差設(shè)計(jì)是同一時刻對 4 個及以上的數(shù)據(jù)進(jìn)行加法運(yùn)算。

如果設(shè)計(jì)中有同時對 4 個數(shù)據(jù)進(jìn)行加法運(yùn)算的操作設(shè)計(jì),那么此部分設(shè)計(jì)就會有危險(xiǎn),可能導(dǎo)致時序不滿足。

此時,設(shè)計(jì)參數(shù)可配、時序可控的流水線式乘法器就顯得有必要了。

設(shè)計(jì)原理

和十進(jìn)制乘法類似,計(jì)算 13 與 5 的相乘過程如下所示:


由此可知,被乘數(shù)按照乘數(shù)對應(yīng) bit 位進(jìn)行移位累加,便可完成相乘的過程。

假設(shè)每個周期只能完成一次累加,那么一次乘法計(jì)算時間最少的時鐘數(shù)恰好是乘數(shù)的位寬。所以建議,將位寬窄的數(shù)當(dāng)做乘數(shù),此時計(jì)算周期短。

乘法器設(shè)計(jì)

考慮每次乘法運(yùn)算只能輸出一個結(jié)果(非流水線設(shè)計(jì)),設(shè)計(jì)代碼如下。

module    mult_low
    #(parameter N=4,
      parameter M=4)
     (
      input                     clk,
      input                     rstn,
      input                     data_rdy ,  //數(shù)據(jù)輸入使能
      input [N-1:0]             mult1,      //被乘數(shù)
      input [M-1:0]             mult2,      //乘數(shù)

      output                    res_rdy ,   //數(shù)據(jù)輸出使能
      output [N+M-1:0]          res         //乘法結(jié)果
      );

    //calculate counter
    reg [31:0]           cnt ;
    //乘法周期計(jì)數(shù)器
    wire [31:0]          cnt_temp = (cnt == M)? 'b0 : cnt + 1'b1 ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            cnt    <= 'b0 ;
        end
        else if (data_rdy) begin    //數(shù)據(jù)使能時開始計(jì)數(shù)
            cnt    <= cnt_temp ;
        end
        else if (cnt != 0 ) begin  //防止輸入使能端持續(xù)時間過短
            cnt    <= cnt_temp ;
        end
        else begin
            cnt    <= 'b0 ;
        end
    end

    //multiply
    reg [M-1:0]          mult2_shift ;
    reg [M+N-1:0]        mult1_shift ;
    reg [M+N-1:0]        mult1_acc ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            mult2_shift    <= 'b0 ;
            mult1_shift    <= 'b0 ;
            mult1_acc      <= 'b0 ;
        end
        else if (data_rdy && cnt=='b0) begin  //初始化
            mult1_shift    <= {{(N){1'b0}}, mult1} << 1 ;  
            mult2_shift    <= mult2 >> 1 ;  
            mult1_acc      <= mult2[0] ? {{(N){1'b0}}, mult1} : 'b0 ;
        end
        else if (cnt != M) begin
            mult1_shift    <= mult1_shift << 1 ;  //被乘數(shù)乘2
            mult2_shift    <= mult2_shift >> 1 ;  //乘數(shù)右移,方便判斷
            //判斷乘數(shù)對應(yīng)為是否為1,為1則累加
            mult1_acc      <= mult2_shift[0] ? mult1_acc + mult1_shift : mult1_acc ;
        end
        else begin
            mult2_shift    <= 'b0 ;
            mult1_shift    <= 'b0 ;
            mult1_acc      <= 'b0 ;
        end
    end

    //results
    reg [M+N-1:0]        res_r ;
    reg                  res_rdy_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            res_r          <= 'b0 ;
            res_rdy_r      <= 'b0 ;
        end  
        else if (cnt == M) begin
            res_r          <= mult1_acc ;  //乘法周期結(jié)束時輸出結(jié)果
            res_rdy_r      <= 1'b1 ;
        end
        else begin
            res_r          <= 'b0 ;
            res_rdy_r      <= 'b0 ;
        end
    end

    assign res_rdy       = res_rdy_r;
    assign res           = res_r;

endmodule

testbench

`timescale 1ns/1ns

module test ;
    parameter    N = 8 ;
    parameter    M = 4 ;
    reg          clk, rstn;
 
   //clock
    always begin
        clk = 0 ; #5 ;
        clk = 1 ; #5 ;
    end

   //reset
    initial begin
        rstn      = 1'b0 ;
        #8 ;      rstn      = 1'b1 ;
    end

    //no pipeline
    reg                  data_rdy_low ;
    reg [N-1:0]          mult1_low ;
    reg [M-1:0]          mult2_low ;
    wire [M+N-1:0]       res_low ;
    wire                 res_rdy_low ;

    //使用任務(wù)周期激勵
    task mult_data_in ;  
        input [M+N-1:0]   mult1_task, mult2_task ;
        begin
            wait(!test.u_mult_low.res_rdy) ;  //not output state
            @(negedge clk ) ;
            data_rdy_low = 1'b1 ;
            mult1_low = mult1_task ;
            mult2_low = mult2_task ;
            @(negedge clk ) ;
            data_rdy_low = 1'b0 ;
            wait(test.u_mult_low.res_rdy) ; //test the output state
        end
    endtask

    //driver
    initial begin
        #55 ;
        mult_data_in(25, 5 ) ;
        mult_data_in(16, 10 ) ;
        mult_data_in(10, 4 ) ;
        mult_data_in(15, 7) ;
        mult_data_in(215, 9) ;
    end

    mult_low  #(.N(N), .M(M))
    u_mult_low
    (
      .clk              (clk),
      .rstn             (rstn),
      .data_rdy         (data_rdy_low),
      .mult1            (mult1_low),
      .mult2            (mult2_low),
      .res_rdy          (res_rdy_low),
      .res              (res_low));

   //simulation finish
   initial begin
      forever begin
         #100;
         if ($time >= 10000)  $finish ;
      end
   end

endmodule // test

仿真結(jié)果如下。

由圖可知,輸入的 2 個數(shù)據(jù)在延遲 4 個周期后,得到了正確的相乘結(jié)果。算上中間送入數(shù)據(jù)的延遲時間,計(jì)算 4 次乘法大約需要 20 個時鐘周期。


流水線乘法器設(shè)計(jì)

下面對乘法執(zhí)行過程的中間狀態(tài)進(jìn)行保存,以便流水工作,設(shè)計(jì)代碼如下。

單次累加計(jì)算過程的代碼文件如下(mult_cell.v ):

module    mult_cell
    #(parameter N=4,
      parameter M=4)
    (
      input                     clk,
      input                     rstn,
      input                     en,
      input [M+N-1:0]           mult1,      //被乘數(shù)
      input [M-1:0]             mult2,      //乘數(shù)
      input [M+N-1:0]           mult1_acci, //上次累加結(jié)果

      output reg [M+N-1:0]      mult1_o,     //被乘數(shù)移位后保存值
      output reg [M-1:0]        mult2_shift, //乘數(shù)移位后保存值
      output reg [N+M-1:0]      mult1_acco,  //當(dāng)前累加結(jié)果
      output reg                rdy );

    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            rdy            <= 'b0 ;
            mult1_o        <= 'b0 ;
            mult1_acco     <= 'b0 ;
            mult2_shift    <= 'b0 ;
        end
        else if (en) begin
            rdy            <= 1'b1 ;
            mult2_shift    <= mult2 >> 1 ;
            mult1_o        <= mult1 << 1 ;
            if (mult2[0]) begin
                //乘數(shù)對應(yīng)位為1則累加
                mult1_acco  <= mult1_acci + mult1 ;  
            end
            else begin
                mult1_acco  <= mult1_acci ; //乘數(shù)對應(yīng)位為1則保持
            end
        end
        else begin
            rdy            <= 'b0 ;
            mult1_o        <= 'b0 ;
            mult1_acco     <= 'b0 ;
            mult2_shift    <= 'b0 ;
        end
    end

endmodule

頂層例化

多次模塊例化完成多次累加,代碼文件如下(mult_man.v ):

module    mult_man
    #(parameter N=4,
      parameter M=4)
    (
      input                     clk,
      input                     rstn,
      input                     data_rdy ,
      input [N-1:0]             mult1,
      input [M-1:0]             mult2,

      output                    res_rdy ,
      output [N+M-1:0]          res );

    wire [N+M-1:0]       mult1_t [M-1:0] ;
    wire [M-1:0]         mult2_t [M-1:0] ;
    wire [N+M-1:0]       mult1_acc_t [M-1:0] ;
    wire [M-1:0]         rdy_t ;

    //第一次例化相當(dāng)于初始化,不能用 generate 語句
    mult_cell      #(.N(N), .M(M))
    u_mult_step0
    (
      .clk              (clk),
      .rstn             (rstn),
      .en               (data_rdy),
      .mult1            ({{(M){1'b0}}, mult1}),
      .mult2            (mult2),
      .mult1_acci       ({(N+M){1'b0}}),
      //output
      .mult1_acco       (mult1_acc_t[0]),
      .mult2_shift      (mult2_t[0]),
      .mult1_o          (mult1_t[0]),
      .rdy              (rdy_t[0]) );

    //多次模塊例化,用 generate 語句
    genvar               i ;
    generate
        for(i=1; i<=M-1; i=i+1) begin: mult_stepx
            mult_cell      #(.N(N), .M(M))
            u_mult_step
            (
              .clk              (clk),
              .rstn             (rstn),
              .en               (rdy_t[i-1]),
              .mult1            (mult1_t[i-1]),
              .mult2            (mult2_t[i-1]),
              //上一次累加結(jié)果作為下一次累加輸入
              .mult1_acci       (mult1_acc_t[i-1]),
              //output
              .mult1_acco       (mult1_acc_t[i]),                                      
              .mult1_o          (mult1_t[i]),  //被乘數(shù)移位狀態(tài)傳遞
              .mult2_shift      (mult2_t[i]),  //乘數(shù)移位狀態(tài)傳遞
              .rdy              (rdy_t[i]) );
        end
    endgenerate

    assign res_rdy       = rdy_t[M-1];
    assign res           = mult1_acc_t[M-1];

endmodule

testbench

將下述仿真描述添加到非流水乘法器設(shè)計(jì)例子的 testbench 中,即可得到流水式乘法運(yùn)算的仿真結(jié)果。

2 路數(shù)據(jù)為不間斷串行輸入,且?guī)в凶孕r?yàn)?zāi)K,可自動判斷乘法運(yùn)算結(jié)果的正確性。

    reg          data_rdy ;
    reg [N-1:0]  mult1 ;
    reg [M-1:0]  mult2 ;
    wire                 res_rdy ;
    wire [N+M-1:0]       res ;

    //driver
    initial begin
        #55 ;
        @(negedge clk ) ;
        data_rdy  = 1'b1 ;
        mult1  = 25;      mult2      = 5;
        #10 ;      mult1  = 16;      mult2      = 10;
        #10 ;      mult1  = 10;      mult2      = 4;
        #10 ;      mult1  = 15;      mult2      = 7;
        mult2      = 7;   repeat(32)    #10   mult1   = mult1 + 1 ;
        mult2      = 1;   repeat(32)    #10   mult1   = mult1 + 1 ;
        mult2      = 15;  repeat(32)    #10   mult1   = mult1 + 1 ;
        mult2      = 3;   repeat(32)    #10   mult1   = mult1 + 1 ;
        mult2      = 11;  repeat(32)    #10   mult1   = mult1 + 1 ;
        mult2      = 4;   repeat(32)    #10   mult1   = mult1 + 1 ;
        mult2      = 9;   repeat(32)    #10   mult1   = mult1 + 1 ;
    end

    //對輸入數(shù)據(jù)進(jìn)行移位,方便后續(xù)校驗(yàn)
    reg  [N-1:0]   mult1_ref [M-1:0];
    reg  [M-1:0]   mult2_ref [M-1:0];
    always @(posedge clk) begin
        mult1_ref[0] <= mult1 ;
        mult2_ref[0] <= mult2 ;
    end

    genvar         i ;
    generate
        for(i=1; i<=M-1; i=i+1) begin
            always @(posedge clk) begin
            mult1_ref[i] <= mult1_ref[i-1];
            mult2_ref[i] <= mult2_ref[i-1];
            end
        end
    endgenerate
   
    //自校驗(yàn)
    reg  error_flag ;
    always @(posedge clk) begin
        # 1 ;
        if (mult1_ref[M-1] * mult2_ref[M-1] != res && res_rdy) begin
            error_flag <= 1'b1 ;
        end
        else begin
            error_flag <= 1'b0 ;
        end
    end

    //module instantiation
    mult_man  #(.N(N), .M(M))
     u_mult
     (
      .clk              (clk),
      .rstn             (rstn),
      .data_rdy         (data_rdy),
      .mult1            (mult1),
      .mult2            (mult2),
      .res_rdy          (res_rdy),
      .res              (res));

仿真結(jié)果

前幾十個時鐘周期的仿真結(jié)果如下。

由圖可知,仿真結(jié)果判斷信號 ?error_flag ?一直為 0,表示乘法設(shè)計(jì)正確。

數(shù)據(jù)在時鐘驅(qū)動下不斷串行輸入,乘法輸出結(jié)果延遲了 4 個時鐘周期后,也源源不斷的在每個時鐘下無延時輸出,完成了流水線式的工作。


相對于一般不采用流水線的乘法器,乘法計(jì)算效率有了很大的改善。

但是,流水線式乘法器使用的寄存器資源也大約是之前不采用流水線式的 4 倍。

所以,一個數(shù)字設(shè)計(jì),是否采用流水線設(shè)計(jì),需要從資源和效率兩方面進(jìn)行權(quán)衡。

點(diǎn)擊這里下載源碼


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號