162 lines
4.0 KiB
Verilog
162 lines
4.0 KiB
Verilog
// vim:set sw=3:
|
|
// Source modes: Destination modes:
|
|
// constant data discard
|
|
// constant address constant address
|
|
// decrementing address decrementing address
|
|
// incrementing address incrementing address
|
|
|
|
// Constant source data is for initialization (e.g. zero-fill).
|
|
// Discard destination is for computations (e.g. CRC [future]).
|
|
// All transfers are word-aligned -- no SEL_O signals are generated.
|
|
|
|
// Control interface:
|
|
// 0x00 source address (R/W)
|
|
// 0x04 dest. address (R/W)
|
|
// 0x08 status/control (RO)
|
|
|
|
// IRQ_O is edge-triggered, active-high. 0->1 indicates completion.
|
|
|
|
// synthesis attribute slice_utilization_ratio of WB_DMA is 15;
|
|
module WB_DMA (CLK_I, RST_I, IRQ_O,
|
|
CTL_ADR_I, CTL_DAT_I, CTL_DAT_O, CTL_STB_I, CTL_WE_I, CTL_ACK_O,
|
|
SRC_ADR_O, SRC_DAT_I, SRC_STB_O, SRC_WE_O, SRC_ACK_I,
|
|
DST_ADR_O, DST_DAT_O, DST_STB_O, DST_WE_O, DST_ACK_I);
|
|
|
|
parameter ADDR_WIDTH = 30;
|
|
parameter DATA_WIDTH = 32;
|
|
|
|
input CLK_I, RST_I;
|
|
output IRQ_O;
|
|
|
|
input CTL_STB_I, CTL_WE_I;
|
|
input [1:0] CTL_ADR_I;
|
|
input [31:0] CTL_DAT_I;
|
|
output [31:0] CTL_DAT_O;
|
|
output CTL_ACK_O;
|
|
|
|
output [ADDR_WIDTH-1:0] SRC_ADR_O;
|
|
input [DATA_WIDTH-1:0] SRC_DAT_I;
|
|
output SRC_STB_O, SRC_WE_O;
|
|
input SRC_ACK_I;
|
|
|
|
output [ADDR_WIDTH-1:0] DST_ADR_O;
|
|
output [DATA_WIDTH-1:0] DST_DAT_O;
|
|
output DST_STB_O, DST_WE_O;
|
|
input DST_ACK_I;
|
|
|
|
reg [DATA_WIDTH-1:0] SRC_ADR_REG;
|
|
reg [DATA_WIDTH-1:0] DST_ADR_REG;
|
|
reg [1:0] SRC_TYPE, DST_TYPE;
|
|
reg [16:0] WORD_COUNT = 0;
|
|
|
|
wire CMD_WRITE = CTL_STB_I & CTL_WE_I & (CTL_ADR_I == 2'h2);
|
|
|
|
wire CONSTANT = ~|SRC_TYPE;
|
|
wire DISCARD = ~|DST_TYPE;
|
|
wire CANCEL = CMD_WRITE & CTL_DAT_I[1];
|
|
wire START = CMD_WRITE & CTL_DAT_I[0];
|
|
wire RUNNING = |WORD_COUNT & ~RST_I;
|
|
wire DONE;
|
|
|
|
FIFO #(
|
|
.QUEUE_SIZE(2),
|
|
.DATA_WIDTH(32)
|
|
) TransferFIFO (
|
|
.CLK_I(CLK_I),
|
|
.RST_I(RST_I | CANCEL),
|
|
.DAT_I(CONSTANT ? SRC_ADR_REG : SRC_DAT_I),
|
|
.DAT_O(DST_DAT_O),
|
|
.QUEUE_I(QUEUE),
|
|
.DEQUEUE_I(DEQUEUE),
|
|
.EMPTY_O(FIFO_EMPTY),
|
|
.FULL_O(FIFO_FULL)
|
|
);
|
|
|
|
assign QUEUE = CONSTANT ? (RUNNING & ~FIFO_FULL) : (SRC_STB_O & SRC_ACK_I);
|
|
assign DEQUEUE = DISCARD ? ~FIFO_EMPTY : (DST_STB_O & DST_ACK_I);
|
|
assign DONE = ~RUNNING & FIFO_EMPTY;
|
|
assign IRQ_O = DONE;
|
|
|
|
assign SRC_ADR_O = SRC_ADR_REG[DATA_WIDTH-1:DATA_WIDTH-ADDR_WIDTH];
|
|
assign DST_ADR_O = DST_ADR_REG[DATA_WIDTH-1:DATA_WIDTH-ADDR_WIDTH];
|
|
|
|
assign SRC_STB_O = RUNNING & ~CONSTANT & ~FIFO_FULL;
|
|
assign DST_STB_O = ~DISCARD & ~FIFO_EMPTY & ~RST_I;
|
|
|
|
assign SRC_WE_O = 1'b0;
|
|
assign DST_WE_O = 1'b1;
|
|
|
|
assign CTL_ACK_O = CTL_STB_I;
|
|
|
|
reg [31:0] CTL_DAT_O;
|
|
|
|
wire [31:0] STATUS = {
|
|
WORD_COUNT[15:0],
|
|
4'h0,
|
|
{ 2'b0, SRC_TYPE },
|
|
{ 2'b0, DST_TYPE },
|
|
{ 3'b0, ~DONE }
|
|
};
|
|
|
|
always @*
|
|
begin
|
|
case (CTL_ADR_I)
|
|
2'h0: CTL_DAT_O <= SRC_ADR_REG;
|
|
2'h1: CTL_DAT_O <= DST_ADR_REG;
|
|
2'h2: CTL_DAT_O <= STATUS;
|
|
2'h3: CTL_DAT_O <= 32'dX;
|
|
endcase
|
|
end
|
|
|
|
always @(posedge CLK_I)
|
|
begin
|
|
if (RST_I | CANCEL)
|
|
begin
|
|
WORD_COUNT <= 0;
|
|
end
|
|
else if (DONE)
|
|
begin
|
|
if (CTL_STB_I & CTL_WE_I)
|
|
begin
|
|
case (CTL_ADR_I)
|
|
2'h0: SRC_ADR_REG <= CTL_DAT_I;
|
|
2'h1: DST_ADR_REG <= CTL_DAT_I;
|
|
endcase
|
|
end
|
|
|
|
if (START)
|
|
begin
|
|
// word count of zero means 2^16 == 0x10000 words.
|
|
WORD_COUNT <= { ~|CTL_DAT_I[31:16], CTL_DAT_I[31:16] };
|
|
SRC_TYPE <= CTL_DAT_I[9:8];
|
|
DST_TYPE <= CTL_DAT_I[5:4];
|
|
end
|
|
end
|
|
else
|
|
begin
|
|
if (QUEUE)
|
|
begin
|
|
WORD_COUNT <= WORD_COUNT - 1;
|
|
|
|
case (SRC_TYPE)
|
|
2'b00: SRC_ADR_REG <= SRC_ADR_REG;
|
|
2'b01: SRC_ADR_REG <= SRC_ADR_REG;
|
|
2'b10: SRC_ADR_REG <= SRC_ADR_REG - (DATA_WIDTH/8);
|
|
2'b11: SRC_ADR_REG <= SRC_ADR_REG + (DATA_WIDTH/8);
|
|
endcase
|
|
end
|
|
|
|
if (DEQUEUE)
|
|
begin
|
|
case (DST_TYPE)
|
|
2'b00: DST_ADR_REG <= DST_ADR_REG;
|
|
2'b01: DST_ADR_REG <= DST_ADR_REG;
|
|
2'b10: DST_ADR_REG <= DST_ADR_REG - (DATA_WIDTH/8);
|
|
2'b11: DST_ADR_REG <= DST_ADR_REG + (DATA_WIDTH/8);
|
|
endcase
|
|
end
|
|
end
|
|
end
|
|
|
|
endmodule
|