// 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