本博文設計思想采用明德揚至簡設計法。之前都是通過一些完整的案例來分享設計心得,而這篇文章以需要配置多個寄存器的場景講述核心設計技巧。
在設計案例時發現,經常會配置比較復雜的IP核或驅動一些接口進而操作外設。此時,為了讓外設或IP核正常工作,需要對其內部多個寄存器進行適當配置來保證在所需模式下正常工作。我們一般先設計接口模塊或IP核頂層文件,之后通過控制模塊按照先后順序自動給出所需指令,如讀寫等(下面的講述以只有讀寫指令為例)。接口模塊或IP核頂層模塊收到指令后完成相應的操作。
第一個問題:如何實現多個寄存器且每個寄存器多個指令的自動化配置?
我們可以在控制模塊中建立一個“配置表”,把讀寫指令以及相應的地址和待寫入數據保存其中,然后通過計數器進行指令掃描。這里需要兩級計數器,第一級計數一個寄存器的指令數,第二級計數器記錄已經操作過的寄存器個數。配置表以always組合邏輯中case語句塊形式給出,使用寄存器計數值區分不同寄存器。區分出待操作寄存器后根據操作計數器解析出讀寫指令。
第二個問題:當控制模塊給出指令時,接口模塊或IP核一定能有時間響應么?
這是我們設計時需要深思熟慮的問題:如何才能保證給出的指令一定會被下一模塊有效地響應?為了實現這一目的,可以在控制(配置)模塊和時序接口模塊或IP核頂層模塊之間放置一個接口銜接模塊,結構如下:
根據上述需求定義銜接模塊功能:在下游模塊准備好后才讓上游模塊ctrl給出下一命令,否則等待。並完成讀出的有效數據送到上游模塊的任務。很簡單,下游模塊輸出給控制模塊一個信號rdy,當它為高電平時代表當前沒有指令或者上一指令已響應完成。控制模塊中指令計數器的原有計數條件和rdy==1條件邏輯與就完成了上述功能。這里需要特別注意的是:rdy信號必須以組合邏輯形式給出,否則由於rdy信號晚一拍輸出,上游模塊會出現誤認情況。核心代碼如下:
控制模塊中:
1 //讀寫操作計數器 2 always @(posedge clk or negedge rst_n)begin 3 if(rst_n==1'b0)begin 4 rw_cnt <= 0; 5 end 6 else if(add_rw_cnt) begin 7 if(end_rw_cnt) 8 rw_cnt <= 0; 9 else 10 rw_cnt <= rw_cnt + 1; 11 end 12 end 13 14 assign add_rw_cnt = con_flag && rdy; 15 assign end_rw_cnt = add_rw_cnt && rw_cnt==RW_NUM-1; 16 17 //寫使能 wr_flag和rd_flag由配置表給出 18 always @(posedge clk or negedge rst_n)begin 19 if(rst_n==1'b0)begin 20 wr_en <= 1'b0; 21 end 22 else if(add_rw_cnt && rw_cnt==0 && wr_flag)begin 23 wr_en <= 1'b1; 24 end 25 else begin 26 wr_en <= 1'b0; 27 end 28 end 29 30 //讀使能 31 always @(posedge clk or negedge rst_n)begin 32 if(rst_n==1'b0)begin 33 rd_en <= 1'b0; 34 end 35 else if(add_rw_cnt && rw_cnt==1 && rd_flag)begin 36 rd_en <= 1'b1; 37 end 38 else begin 39 rd_en <= 1'b0; 40 end 41 end
銜接模塊中:
1 //空閑輸出 2 always@(*)begin 3 if(rd_en || wr_en || rd_com || wr_com) 4 rdy <= 0; 5 else 6 rdy <= 1; 7 end 8 9 //命令區間標志位 表示正在響應該命令 狀態機實現時序接口模塊情況 10 always@(posedge clk or negedge rst_n)begin 11 if(!rst_n) 12 wr_com <= 0; 13 else if(wr_en) 14 wr_com <= 1; 15 else if(wr_com && stop2idle) 16 wr_com <= 0; 17 end 18 19 always@(posedge clk or negedge rst_n)begin 20 if(!rst_n) 21 rd_com <= 0; 22 else if(rd_en) 23 rd_com <= 1; 24 else if(rd_com && stop2idle) 25 rd_com <= 0; 26 end 27 28 //地址更新 29 always@(posedge clk or negedge rst_n)begin 30 if(!rst_n) 31 addr_tmp <= 0; 32 else if(wr_en || rd_en) 33 addr_tmp <= addr; 34 end
到此,寄存器自動化配置中兩個重點問題已然解決。本文是我在設計攝像頭圖像采集和以太網兩個案例過程中總結所得。本人認為這種設計思想非常具有通用性,並不僅僅局限於這兩個案例,因此單獨提出,以備今后回顧和重用。