基於AHB總線的master讀寫設計(Verilog)

1、AHB總線學習性能

   1. AHB總線結構學習

         

 

    如圖所示,AHB總線系統利用中央多路選擇機制實現主機與從機的互聯問題。從圖中能夠看出,AHB總線結構主要可分爲三部分:主機、從機、控制部分。控制部分由仲裁器、數據多路選擇、地址和數據多路選擇及地址譯碼器組成。主機首先須要向仲裁器提出使用總線的請求hbusreq信號,仲裁器經過仲裁(多主機使用總線的優先級)受權(hgrant)給某一主機(注意:一個週期內只能有一個主機接入總線),此時,主機就能夠開始進行AHB傳輸了。主機首先發出地址和控制信號。這些信號主要提供地址信息、傳輸方向、帶寬及burst類型(burst傳輸並不是本文重點,故不做討論)。因爲AHB總線統一給每一個從機分配地址,譯碼器能夠根據主機發出的地址選擇哪一個主機與從機進行互聯。spa

二、AHB總線基本傳輸設計

        AHB總線的一次傳輸主要由兩部分組成:地址段(開始傳輸的第一個週期)和數據段(傳輸開始後的週期)。在hclk上升沿來臨時,得到受權的主機驅動地址和控制信號到AHB總線上,在hclk下一週期的上升沿時,slave開始採樣地址和控制信息。獲取地址和控制信息的slave會返回hresp(迴應信號)給master,而在hclk的第三個時鐘上升沿hresp被master採樣,與此同時,master與slave間完成數據的第一次讀寫操做。code

      在進行數據傳輸時,若從機沒有準備好接收下一個數據iketongg將hready信號拉低來插入一個空閒週期,等下一週期hready從新爲高時再接收數據。主機在當前週期發送完部分數據,而在下一週期沒有準備好發後面的數據,可經過加入BUSY狀態來延緩傳輸。blog

2、基於AHB總線的讀寫設計接口

   一、輸入輸出接口input

          在設計某個模塊時,首先須要理清它有哪些輸入輸出,從而對設計進行一個總體瞭解。因爲本文的讀寫模塊設計屬於比較基礎的AHB傳輸,不涉及突發傳輸、鎖定傳輸和從機的分塊傳輸。本設計的輸入有:hclk_i、irst_n、hgrant_i、hrdata_i、hready_i,輸出有:hwdata_o、htrans_o、hwrite_o、haddr_o、hbusreq_o。同步

   一、狀態機設計it

          狀態機的設計比較重要,本設計的主狀態機是:空閒狀態、讀狀態、寫狀態,從狀態機分爲讀狀態機(rd_fsm_r)和寫狀態機(wr_fsm_r),讀狀態機和寫狀態機的狀態轉移圖如圖所示。

 根據AHB總線地址段和數據段的特性,可將其分爲:空閒狀態、請求總線狀態、地址段狀態、讀/寫數據狀態和讀/寫最後一個字節狀態。注意:在狀態機中,什麼時候有效很重要,從圖中能夠看出,各狀態的觸發條件都有hready_i信號(因爲hready_i信號是一直在變化的,可能前一個狀態hready_i信號爲高,但後一個狀態會變低,不能使用軟件思惟去思考。),其次,什麼時候開始讀/寫數據,什麼時候數據讀/寫完成,這都是由計數器計數來決定的

 

  二、設計時序圖

         讀寫過程比較相似,時序圖如圖所示:

          

 從圖中能夠看出,地址與數據並不是在同一週期(AHB總線的特性)。當前週期的地址,存儲的數據在下一週期纔會出現。這種地址和數據交疊出現使總線能進行高性能操做的同時,給從機也提供了足夠的時間來響應傳輸。

 

   三、基本代碼

      (1)狀態機邏輯

