209 lines
3.9 KiB
Verilog
209 lines
3.9 KiB
Verilog
/*
|
|
* wb_dma.v
|
|
*
|
|
* vim: ts=4 sw=4
|
|
*
|
|
* Copyright (C) 2020 Sylvain Munaut <tnt@246tNt.com>
|
|
* 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
|