Imported Upstream version 3.2.2
[debian/gnuradio] / usrp2 / firmware / lib / bsm12.c
1 /* -*- c++ -*- */
2 /*
3  * Copyright 2009 Free Software Foundation, Inc.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 /*
20  * buffer state machine: 1 input to two outputs
21  *
22  * Typically used to read packets from the ethernet and then after inspecting,
23  * handle the packet in firmware or pass it on to 1 of the 2 buffer destinations.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include "bsm12.h"
31 #include "memory_map.h"
32 #include "buffer_pool.h"
33 #include "bool.h"
34 #include "nonstdio.h"
35 #include <stdlib.h>
36
37 typedef enum {
38   BS_EMPTY,
39   BS_FILLING,
40   BS_FULL,
41   BS_EMPTYING,
42 } buffer_state_t;
43
44 static buffer_state_t buffer_state[NBUFFERS];
45 static uint32_t last_send_ctrl[NBUFFERS];
46 static int8_t  buffer_target[NBUFFERS];     // -1, 0 or 1.
47 static uint8_t buffer_dst[NBUFFERS];        // 0 or 1. Valid only when BF_EMPTYING
48
49 #define ST_IDLE (-1)
50
51 void
52 bsm12_init(bsm12_t *sm, int buf0,
53            const buf_cmd_args_t *recv,
54            const buf_cmd_args_t *send0,
55            const buf_cmd_args_t *send1,
56            bsm12_inspector_t inspect)
57 {
58   if (buf0 & 0x3)       // precondition: buf0 % 4 == 0
59     abort();
60
61   sm->buf0 = buf0;
62   sm->running = false;
63   sm->recv_args = *recv;
64   sm->send_args[0] = *send0;
65   sm->send_args[1] = *send1;
66
67   sm->rx_state = ST_IDLE;
68   sm->tx_state[0] = ST_IDLE;
69   sm->tx_state[1] = ST_IDLE;
70
71   sm->inspect = inspect;
72
73   sm->bps_error = BPS_ERROR(buf0 + 0) | BPS_ERROR(buf0 + 1) | BPS_ERROR(buf0 + 2);
74   sm->bps_done  = BPS_DONE(buf0 + 0)  | BPS_DONE(buf0 + 1)  | BPS_DONE(buf0 + 2);
75   sm->bps_error_or_done = sm->bps_error | sm->bps_done;
76
77   // How much to adjust the last_line register.
78   // It's 1 for everything but the ethernet.
79   sm->last_line_adj = recv->port == PORT_ETH ? 3 : 1;
80
81   buffer_state[sm->buf0 + 0] = BS_EMPTY;
82   buffer_state[sm->buf0 + 1] = BS_EMPTY;
83   buffer_state[sm->buf0 + 2] = BS_EMPTY;
84
85   buffer_target[sm->buf0 + 0] = -1;
86   buffer_target[sm->buf0 + 1] = -1;
87   buffer_target[sm->buf0 + 2] = -1;
88
89   for (int i = 0; i < NBUFFERS; i++)
90     sm->next_buf[i] = buf0;
91
92   sm->next_buf[buf0 + 0] = buf0 + 1;
93   sm->next_buf[buf0 + 1] = buf0 + 2;
94   sm->next_buf[buf0 + 2] = buf0 + 0;
95
96   for (int i = 0; i < 3; i++){
97     sm->precomputed_receive_to_buf_ctrl_word[i] =
98       (BPC_READ
99        | BPC_BUFFER(sm->buf0 + i)
100        | BPC_PORT(sm->recv_args.port)
101        | BPC_STEP(1)
102        | BPC_FIRST_LINE(sm->recv_args.first_line)
103        | BPC_LAST_LINE(sm->recv_args.last_line));
104     
105     for (int j = 0; j < 2; j++){
106       sm->precomputed_send_from_buf_ctrl_word[i][j] =
107         (BPC_WRITE
108          | BPC_BUFFER(sm->buf0 + i)
109          | BPC_PORT(sm->send_args[j].port)
110          | BPC_STEP(1)
111          | BPC_FIRST_LINE(sm->send_args[j].first_line)
112          | BPC_LAST_LINE(0));           // last line filled in at runtime
113     }
114   }
115 }
116
117 static inline void
118 bsm12_receive_to_buf(bsm12_t *sm, int bufno)
119 {
120   buffer_pool_ctrl->ctrl = sm->precomputed_receive_to_buf_ctrl_word[bufno & 0x3];
121 }
122
123 static inline void
124 bsm12_send_from_buf(bsm12_t *sm, int bufno, int dst_idx)
125 {
126   dst_idx &= 0x1;
127
128   uint32_t t = 
129     (sm->precomputed_send_from_buf_ctrl_word[bufno & 0x3][dst_idx]
130      | BPC_LAST_LINE(buffer_pool_status->last_line[bufno] - sm->last_line_adj));
131
132   buffer_pool_ctrl->ctrl = t;
133   last_send_ctrl[bufno] = t;
134   buffer_dst[bufno] = dst_idx;
135 }
136
137 static inline void
138 bsm12_resend_from_buf(bsm12_t *sm, int bufno)
139 {
140   buffer_pool_ctrl->ctrl = last_send_ctrl[bufno];
141 }
142
143 void
144 bsm12_start(bsm12_t *sm)
145 {
146   sm->running = true;
147
148   buffer_state[sm->buf0 + 0] = BS_EMPTY;
149   buffer_state[sm->buf0 + 1] = BS_EMPTY;
150   buffer_state[sm->buf0 + 2] = BS_EMPTY;
151
152   buffer_target[sm->buf0 + 0] = -1;
153   buffer_target[sm->buf0 + 1] = -1;
154   buffer_target[sm->buf0 + 2] = -1;
155
156   bp_clear_buf(sm->buf0 + 0);
157   bp_clear_buf(sm->buf0 + 1);
158   bp_clear_buf(sm->buf0 + 2);
159
160   sm->rx_state = 0;
161   sm->tx_state[0] = ST_IDLE;
162   sm->tx_state[1] = ST_IDLE;
163   bsm12_receive_to_buf(sm, sm->buf0);
164   buffer_state[sm->buf0] = BS_FILLING;
165 }
166
167 void
168 bsm12_stop(bsm12_t *sm)
169 {
170   sm->running = false;
171   bp_clear_buf(sm->buf0 + 0);
172   bp_clear_buf(sm->buf0 + 1);
173   bp_clear_buf(sm->buf0 + 2);
174   buffer_state[sm->buf0 + 0] = BS_EMPTY;
175   buffer_state[sm->buf0 + 1] = BS_EMPTY;
176   buffer_state[sm->buf0 + 2] = BS_EMPTY;
177 }
178
179 static void bsm12_process_helper(bsm12_t *sm, int buf_this);
180 static void bsm12_error_helper(bsm12_t *sm, int buf_this);
181
182 void
183 bsm12_process_status(bsm12_t *sm, uint32_t status)
184 {
185   // anything for us?
186   if ((status & sm->bps_error_or_done) == 0 || !sm->running)
187     return;
188
189   if (status & sm->bps_error){
190     // Most likely an ethernet Rx error.  We just restart the transfer.
191     if (status & (BPS_ERROR(sm->buf0 + 0)))
192       bsm12_error_helper(sm, sm->buf0 + 0);
193
194     if (status & (BPS_ERROR(sm->buf0 + 1)))
195       bsm12_error_helper(sm, sm->buf0 + 1);
196
197     if (status & (BPS_ERROR(sm->buf0 + 2)))
198       bsm12_error_helper(sm, sm->buf0 + 2);
199   }
200
201   if (status & BPS_DONE(sm->buf0 + 0))
202     bsm12_process_helper(sm, sm->buf0 + 0);
203
204   if (status & BPS_DONE(sm->buf0 + 1))
205     bsm12_process_helper(sm, sm->buf0 + 1);
206
207   if (status & BPS_DONE(sm->buf0 + 2))
208     bsm12_process_helper(sm, sm->buf0 + 2);
209 }
210
211 \fstatic void 
212 bsm12_process_helper(bsm12_t *sm, int buf_this)
213 {
214   bp_clear_buf(buf_this);
215
216   if (buffer_state[buf_this] == BS_FILLING){
217
218     buffer_state[buf_this] = BS_FULL;
219     buffer_target[buf_this] = -1;
220
221     //
222     // where does this packet go?
223     //
224     int dst = sm->inspect(sm, buf_this);
225     if (dst == -1){
226       //
227       // f/w handled the packet; refill the buffer
228       //
229       bsm12_receive_to_buf(sm, buf_this);
230       buffer_state[buf_this] = BS_FILLING;
231       buffer_target[buf_this] = -1;
232       sm->rx_state = buf_this & 0x3;
233     }
234     else {      // goes to dst 0 or 1
235       //
236       // If the next buffer is empty, start a receive on it
237       //
238       int t = sm->next_buf[buf_this];
239       if (buffer_state[t] == BS_EMPTY){
240         bsm12_receive_to_buf(sm, t);
241         buffer_state[t] = BS_FILLING;
242         buffer_target[t] = -1;
243         sm->rx_state = t & 0x3;
244       }
245       else
246         sm->rx_state = ST_IDLE;
247
248       //
249       // If the destination is idle, start the xfer, othewise remember it
250       //
251       if (sm->tx_state[dst] == ST_IDLE){
252         bsm12_send_from_buf(sm, buf_this, dst);
253         sm->tx_state[dst] = buf_this & 0x3;
254         buffer_state[buf_this] = BS_EMPTYING;
255         buffer_target[buf_this] = -1;
256       }
257       else {
258         buffer_target[buf_this] = dst;
259       }
260     }
261   }
262
263   else {    // BS_EMPTYING
264
265     buffer_state[buf_this] = BS_EMPTY;
266     buffer_target[buf_this] = -1;
267
268     if (sm->rx_state == ST_IDLE){       // fire off another receive
269       sm->rx_state = buf_this & 0x3;
270       bsm12_receive_to_buf(sm, buf_this);
271       buffer_state[buf_this] = BS_FILLING;
272       buffer_target[buf_this] = -1;
273     }
274
275     int dst = buffer_dst[buf_this];     // the dst we were emptying into
276     // is the next buffer full and for us?
277     int t = sm->next_buf[buf_this];
278     if (buffer_target[t] == dst){               // yes,
279       bsm12_send_from_buf(sm, t, dst);          // send it
280       buffer_state[t] = BS_EMPTYING;
281       buffer_target[t] = -1;
282       sm->tx_state[dst] = t & 0x3;
283     }
284     // how about the one after that?
285     else if (buffer_target[t=sm->next_buf[t]] == dst){  // yes,
286       bsm12_send_from_buf(sm, t, dst);                  // send it
287       buffer_state[t] = BS_EMPTYING;
288       buffer_target[t] = -1;
289       sm->tx_state[dst] = t & 0x3;
290     }
291     else {
292       sm->tx_state[dst] = ST_IDLE;
293     }
294   }
295 }
296
297 static void
298 bsm12_error_helper(bsm12_t *sm, int buf_this)
299 {
300   bp_clear_buf(buf_this);                 // clears ERROR flag
301
302   if (buffer_state[buf_this] == BS_FILLING){
303     bsm12_receive_to_buf(sm, buf_this);   // restart the xfer
304   }
305   else { // buffer was emptying
306     bsm12_resend_from_buf(sm, buf_this);  // restart the xfer
307   }
308 }
309
310
311 void
312 bsm12_handle_tx_underrun(bsm12_t *sm)
313 {
314 }
315
316 void
317 bsm12_handle_rx_overrun(bsm12_t *sm)
318 {
319 }