module ahb_test(hbusreq_o,haddr_o,htrans_o,hwdata_o,hwrite_o,
                           hclk_i,irst_n,hgrant_i,hready_i,hrdata_i,we_i,re_i);

   input  hclk_i,irst_n,we_i,re_i,hgrant_i,hready_i;
   input  [31:0] hrdata_i;
   output hbusreq_o,hwrite_o;
   output [31:0] hwdata_o;
   output [1:0] htrans_o;
   output [31:0] haddr_o;

   reg [1:0] main_fsm_r;
   reg [2:0] rd_fsm_r;
   reg [2:0] wr_fsm_r;
   reg  [31:0 haddr_r;
   reg [2:0] rd_cnt_r;
   reg [2:0] wr_cnt_r;

    parameter  data_size = 4; //讀寫4個字節數據
    parameter rd_base_addr = 'h1A00;
    parameter  wr_base_addr = 'h1B00;

//the status of main fsm
    parameter    S0 = 'd0;
    parameter    S1 = 'd1;
    parameter    S2 = 'd2;

//the status of read fsm
    parameter  RD_IDLE = 3'b000;
    parameter  RD_BUSREQ = 3'b001;
    parameter  RD_ADDR = 3'b010;
    parameter  RD_RD = 3'b011;
    parameter  RD_LRD = 3'b100;

     wire   fsm_rd_idle = rd_fsm_r == RD_IDLE;
     wire   fsm_rd_busreq = rd_fsm_r == RD_BUSREQ;
     wire   fsm_rd_addr =  rd_fsm_r ==RD_ADDR;
     wire   fsm_rd_rd =  rd_fsm_r == RD_RD;
     wire   fsm_rd_lrd = rd_fsm_r === RD_LRD;
     wire    rd_last_data = rd_cnt_r == data_size - 1'd1;

//the status of write fsm
    parameter  WR_IDLE = 3'b000;
    parameter  WR_BUSREQ = 3'b001;
    parameter  WR_ADDR = 3'b010;
    parameter  WR_WD = 3'b011;
    parameter  WR_LWD = 3'b100;

     wire   fsm_wr_idle = wr_fsm_r == WR_IDLE;
     wire   fsm_wr_busreq = wr_fsm_r == WR_BUSREQ;
     wire   fsm_wr_addr =  wr_fsm_r ==WR_ADDR;
     wire   fsm_wr_wd =  wr_fsm_r == WR_WD;
     wire   fsm_wr_lwd = wr_fsm_r === WR_LWD;
     wire    wr_last_data = wr_cnt_r == data_size - 1'd1;
 
//Main  FSM  
       wire rd_done;
       wire wr_done;
       reg we_r,re_r;
       reg  [1:0] main_fsm_r;
   
       always @(posedge hclk_i)
             if(~irst_n)
                main_fsm_r   <=S0;
              else
                 case(main_fsm_r)
                     S0: if(we_r | re_r)
                         main_fsm_r    <= S1;
                     S1: if(rd_done)
                          main_fsm_r   <=S2;
                     S2: if(wr_done)
                          main_fsm_r   <=S0;
                    default:
                          main_fsm_r   <= S0;
                  endcase

//Sub Read FSM
       always @(posedge hclk_i)
             if(~irst_n)
                rd_fsm_r   <= RD_IDLE;
             else
                 case(rd_fsm_r)
                    RD_IDLE : if((we_r | re_r) | (rd_done))
                              rd_fsm_r    <= RD_BUSREQ;
                    RD_BUSREQ : if(hgrant_i & hready_i)
                              rd_fsm_r    <= RD_ADDR;
                     RD_ADDR : if(hready_i)
                              rd_fsm_r    <= RD_RD;
                     RD_RD : if(rd_cnt_r == data_size-2 & hready_i)
                              rd_fsm_r    <= RD_LRD;
                     RD_LRD : if(hready_i & rd_last_data)
                              rd_fsm_r    <= RD_IDLE;
                       default:
                                rd_fsm_r    <= RD_IDLE;
                    endcase

//Sub Write FSM
       always @(posedge hclk_i)
             if(~irst_n)
                wr_fsm_r   <= WR_IDLE;
             else
                 case(wr_fsm_r)
                    WR_IDLE : if(rd_done)
                              wr_fsm_r    <= WR_BUSREQ;
                    WR_BUSREQ : if(hgrant_i & hready_i)
                              wr_fsm_r    <= WR_ADDR;
                     WR_ADDR : if(hready_i)
                              wr_fsm_r    <= WR_WD;
                     WR_WD : if(wr_cnt_r == data_size-2 & hready_i)
                              wr_fsm_r    <= WR_LWD;
                     WR_LWD : if(hready_i & wr_last_data)
                              wr_fsm_r    <= WR_IDLE;
                       default:
                                wr_fsm_r    <= WR_IDLE;
                    endcase

(2)寄存器邏輯

//we_r
always @(posedge hclk_i)
    if(~irst_n | we_r)
       we_r    <= 1'b0;
    else(we_i)
        we_r    <=1'b1;

//re_r
always @(posedge hclk_i)
    if(~irst_n | re_r)
       re_r    <= 1'b0;
    else(re_i)
        re_r    <=1'b1;

assign rd_done = main_fsm_r == S1 & hready_i & rd_last_data;

assign wr_done = main_fsm_r == S2 & hready_i & wr_last_data;

assign hwrite_o = (main_fsm_r == S2) ? 'd1 : 'd0;

assign  hbusreq_o = (fsm_rd_busreq || fsm_wr_busreq) ? 'd1 : 'd0;

//rd_done_r
always @(posedge hclk_i)
    if(~irst_n || rd_done_r)
        rd_done_r    <= 'd0;
    else if(rd_done)
         rd_done_r    <= 'd1;

//wr_done_r
always @(posedge hclk_i)
    if(~irst_n || wr_done_r)
        wr_done_r    <= 'd0;
    else if(wr_done)
         wr_done_r    <= 'd1;

assign  htrans_o = (fsm_rd_addr || fsm_wr_addr) ? 2'b10 : 2'b11;
wire  addr_add_en = (main_fsm_r == S1 || main_fsm_r == S2) && 
                   (fsm_rd_addr || fsm_rd_rd || fsm_wr_addr || fsm_wr_wd);

//haddr_r
always @(posedge hclk_i)
     if(~irst_n)
          haddr_r    <= 32'd0;
    else if(main_fsm_r == S1 & fsm_rd_busreq & hready_i)
           haddr_r    <= rd_base_addr;
    else if(main_fsm_r == S2 & fsm_wr_busreq & hready_i)
           haddr_r    <= wr_base_addr;
    else if(addr_add_en)
           haddr_r    <= haddr_r + 32'd4;


//rd_cnt_r
always @(posedge hclk_i)
     if (~irst_n)
          rd_cnt_r    <= 3'd0;
     else if (hready_i & fsm_rd_addr)
          rd_cnt_r    <= 3'd0;
     else if (hready_i & fsm_rd_rd)
          rd_cnt_r    <= rd_cnt_r + 1'd1;
      else if (hready_i & rd_last_data)
          rd_cnt_r    <= 3'd0;

//wr_cnt_r
always @(posedge hclk_i)
     if (~irst_n)
          wr_cnt_r    <= 3'd0;
     else if (hready_i & fsm_wr_addr)
          wr_cnt_r    <= 3'd0;
     else if (hready_i & fsm_wr_wd)
          wr_cnt_r    <= wr_cnt_r + 1'd1;
      else if (hready_i & wr_last_data)
          wr_cnt_r    <= 3'd0;

reg  [31:0] rd_data_r [ 0 : data_size-1];
//rd_data_r
always @(posedge hclk_i)
      if(~irst_n)
        {rd_data_r[0],rd_data_r[1],rd_data_r[2],rd_data_r[3]}  <= 32'd0;
      else if(main_fsm_r == S1 & (fsm_rd_rd || fsm_rd_lrd) & hready_i)
             rd_data_r    <= hrdata_i;

assign  hwdata_o = (main_fsm_r == S2 & (fsm_wr_wd || fsm_wr_lwd) & hready_i) ? rd_data_r[wr_cnt_r] : 32'b0;
assign  haddr_o = haddr_r;

endmodule

 至此,本文基於AHB總線的master讀寫設計就完成了。在設計過程當中,重要的是畫出狀態機,並理解每一個狀態的邏輯及狀態與狀態間跳轉的觸發條件。須要理解阻塞賦值和非阻塞賦值。在這裏說一下我對阻塞賦值和非阻塞賦值的理解:

(1)非阻塞賦值(須要使用寄存器將值存儲起來,使用always塊賦值):當前週期時鐘上升沿時存儲值,下一週期時鐘上升沿纔會進行賦值操做。(和下一週期的時序也有關係)。使用非阻塞賦值,各個賦值語句在塊結束後(下一週期)同步賦值。

(2)阻塞賦值(組合邏輯,assign賦值):當前週期時鐘上升沿賦值生效,不存儲值。使用assign能實時給wire型信號賦值。(在當前週期完成操做)。阻塞語句是順序執行的。在當前週期,前一個賦值語句執行完才能執行下一個賦值語句,即前一賦值語句的結果能影響下一賦值語句的值。

模塊輸入輸出通常都是wire型,內部邏輯能夠是wire也能夠是reg,通常先對一些內部邏輯信號進行各類操做,最後再將其賦值給輸出信號。如本文assign haddr_o = haddr_r;中間對haddr_r進行操做,最後將其賦值給haddr_o。

相關文章
相關標籤/搜索