// part of NeoGS project (c) 2008-2013 NedoPC
// dma sequencer
/*
input interface of every DMA end-user
clocks: ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
reqN _______/```````````\______________/`````````````````\______________/``````````````````
ackN ________________/``\_____________________________/``\____________________/``\__/``\__/
doneN _________________________/``\_____________________________/``\______________/``\__/``\
rnwN -------/```````````\--------------\_________________/--------------/````````\_____/```
adrwdN -------< read addr >--------------< write addr&data >--------------< rdaddr X wra X rd
rdN -------------------------<dd>-----------------------------------------------<dd>------
sequencing algorithm is round-robin with fixed initial prioritizing when multiple requests appear simultaneously while
everything was idle
*/
module dma_sequencer(
input wire clk,
input wire rst_n,
// dma requests connection
//
input wire req0,
input wire req1,
input wire req2,
input wire req3,
input wire [21:0] addr0,
input wire [21:0] addr1,
input wire [21:0] addr2,
input wire [21:0] addr3,
input wire rnw0,
input wire rnw1,
input wire rnw2,
input wire rnw3,
input wire [7:0] wd0,
input wire [7:0] wd1,
input wire [7:0] wd2,
input wire [7:0] wd3,
output wire ack0,
output wire ack1,
output wire ack2,
output wire ack3,
output wire end0,
output wire end1,
output wire end2,
output wire end3,
// dma controller connection
output wire dma_req,
output wire [21:0] dma_addr,
output wire dma_rnw,
output wire [7:0] dma_wd,
input wire dma_ack,
input wire dma_end
);
localparam DEVNUM = 4;
wire [DEVNUM-1:0] reqs;
wire [21:0] addrs [0:DEVNUM-1];
wire [DEVNUM-1:0] rnws;
wire [7:0] wds [0:DEVNUM-1];
wire [DEVNUM-1:0] acks;
wire [DEVNUM-1:0] ends;
// aggregate signals for brewity
assign reqs[DEVNUM-1:0] = {req3,req2,req1,req0};
assign addrs[0] = addr0;
assign addrs[1] = addr1;
assign addrs[2] = addr2;
assign addrs[3] = addr3;
assign rnws[DEVNUM-1:0] = {rnw3,rnw2,rnw1,rnw0};
assign wds[0] = wd0;
assign wds[1] = wd1;
assign wds[2] = wd2;
assign wds[3] = wd3;
assign {ack3,ack2,ack1,ack0} = acks[DEVNUM-1:0];
assign {end3,end2,end1,end0} = ends[DEVNUM-1:0];
reg [DEVNUM-1:0] cur_input_mux; // which current input is muxed to the DMA (input=req,ack,addr,wd)
reg [DEVNUM-1:0] cur_output_mux; // which current output is muxed to the DMA (output=end)
wire [DEVNUM-1:0] next_input_mux;
reg busy;
always @(posedge clk, negedge rst_n)
begin
if( !rst_n )
busy = 1'b0;
else // posedge clk
begin
if( !busy )
busy <= |reqs;
else // busy
busy <= dma_req;
end
end
always @(posedge clk, negedge rst_n)
begin
if( !rst_n )
begin
cur_input_mux = {DEVNUM{1'b0}}; // to remove static priority selection after idle -- have here 'd1!
end
else // posedge clk
begin
if( (!busy) || dma_ack ) // idle or dma acknoledges data receive
begin
cur_input_mux <= next_input_mux;
end
end
end
rr_arbiter #( .DEVNUM(DEVNUM) ) rr_arbiter(
.reqs(reqs),
.prev( cur_input_mux),
.next(next_input_mux)
);
// output mux should follow input after dma_ack
//
always @(posedge clk, negedge rst_n)
begin
if( !rst_n )
begin
cur_output_mux = {DEVNUM{1'b0}};
end
else // posedge clk
begin
if( dma_ack )
cur_output_mux <= cur_input_mux;
end
end
// actual muxing of input data
//
wor int_dma_req; // wor is to do easily decoded AND-OR muxes
wor [20:0] int_dma_addr; //
wand int_dma_rnw; // this is WAND to have it 1 in idle
wor [7:0] int_dma_wd; //
//
genvar i;
generate
for(i=0;i<DEVNUM;i=i+1)
begin : mux_dma_inputs
assign int_dma_req = cur_input_mux[i] & reqs[i]; // wired OR happens!
assign int_dma_addr = {21{cur_input_mux[i]}} & addrs[i]; //
assign int_dma_rnw = (~cur_input_mux[i]) | rnws[i]; // wired AND...
assign int_dma_wd = {8{cur_input_mux[i]}} & wds[i]; //
end
endgenerate
//
// output data to dma controller
//
assign dma_req = int_dma_req;
assign dma_addr = int_dma_addr;
assign dma_rnw = int_dma_rnw;
assign dma_wd = int_dma_wd;
// actual de-muxing of output data from dma controller
//
assign acks = cur_input_mux & {DEVNUM{dma_ack}};
assign ends = cur_output_mux & {DEVNUM{dma_end}};
endmodule
// round-robin arbiter
module rr_arbiter(
/*input wire [DEVNUM-1:0]*/ reqs,
/*input wire [DEVNUM-1:0]*/ prev,
/*output wire [DEVNUM-1:0]*/ next
);
parameter DEVNUM=4;
input wire [DEVNUM-1:0] reqs;
input wire [DEVNUM-1:0] prev;
output wire [DEVNUM-1:0] next;
genvar i;
// arbitration if there was previous actives
//
wire [DEVNUM-1:0] loop;
wire [DEVNUM-1:0] next_arb;
generate
for(i=0;i<DEVNUM;i=i+1)
begin : gen_forwarders
if( i==0 )
rr_fwd forwarder( .prev(prev[i]),
.req(reqs[i]),
.next(next_arb[i]),
.loop_in(loop[DEVNUM-1]),
.loop_out(loop[i]) );
else
rr_fwd forwarder( .prev(prev[i]),
.req(reqs[i]),
.next(next_arb[i]),
.loop_in(loop[i-1]),
.loop_out(loop[i]) );
end
endgenerate
// arbitration if there was no actives prior to requests
//
wire [DEVNUM-1:0] next_empty;
generate
for(i=0;i<DEVNUM;i=i+1)
begin : pri_enc
if( i==0 )
begin : pri_zero
assign next_empty[0] = reqs[0];
end
else
begin : pri_nonzero
assign next_empty[i] = reqs[i] & ( ~|reqs[i-1:0] );
end
end
endgenerate
// select between prev-busy and prev-free cases
assign next = ( prev ) ? next_arb : next_empty;
endmodule
// round-robin request forwarder (1 bit)
module rr_fwd(
input wire prev, // who was arbitrated last time (one-hot)
input wire req, // who are requesting
output reg next, // who will be next
input wire loop_in, // for internal arbitration
output reg loop_out //
);
always @*
begin
if( prev )
begin
loop_out = 1'b1;
end
else //!prev
begin
loop_out = req ? 1'b0 : loop_in;
end
end
always @*
begin
next = req ? loop_in : 1'b0;
end
endmodule