/* * wb_dma.v * * vim: ts=4 sw=4 * * Copyright (C) 2020 Sylvain Munaut * SPDX-License-Identifier: CERN-OHL-P-2.0 */ `default_nettype none module wb_dma #( parameter integer A0W = 9, parameter integer A1W = 9, parameter integer DW = 32 )( // Master 0 output wire [A0W-1:0] m0_addr, input wire [ DW-1:0] m0_rdata, output wire [ DW-1:0] m0_wdata, output wire m0_we, output wire m0_cyc, input wire m0_ack, // Master 1 output wire [A1W-1:0] m1_addr, input wire [ DW-1:0] m1_rdata, output wire [ DW-1:0] m1_wdata, output wire m1_we, output wire m1_cyc, input wire m1_ack, // Slave (control) input wire [ 1:0] ctl_addr, output wire [DW-1:0] ctl_rdata, input wire [DW-1:0] ctl_wdata, input wire ctl_we, input wire ctl_cyc, output wire ctl_ack, // Clock / Reset input wire clk, input wire rst ); // Signals // ------- // Control reg [1:0] state; // [1] = busy [0] = phase 0(read) 1(write) reg [1:0] state_nxt; reg dir; // 0 = M0->M1, 1 = M1->M0 reg go; wire ack_rd; wire ack_wr; // Data register wire data_ce; reg [DW-1:0] data_reg; // Address counters wire m0_addr_ce; wire m0_addr_ld; reg [A0W-1:0] m0_addr_i; wire m1_addr_ce; wire m1_addr_ld; reg [A1W-1:0] m1_addr_i; // Length counter wire len_ce; wire len_ld; reg [12:0] len; wire len_last; // Control IF reg ctl_do_write; reg ctl_do_read; reg ctl_ack_i; // Control // ------- always @(posedge clk or posedge rst) if (rst) go <= 1'b0; else go <= ctl_do_write & (ctl_addr[1:0] == 2'b00) & ctl_wdata[15]; always @(posedge clk or posedge rst) if (rst) state <= 2'b00; else state <= state_nxt; always @(*) begin state_nxt <= state; case (state) 2'b00: begin if (go) state_nxt <= 2'b10; end 2'b10: begin if (ack_rd) state_nxt <= 2'b11; end 2'b11: begin if (ack_wr) state_nxt <= len_last ? 2'b00 : 2'b10; end default: state_nxt <= 2'b00; endcase end assign ack_rd = (m0_ack & ~dir) | (m1_ack & dir); assign ack_wr = (m0_ack & dir) | (m1_ack & ~dir); // WB transaction // -------------- assign m0_cyc = state[1] & ~(state[0] ^ dir); assign m1_cyc = state[1] & (state[0] ^ dir); assign m0_we = dir; assign m1_we = ~dir; // Data register // ------------- assign data_ce = ack_rd; always @(posedge clk) if (data_ce) data_reg <= dir ? m1_rdata : m0_rdata; assign m0_wdata = data_reg; assign m1_wdata = data_reg; // Address counters // ---------------- always @(posedge clk) if (m0_addr_ce) m0_addr_i <= m0_addr_ld ? ctl_wdata[A0W-1:0] : (m0_addr_i + 1); always @(posedge clk) if (m1_addr_ce) m1_addr_i <= m1_addr_ld ? ctl_wdata[A1W-1:0] : (m1_addr_i + 1); assign m0_addr_ce = m0_addr_ld | ack_wr; assign m1_addr_ce = m1_addr_ld | ack_wr; assign m0_addr_ld = ctl_do_write & (ctl_addr[1:0] == 2'b10); assign m1_addr_ld = ctl_do_write & (ctl_addr[1:0] == 2'b11); assign m0_addr = m0_addr_i; assign m1_addr = m1_addr_i; // Length counter // -------------- always @(posedge clk) if (len_ce) len <= len_ld ? { 1'b0, ctl_wdata[11:0] } : (len - 1); always @(posedge clk) if (len_ld) dir <= ctl_wdata[14]; assign len_ce = len_ld | ack_wr; assign len_ld = ctl_do_write & (ctl_addr[1:0] == 2'b00); assign len_last = len[12]; // Control IF // ---------- always @(posedge clk or posedge rst) if (rst) begin ctl_do_write <= 1'b0; ctl_do_read <= 1'b0; ctl_ack_i <= 1'b0; end else begin ctl_do_write <= ~ctl_ack_i & ctl_cyc & ctl_we; ctl_do_read <= ~ctl_ack_i & ctl_cyc & ~ctl_we & (ctl_addr[1:0] == 2'b00); ctl_ack_i <= ~ctl_ack_i & ctl_cyc; end assign ctl_ack = ctl_ack_i; assign ctl_rdata = { {(DW-16){1'b0}}, (ctl_do_read ? { state[1], dir, 1'b0, len } : 16'h0000) }; endmodule // wb_dma