stack-machine/dma.v

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