MCDT是MCDF的縮減版贱案,MCDT比MCDF(multi-channel data formatter)少了寄存器配置模塊(Registers)和整型打包數(shù)據(jù)模塊(Formater)永淌。
1. Slave_fifo
此模塊是
Slave部分與FIFO部分結(jié)合的模塊,一共分別有3個Slave和3個FIFO贞言,對應3個發(fā)送激勵的chnl模塊砰粹。
module slave_fifo (
input? ? ? ? ? ? ? ? ? ? ? clk_i,? ? ? ? ? ? ? ? ?// Clock input
input? ? ? ? ? ? ? ? ? ? ? rstn_i,? ? ? ? ? ? ? ?// Reset signal
input [31:0]? ? ? ? ? ? ? ? chx_data_i,? ? ? ? ? ?// 來自外部的數(shù)據(jù)輸入孵稽,這里即是來自Chnl發(fā)送的激勵數(shù)據(jù)輸入政模。
input? ? ? ? ? ? ? ? ? ? ? a2sx_ack_i,? ? ? ? ? ? // Read ack? ? ? ? ? ? ? ? ? ---->來自Aribiter的反饋輸入呐粘,Aribiter要響應并從你這里讀FIFO中的數(shù)據(jù)满俗,ack意思是我(Aribiter)是否現(xiàn)在可以行動起來從你FIFO拿數(shù)據(jù)了。
input? ? ? ? ? ? ? ? ? ? ? chx_valid_i,? ? ? ? ? ? // Data is valid From outside ---->來自外部的控制信號作岖,告訴Slave唆垃,我(Chnl)此次發(fā)送的數(shù)據(jù)data做不做數(shù)。
output reg [31:0]? ? ? ? ? slvx_data_o,? ? ? ? ? ? // Data Output? ? ? ? ? ? ? ? ---->向Arbiter輸出的數(shù)據(jù)痘儡,x表示從哪一個slave來都有可能辕万。
output [5:0]? ? ? ? ? ? ? ? slvx_margin_o,? ? ? ? ? // Data margin? ? ? ? ? ? ? ---->告訴外部/寄存器模塊,我現(xiàn)在還剩余多少空位沉删,你還可以繼續(xù)發(fā)送數(shù)據(jù)渐尿。
output reg? ? ? ? ? ? ? ? ? chx_ready_o,? ? ? ? ? ? // Ready to accept data? ? ? ---->告訴外部(Chnl),我準備好了矾瑰,有空位砖茸,你可以發(fā)送數(shù)據(jù)進來。
output reg? ? ? ? ? ? ? ? ? slvx_val_o,? ? ? ? ? ? // Output data is valid? ? ? ---->告訴下游Arbiter殴穴,我發(fā)出的數(shù)據(jù)是否有效凉夯,可以類比chx_valid_i
output reg? ? ? ? ? ? ? ? ? slvx_req_o? ? ? ? ? ? ? // Request to send data? ? ? ---->告訴下游Arbiter货葬,我需要發(fā)送數(shù)據(jù)出去了。
);? ?
//------------------------------Internal variables-------------------//
reg [5:0] wr_pointer_r; ? ? ? // 內(nèi)部變量劲够,表示FIFO的寫操作指針宝惰。
reg [5:0] rd_pointer_r; ? ? ? ?//內(nèi)部變量,表示FIFO的讀操作指針再沧。
reg [31:0] mem [0:31]; ? ? ?// 一個[31:0]寬,32bit深的FIFO尊残。
//-----------------------------Variable assignments------------------//
wire full_s, empty_s, rd_en_s ; ? ? ? // 由我上一篇文章寫的炒瘸,這是代表信號線,可以傳遞指示狀態(tài)寝衫。
wire [5:0] data_cnt_s; ? ? ? ?// 一捆線顷扩,由6根信號線組成,可以指示FIFO當前的余量狀態(tài)慰毅。
assign full_s = ({~wr_pointer_r[5], wr_pointer_r[4:0]}==rd_pointer_r); ? ? // ?滿狀態(tài)的標志隘截,邏輯是寫指針的最高位取反,寫指針最高位達到1了肯定就滿了汹胃,因為我們的FIFO是32位深的婶芭,2^5就能表示32了(`b100_000),在達到最大值32之前着饥,寫指針的最高位都是0犀农。因此可以通過最高位取反與讀指針做比較來判斷當前是否是滿狀態(tài)。
assign empty_s = (wr_pointer_r == rd_pointer_r); ?// 空狀態(tài)宰掉,其實空狀態(tài)和滿狀態(tài)呵哨,讀寫指針都相等,但在上一行已經(jīng)通過最高位可以判斷此時是空還是滿轨奄。
assign data_cnt_s = (6'd32 - (wr_pointer_r - rd_pointer_r)); ? // 簡單的數(shù)學計算孟害,剩余空位 = 寫入數(shù) - 讀出數(shù)。
assign slvx_margin_o = data_cnt_s; ? // 將剩余空位信號挪拟,驅(qū)動到margin信號上挨务。
assign rd_en_s = a2sx_ack_i; ? // 將Arbiter反饋的可讀信號線,驅(qū)動到read_enable信號線上舞丛,這根信號線有用耘子。
always @ (*) //ready signal
begin
????if (!full_s) chx_ready_o = 1'b1; ? ? ? ? ? ??// 如果滿信號為0,說明還沒滿球切,把ready信號線拉高谷誓,說明準備可以接收數(shù)據(jù)了。? ? ?
????else chx_ready_o = 1'b0;? ? ? ? ? ? ? ?// 如果滿信號為1吨凑,拉低ready捍歪。
end
always @ (*) //reset signal
begin
? ????if (!rstn_i) slvx_req_o = 1'b0; ? ? ?// 復位信號户辱,低有效,意思rstn_i拉低糙臼,說明我們要進行重制庐镐,把req請求信號拉低,代表我們不往下游發(fā)送數(shù)據(jù)变逃。
????? else if (!empty_s) slvx_req_o = 1'b1;? ? ? ? ? // ?不復位必逆,且empty信號為0,代表我現(xiàn)在FIFO不為空揽乱,拉高req名眉,表明我需要把我FIFO的數(shù)據(jù)排空到下游Arbiter。
? ????else slvx_req_o = 1'b0; ? // 不復位凰棉,但FIFO為空损拢,發(fā)也沒得發(fā)了,拉低req撒犀。
end
// 寫指針的運作原理
always @ (posedge clk_i or negedge rstn_i) ? ? ? ?// 時鐘上升沿或重置信號下降沿觸發(fā)
begin :?
? if (!rstn_i) begin ? ? ?// ?復位福压,寫指針清零,同時也代表著我FIFO里被清零了或舞。
? ? wr_pointer_r <= 6'b0000;
? end else
? if (chx_valid_i && chx_ready_o) begin ? ? ? ? // ?valid信號拉高荆姆,且ready信號拉高,不僅我Chnl準備好了映凳,我的Slave和FIFO也準備好了胞枕,開始寫入數(shù)據(jù)到FIFO。(具體寫操作在下面魏宽,此處僅對寫指針做操作)
? ? wr_pointer_r <= wr_pointer_r + 6'b0001; ? ? // 每寫一個數(shù)據(jù)腐泻,指針+1
? end
end
//????讀指針加法
always @ (posedge clk_i or negedge rstn_i)
begin : READ_POINTER
? if (!rstn_i) begin
? ? rd_pointer_r <= 6'b0000;
? end else
? if (rd_en_s && (!empty_s)) begin ? ?// ?當下游Arbiter可以從FIFO讀數(shù)據(jù)了,并且我的FIFO不為空的時候队询,開始讀取數(shù)據(jù)派桩。(具體讀操作在下面,此處只對讀指針做+1操作)
? ? rd_pointer_r <= rd_pointer_r + 6'b0001;
? end
end
// 數(shù)據(jù)輸出有效信號的操作(slave-FIFO給Arbiter的valid信號)
always @ (posedge clk_i or negedge rstn_i)
begin
? if (!rstn_i) slvx_val_o <= 1'b0;
? ????????else if (rd_en_s && (!empty_s)) ? ? ?// ?下游的 Arbiter可以讀數(shù)據(jù)蚌斩,并且不為空铆惑,此處其實與上一塊代碼重合,可以優(yōu)化送膳。
? ????????? slvx_val_o <= 1'b1; ? ? ?// ?拉起valid
? else slvx_val_o <= 1'b0; ? ?// ?拉低valid
end
// ?FIFO讀數(shù)據(jù)
always? @ (posedge clk_i )
begin : READ_DATA
? if (rstn_i && rd_en_s && (!empty_s)) begin ??
? ????? slvx_data_o <= mem[rd_pointer_r[4:0]]; ? // ?將FIFO中讀指針對應位置的數(shù)據(jù)拿出员魏,賦給slvx_data_out
? end
end
// FIFO寫數(shù)據(jù)
always @ (posedge clk_i)
begin : MEM_WRITE
? if (rstn_i && chx_valid_i && chx_ready_o) begin
? ? mem[wr_pointer_r[4:0]] <= chx_data_i; ? // ?將此時Chnl傳遞來的數(shù)據(jù),寫入到FIFO中寫指針對應的位置叠聋。
? end
end
2. Arbiter
module arbiter(
input ? ? ? ? ? ? ? ? ? ?clk_i,
input ? ? ? ? ? ? ? ? ? ?rstn_i,
//connect with slave port
input ?[31:0] ? ? ? ? ? ?slv0_data_i,
input ?[31:0] ? ? ? ? ? ?slv1_data_i,
input ?[31:0] ? ? ? ? ? ?slv2_data_i,
input ? ? ? ? ? ? ? ? ? ?slv0_req_i,
input ? ? ? ? ? ? ? ? ? ?slv1_req_i,
input ? ? ? ? ? ? ? ? ? ?slv2_req_i,
input ? ? ? ? ? ? ? ? ? ?slv0_val_i,
input ? ? ? ? ? ? ? ? ? ?slv1_val_i,
input ? ? ? ? ? ? ? ? ? ?slv2_val_i,
output ? ? ? ? ? ? ? ? ? a2s0_ack_o,? ? // 輸出到上游FIFO端
output ? ? ? ? ? ? ? ? ? a2s1_ack_o,? ??
output ? ? ? ? ? ? ? ? ? a2s2_ack_o,
//Output of MCDT
output ? ? ? ? ? ? ? ? ? data_val_o,? ?// 輸出到下游外部
output [1:0] ? ? ? ? ? ? arb_id_o,
output [31:0] ? ? ? ? ? ?arb_data_o ? ? ?
);
reg ? ? ? ? ? ? ? ? ? data_val_r;
reg [1:0] ? ? ? ? ? ? arb_id_r;
reg [31:0] ? ? ? ? ? ?arb_data_r;
reg [2:0] ? ? ? ? ? ? c_state;
reg [2:0] ? ? ? ? ? ? n_state;
//--------------------------------use FSM to implete simple Round Robin Arbiter? ? ? ?輪詢狀態(tài)機? ? ? ? ? ? ? ? ? ? ? ?
parameter ? IDLE = 3'b000,
? ? ? ? ? ? GRANT0 = 3'b001,
? ? ? ? ? ? GRANT1 = 3'b010,
? ? ? ? ? ? GRANT2 = 3'b100;
always @ (posedge clk_i or negedge rstn_i)
begin
? if (!rstn_i) c_state <= IDLE;? // 初始狀態(tài)為IDLE
? else c_state <= n_state;?
end
always @ (*)
begin
? if (!rstn_i) n_state = IDLE;? //default priority slv0 > slv1 > slv2
? else
? ? case (c_state)
? ? ? IDLE : ?if (slv0_req_i) n_state = GRANT0;
? ? ? ? ? ? ? else if (slv1_req_i) n_state = GRANT1;
? ? ? ? ? ? ? else if (slv2_req_i) n_state = GRANT2;
? ? ? ? ? ? ? else n_state = IDLE;
? ? ?GRANT0 : if (slv1_req_i) n_state = GRANT1;
? ? ? ? ? ? ? else if (slv2_req_i) n_state = GRANT2;
? ? ? ? ? ? ? else if (slv0_req_i) n_state = GRANT0;
? ? ? ? ? ? ? else n_state = IDLE;
? ? ?GRANT1 : if (slv2_req_i) n_state = GRANT2;
? ? ? ? ? ? ? else if (slv0_req_i) n_state = GRANT0;
? ? ? ? ? ? ? else if (slv1_req_i) n_state = GRANT1;
? ? ? ? ? ? ? else n_state = IDLE;
? ? GRANT2 : if (slv0_req_i) n_state = GRANT0;
? ? ? ? ? ? ?else if (slv1_req_i) n_state = GRANT1;
? ? ? ? ? ? ?else if (slv2_req_i) n_state = GRANT2;
? ? ? ? ? ? ?else n_state = IDLE;
? ? default : n_state = IDLE;
? ? endcase
end
輪詢機制描述了狀態(tài)轉(zhuǎn)換撕阎,使得狀態(tài)機無論進入到哪一種狀態(tài),都可以進入到下一個狀態(tài)碌补,不至于鎖死虏束。例如:當前為IDLE狀態(tài)時棉饶,3個req信號都同時拉起了,一定是先進入GRANT0狀態(tài)镇匀,進入GRANT0之后照藻,即使req信號是同時拉起的狀態(tài),不會再進入GRANT0狀態(tài)了汗侵,而是會進入下一個GRANT1狀態(tài)幸缕。依次類推。
assign {a2s2_ack_o,a2s1_ack_o,a2s0_ack_o} = ?c_state;? // 狀態(tài)機映射到信號線
always @ (*)
begin
if (!rstn_i) begin
? ? data_val_r = 1'b0;
? ? arb_id_r = 2'b11;
? ? arb_data_r = 32'hffff_ffff;
? end
? else
? case ({slv2_val_i,slv1_val_i,slv0_val_i}) ? ?
? 3'b001 : begin
? ? ? ? data_val_r = slv0_val_i;
? ? ? ? ? ? arb_id_r = 2'b00;
? ? ? ? ? ? arb_data_r = slv0_data_i;
? ? ? ? end
? 3'b010 : begin
? ? ? ? ? ? data_val_r = slv1_val_i;
? ? ? ? ? ? arb_id_r = 2'b01;
? ? ? ? ? ? arb_data_r = slv1_data_i;
? ? ? ? ? ? end
? 3'b100 : begin
? ? ? ? ? ? data_val_r = slv2_val_i;
? ? ? ? ? ? arb_id_r = 2'b10;
? ? ? ? ? ? arb_data_r = slv2_data_i;
? ? ? ? ? ? end
? default : begin
? ? ? ? data_val_r = 1'b0;
? ? ? ? arb_id_r = 2'b11;
? ? ? ? arb_data_r = 32'hffff_ffff;
? ? ? ? end
? endcase
end
assign data_val_o = data_val_r;
assign arb_data_o = arb_data_r;
assign arb_id_o = arb_id_r;
endmodule