osmo-e1-hardware/gateware/common/rtl/wb_dma.v

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