? 模塊化設計是FPGA設計中一個很重要的技巧,它能夠使一個大型設計的分工協作、仿真測試更加容易,代碼維護或升級也更加便利。
? 如圖3.28所示,一般整個設計工程的頂層文件里只做例化,不做邏輯處理。頂層模塊下會包含多個子模塊,比如圖中的模塊A、模塊B、模塊C等等,而模塊A、B、C下又可以再為分多個子模塊實現,如A模塊可以包含子模塊A1、A2和A3等。
圖3.28 模塊設計示意圖
? 采用模塊化的設計,就可以將大規模復雜系統按照一定規則劃分成若干模塊,然后對每個模塊分別進行設計輸入、綜合與實現,并將實現結果約束在預先設置好的區域內,最后將所有模塊的實現結果進行整合集成,就能完成整個系統的設計。
? 模塊化設計的實現一般包含以下步驟。
●初始預算,本階段是實現步驟的第一步,對整個模塊化設計起著指導性的作用。在初始預算階段,項目管理者需要為設計的整體進行位置布局,只有布局合理,才能夠在最大程度上體現模塊化設計的優勢;反之,如果因布局不合理而在較后的階段需要再次進行初始預算,則需要對整個實現步驟全面返工。
●子模塊的設計實現,在該階段,每個項目成員并行完成各自子模塊的實現。
●模塊的最終集成,在該階段項目管理者將頂層的實現結果和所有子模塊的實現結果進行整合集成,完成整個設計的實現。
? 模塊劃分的基本原則是:各個子模塊的功能相對獨立,模塊內部聯系盡量緊密,模塊間的連接盡量簡單。對于那些難以滿足模塊劃分準則的具有強內部關聯的復雜設計,并不適合采用模塊化設計方法。
? 以工程note10_prj004為例,我們用模塊復用的方式,在頂層模塊vlg_design.v下定義了4個子模塊uut1_pulse_counter.v、uut2_pulse_counter.v、uut3_pulse_counter.v和uut4_pulse_counter.v,這4個子模塊雖然代碼一樣,都是pulse_counter.v這個模塊的代碼,但是由于引到頂層模塊的接口信號不同,所以它們最終實現了4個完全獨立的模塊,即4個完全相同的硬件電路。
圖3.29 代碼的模塊化層次視圖
? 頂層模塊vlg_design.v中沒有邏輯處理的代碼,只有子模塊的例化和接口的連接。代碼如下。
module vlg_design(
input i_clk,
input i_rst_n,
input[3:0]i_pulse,
input i_en,
output[15:0]o_pulse_cnt1,o_pulse_cnt2,
output[15:0]o_pulse_cnt3,o_pulse_cnt4
);
pulse_counter uut1_pulse_counter(
.i_clk????????????????? (i_clk),
.i_rst_n???????????? (i_rst_n),
.i_pulse???????????? (i_pulse[0]),
.i_en?????????????????? (i_en),
.o_pulse_cnt??? (o_pulse_cnt1)
);
pulse_counter uut2_pulse_counter(
.i_clk????????????????? (i_clk),
.i_rst_n???????????? (i_rst_n),
.i_pulse???????????? (i_pulse[1]),
.i_en?????????????????? (i_en),
.o_pulse_cnt??? (o_pulse_cnt2)
);
pulse_counter uut3_pulse_counter(
.i_clk????????????????? (i_clk),
.i_rst_n???????????? (i_rst_n),
.i_pulse???????????? (i_pulse[2]),
.i_en?????????????????? (i_en),
.o_pulse_cnt??? (o_pulse_cnt3)
);
pulse_counter uut4_pulse_counter(
.i_clk????????????????? (i_clk),
.i_rst_n???????????? (i_rst_n),
.i_pulse???????????? (i_pulse[3]),
.i_en?????????????????? (i_en),
.o_pulse_cnt??? (o_pulse_cnt4)
);
endmodule
? 下面是對pulse_counter.v模塊的例化代碼。
pulse_counter uut1_pulse_counter(
.i_clk????????????????? (i_clk),
.i_rst_n???????????? (i_rst_n),
.i_pulse???????????? (i_pulse[0]),
.i_en?????????????????? (i_en),
.o_pulse_cnt??? (o_pulse_cnt1)
);
? 以上面這段代碼為例,模塊例化大體有下面幾個要點:
● pulse_counter是原始工程源碼本身的模塊名稱,同一個工程源碼,可以多次被例化。
●uut1_pulse_counter的名稱是可以隨意起的,只要不和已有的名稱重名即可,它表示我們對當前例化模塊pulse_counter.v的唯一識別名。在這個工程中,我們看到pulse_counter.v模塊被例化了多次,但它和uut1_pulse_counter對應位置的命名是不一樣的,而且必須是不一樣的,表示工程中有多個完全一樣的功能模塊。這和軟件程序里面的調用不一樣,軟件程序由于運行起來總是串行的,所以多次調用同一個函數時,這個函數可以只占一個函數所需的物理存儲空間即可;但是FPGA是并行處理的,它的模塊例化,哪怕是完全一樣的模塊,往往也是需要多個完全一樣的物理資源與其對應的。
●“. i_clk (i_clk),”是接口的映射,“.(),”是固定格式。點號后面的i_clk是pulse_counter.v模塊內部的接口,括號內的i_clk是vlg_design.v模塊的接口。
? pulse_counter.v模塊是具體的邏輯處理源碼,其代碼如下。
module pulse_counter(
input i_clk,
input i_rst_n,
input i_pulse,
input i_en,
output reg[15:0]o_pulse_cnt
);
reg[1:0] r_pulse;
wire w_rise_edge;
//////////////////////////////////////////
//脈沖邊沿檢測邏輯
always @(posedge i_clk)
if(!i_rst_n)r_pulse <= 2'b00;
else r_pulse<= {r_pulse[0],i_pulse};
assign w_rise_edge = r_pulse[0] & ~r_pulse[1];
//////////////////////////////////////////
//脈沖計數邏輯
always @(posedge i_clk)
if(i_en) begin
if(w_rise_edge)o_pulse_cnt <= o_pulse_cnt+1;
else/*o_pulse_cnt <= o_pulse_cnt*/;
end
elseo_pulse_cnt <= 'b0;
endmodule
審核編輯:劉清
評論