一、AHB總線學習
1. AHB總線結構

如圖所示,AHB總線系統利用中央多路選擇機制實作主機與從機的互聯問題,從圖中可以看出,AHB總線結構主要可分為三部分:主機、從機、控制部分,控制部分由仲裁器、資料多路選擇、地址和資料多路選擇及地址譯碼器組成,主機首先需要向仲裁器提出使用總線的請求hbusreq信號,仲裁器通過仲裁(多主機使用總線的優先級)授權(hgrant)給某一主機(注意:一個周期內只能有一個主機接入總線),此時,主機就可以開始進行AHB傳輸了,主機首先發出地址和控制信號,這些信號主要提供地址資訊、傳輸方向、帶寬及burst型別(burst傳輸并非本文重點,故不作討論),由于AHB總線統一給每個從機分配地址,譯碼器可以根據主機發出的地址選擇哪個主機與從機進行互聯,
2、AHB總線基本傳輸
AHB總線的一次傳輸主要由兩部分組成:地址段(開始傳輸的第一個周期)和資料段(傳輸開始后的周期),在hclk上升沿來臨時,獲得授權的主機驅動地址和控制信號到AHB總線上,在hclk下一周期的上升沿時,slave開始采樣地址和控制資訊,獲取地址和控制資訊的slave會回傳hresp(回應信號)給master,而在hclk的第三個時鐘上升沿hresp被master采樣,與此同時,master與slave間完成資料的第一次讀寫操作,
在進行資料傳輸時,若從機沒有準備好接收下一個資料iketongg將hready信號拉低來插入一個空閑周期,等下一周期hready重新為高時再接收資料,主機在當前周期發送完部分資料,而在下一周期沒有準備好發后面的資料,可通過加入BUSY狀態來延緩傳輸,
二、基于AHB總線的讀寫設計
1、輸入輸出介面
在設計某個模塊時,首先需要理清它有哪些輸入輸出,從而對設計進行一個整體了解,由于本文的讀寫模塊設計屬于比較基礎的AHB傳輸,不涉及突發傳輸、鎖定傳輸和從機的分塊傳輸,本設計的輸入有:hclk_i、irst_n、hgrant_i、hrdata_i、hready_i,輸出有:hwdata_o、htrans_o、hwrite_o、haddr_o、hbusreq_o,
1、狀態機設計
狀態機的設計比較重要,本設計的主狀態機是:空閑狀態、讀狀態、寫狀態,從狀態機分為讀狀態機(rd_fsm_r)和寫狀態機(wr_fsm_r),讀狀態機和寫狀態機的狀態轉移圖如圖所示,

根據AHB總線地址段和資料段的特性,可將其分為:空閑狀態、請求總線狀態、地址段狀態、讀/寫資料狀態和讀/寫最后一個位元組狀態,注意:在狀態機中,何時有效很重要,從圖中可以看出,各狀態的觸發條件都有hready_i信號(由于hready_i信號是一直在變化的,可能前一個狀態hready_i信號為高,但后一個狀態會變低,不能使用軟體思維去思考,),其次,何時開始讀/寫資料,何時資料讀/寫完成,這都是由計數器計數來決定的,
2、設計時序圖
讀寫程序比較類似,時序圖如圖所示:

從圖中可以看出,地址與資料并非在同一周期(AHB總線的特性),當前周期的地址,存盤的資料在下一周期才會出現,這種地址和資料交疊出現使總線能進行高性能操作的同時,給從機也提供了足夠的時間來回應傳輸,
3、基本代碼
(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 = https://www.cnblogs.com/bing-newyearday/p/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 = https://www.cnblogs.com/bing-newyearday/p/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_ralways @(posedge hclk_i) if(~irst_n | we_r) we_r <= 1'b0; else(we_i) we_r <=1'b1;//re_ralways @(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_ralways @(posedge hclk_i) if(~irst_n || rd_done_r) rd_done_r <= 'd0; else if(rd_done) rd_done_r <= 'd1;//wr_done_ralways @(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_ralways @(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_ralways @(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_ralways @(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_ralways @(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,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/1996.html
標籤:Verilog
