blob: c3af02941578d349076ecb1f2779c307c4aeeef2 [file] [log] [blame]
Sylvain Munaut21b03ba2020-09-14 10:01:45 +02001/*
2 * wb_dma.v
3 *
4 * vim: ts=4 sw=4
5 *
6 * Copyright (C) 2020 Sylvain Munaut <tnt@246tNt.com>
7 * SPDX-License-Identifier: CERN-OHL-P-2.0
8 */
9
10`default_nettype none
11
12module wb_dma #(
13 parameter integer A0W = 9,
14 parameter integer A1W = 9,
15 parameter integer DW = 32
16)(
17 // Master 0
18 output wire [A0W-1:0] m0_addr,
19 input wire [ DW-1:0] m0_rdata,
20 output wire [ DW-1:0] m0_wdata,
21 output wire m0_we,
22 output wire m0_cyc,
23 input wire m0_ack,
24
25 // Master 1
26 output wire [A1W-1:0] m1_addr,
27 input wire [ DW-1:0] m1_rdata,
28 output wire [ DW-1:0] m1_wdata,
29 output wire m1_we,
30 output wire m1_cyc,
31 input wire m1_ack,
32
33 // Slave (control)
34 input wire [ 1:0] ctl_addr,
35 output wire [DW-1:0] ctl_rdata,
36 input wire [DW-1:0] ctl_wdata,
37 input wire ctl_we,
38 input wire ctl_cyc,
39 output wire ctl_ack,
40
41 // Clock / Reset
42 input wire clk,
43 input wire rst
44);
45
46 // Signals
47 // -------
48
49 // Control
50 reg [1:0] state; // [1] = busy [0] = phase 0(read) 1(write)
51 reg [1:0] state_nxt;
52 reg dir; // 0 = M0->M1, 1 = M1->M0
53 reg go;
54
55 wire ack_rd;
56 wire ack_wr;
57
58 // Data register
59 wire data_ce;
60 reg [DW-1:0] data_reg;
61
62 // Address counters
63 wire m0_addr_ce;
64 wire m0_addr_ld;
65 reg [A0W-1:0] m0_addr_i;
66
67 wire m1_addr_ce;
68 wire m1_addr_ld;
69 reg [A1W-1:0] m1_addr_i;
70
71 // Length counter
72 wire len_ce;
73 wire len_ld;
74 reg [12:0] len;
75 wire len_last;
76
77 // Control IF
78 reg ctl_do_write;
79 reg ctl_do_read;
80 reg ctl_ack_i;
81
82
83 // Control
84 // -------
85
86 always @(posedge clk or posedge rst)
87 if (rst)
88 go <= 1'b0;
89 else
90 go <= ctl_do_write & (ctl_addr[1:0] == 2'b00) & ctl_wdata[15];
91
92 always @(posedge clk or posedge rst)
93 if (rst)
94 state <= 2'b00;
95 else
96 state <= state_nxt;
97
98 always @(*)
99 begin
100 state_nxt <= state;
101
102 case (state)
103 2'b00: begin
104 if (go)
105 state_nxt <= 2'b10;
106 end
107
108 2'b10: begin
109 if (ack_rd)
110 state_nxt <= 2'b11;
111 end
112
113 2'b11: begin
114 if (ack_wr)
115 state_nxt <= len_last ? 2'b00 : 2'b10;
116 end
117
118 default:
119 state_nxt <= 2'b00;
120 endcase
121 end
122
123 assign ack_rd = (m0_ack & ~dir) | (m1_ack & dir);
124 assign ack_wr = (m0_ack & dir) | (m1_ack & ~dir);
125
126
127 // WB transaction
128 // --------------
129
130 assign m0_cyc = state[1] & ~(state[0] ^ dir);
131 assign m1_cyc = state[1] & (state[0] ^ dir);
132
133 assign m0_we = dir;
134 assign m1_we = ~dir;
135
136
137 // Data register
138 // -------------
139
140 assign data_ce = ack_rd;
141
142 always @(posedge clk)
143 if (data_ce)
144 data_reg <= dir ? m1_rdata : m0_rdata;
145
146 assign m0_wdata = data_reg;
147 assign m1_wdata = data_reg;
148
149
150 // Address counters
151 // ----------------
152
153 always @(posedge clk)
154 if (m0_addr_ce)
155 m0_addr_i <= m0_addr_ld ? ctl_wdata[A0W-1:0] : (m0_addr_i + 1);
156
157 always @(posedge clk)
158 if (m1_addr_ce)
159 m1_addr_i <= m1_addr_ld ? ctl_wdata[A1W-1:0] : (m1_addr_i + 1);
160
161 assign m0_addr_ce = m0_addr_ld | ack_wr;
162 assign m1_addr_ce = m1_addr_ld | ack_wr;
163
164 assign m0_addr_ld = ctl_do_write & (ctl_addr[1:0] == 2'b10);
165 assign m1_addr_ld = ctl_do_write & (ctl_addr[1:0] == 2'b11);
166
167 assign m0_addr = m0_addr_i;
168 assign m1_addr = m1_addr_i;
169
170
171 // Length counter
172 // --------------
173
174 always @(posedge clk)
175 if (len_ce)
176 len <= len_ld ? { 1'b0, ctl_wdata[11:0] } : (len - 1);
177
178 always @(posedge clk)
179 if (len_ld)
180 dir <= ctl_wdata[14];
181
182 assign len_ce = len_ld | ack_wr;
183 assign len_ld = ctl_do_write & (ctl_addr[1:0] == 2'b00);
184 assign len_last = len[12];
185
186
187 // Control IF
188 // ----------
189
190 always @(posedge clk or posedge rst)
191 if (rst) begin
192 ctl_do_write <= 1'b0;
193 ctl_do_read <= 1'b0;
194 ctl_ack_i <= 1'b0;
195 end else begin
196 ctl_do_write <= ~ctl_ack_i & ctl_cyc & ctl_we;
197 ctl_do_read <= ~ctl_ack_i & ctl_cyc & ~ctl_we & (ctl_addr[1:0] == 2'b00);
198 ctl_ack_i <= ~ctl_ack_i & ctl_cyc;
199 end
200
201 assign ctl_ack = ctl_ack_i;
202
203 assign ctl_rdata = {
204 {(DW-16){1'b0}},
205 (ctl_do_read ? { state[1], dir, 1'b0, len } : 16'h0000)
206 };
207
208endmodule // wb_dma