1 | /** @file wl_node.c |
---|
2 | * @brief WARPLab Framework (Node) |
---|
3 | * |
---|
4 | * This contains the code for WARPLab Framework. |
---|
5 | * |
---|
6 | * @copyright Copyright 2013-2015, Mango Communications. All rights reserved. |
---|
7 | * Distributed under the WARP license (http://warpproject.org/license) |
---|
8 | * |
---|
9 | * @author Chris Hunter (chunter [at] mangocomm.com) |
---|
10 | * @author Patrick Murphy (murphpo [at] mangocomm.com) |
---|
11 | * @author Erik Welsh (welsh [at] mangocomm.com) |
---|
12 | */ |
---|
13 | |
---|
14 | |
---|
15 | |
---|
16 | /**********************************************************************************************************************/ |
---|
17 | /** |
---|
18 | * @brief Common Functions |
---|
19 | * |
---|
20 | **********************************************************************************************************************/ |
---|
21 | |
---|
22 | /***************************** Include Files *********************************/ |
---|
23 | |
---|
24 | // Xilinx / Standard library includes |
---|
25 | #include <stdlib.h> |
---|
26 | #include <stdio.h> |
---|
27 | #include <string.h> |
---|
28 | #include <xparameters.h> |
---|
29 | #include <xio.h> |
---|
30 | |
---|
31 | // Xilinx Peripheral includes |
---|
32 | #include <xtmrctr.h> |
---|
33 | |
---|
34 | // WARPLab includes |
---|
35 | #include "wl_common.h" |
---|
36 | #include "wl_node.h" |
---|
37 | #include "wl_baseband.h" |
---|
38 | #include "wl_interface.h" |
---|
39 | #include "wl_transport.h" |
---|
40 | #include "wl_user.h" |
---|
41 | #include "wl_trigger_manager.h" |
---|
42 | |
---|
43 | |
---|
44 | |
---|
45 | /*************************** Constant Definitions ****************************/ |
---|
46 | |
---|
47 | // |
---|
48 | // See wl_common.h for commonly modified control parameters |
---|
49 | // |
---|
50 | |
---|
51 | /*********************** Global Variable Definitions *************************/ |
---|
52 | |
---|
53 | extern int sock_unicast; // UDP socket for unicast traffic to / from the board |
---|
54 | extern struct sockaddr_in addr_unicast; |
---|
55 | |
---|
56 | |
---|
57 | |
---|
58 | /*************************** Variable Definitions ****************************/ |
---|
59 | |
---|
60 | u16 node; // Node ID |
---|
61 | static u8 dram_present; // Is DRAM present and available for use |
---|
62 | static u8 configure_buffers; // Configure the baseband buffers |
---|
63 | |
---|
64 | #if ALLOW_ETHERNET_PAUSE |
---|
65 | u8 ethernet_pause; // State variable to pause the reception of Ethernet packets |
---|
66 | #endif |
---|
67 | |
---|
68 | /*************************** Functions Prototypes ****************************/ |
---|
69 | |
---|
70 | void blink_node( int num_blinks, int blink_time ); |
---|
71 | |
---|
72 | void set_node_error_status( int status ); |
---|
73 | |
---|
74 | |
---|
75 | /******************************** Functions **********************************/ |
---|
76 | |
---|
77 | |
---|
78 | /*****************************************************************************/ |
---|
79 | /** |
---|
80 | * Node Transport Processing |
---|
81 | * |
---|
82 | * This function is how the node processes Ethernet frames from the Transport. This |
---|
83 | * function will be used as the transport callback for Host-to-Node messages. Based |
---|
84 | * on the Command Group field in the Command header, this function will call the |
---|
85 | * appropriate sub-system to continue processing the packet. |
---|
86 | * |
---|
87 | * @param socket_index - Index of the socket on which message was received |
---|
88 | * @param from - Pointer to socket address structure from which message was received |
---|
89 | * @param recv_buffer - Pointer to transport buffer with received message |
---|
90 | * @param send_buffer - Pointer to transport buffer for a node response to the message |
---|
91 | * |
---|
92 | * @return None |
---|
93 | * |
---|
94 | * @note If this packet is a host to node message, then the process_hton_msg_callback |
---|
95 | * is used to further process the packet. This method will strip off the |
---|
96 | * WARP transport header for future packet processing. |
---|
97 | * |
---|
98 | *****************************************************************************/ |
---|
99 | int node_rx_from_transport(int socket_index, struct sockaddr * from, warp_ip_udp_buffer * recv_buffer, warp_ip_udp_buffer * send_buffer) { |
---|
100 | |
---|
101 | u8 cmd_group; |
---|
102 | u32 resp_sent = NO_RESP_SENT; |
---|
103 | u32 resp_length; |
---|
104 | |
---|
105 | wl_cmd_resp_hdr * cmd_hdr; |
---|
106 | wl_cmd_resp command; |
---|
107 | wl_cmd_resp_hdr * resp_hdr; |
---|
108 | wl_cmd_resp response; |
---|
109 | |
---|
110 | // Initialize the Command/Response structures |
---|
111 | cmd_hdr = (wl_cmd_resp_hdr *)(recv_buffer->offset); |
---|
112 | command.header = cmd_hdr; |
---|
113 | command.args = (u32 *)((recv_buffer->offset) + sizeof(wl_cmd_resp_hdr)); |
---|
114 | command.buffer = (void *)(recv_buffer); |
---|
115 | |
---|
116 | resp_hdr = (wl_cmd_resp_hdr *)(send_buffer->offset); |
---|
117 | response.header = resp_hdr; |
---|
118 | response.args = (u32 *)((send_buffer->offset) + sizeof(wl_cmd_resp_hdr)); |
---|
119 | response.buffer = (void *)(send_buffer); |
---|
120 | |
---|
121 | // Endian swap the command header so future processing can understand it |
---|
122 | cmd_hdr->cmd = Xil_Ntohl(cmd_hdr->cmd); |
---|
123 | cmd_hdr->length = Xil_Ntohs(cmd_hdr->length); |
---|
124 | cmd_hdr->num_args = Xil_Ntohs(cmd_hdr->num_args); |
---|
125 | |
---|
126 | // Send command to appropriate processing sub-system |
---|
127 | cmd_group = WL_CMD_TO_GRP(cmd_hdr->cmd); |
---|
128 | |
---|
129 | switch(cmd_group){ |
---|
130 | case GROUP_NODE: |
---|
131 | resp_sent = node_process_cmd(socket_index, from, &command, &response); |
---|
132 | break; |
---|
133 | case GROUP_TRANSPORT: |
---|
134 | resp_sent = transport_process_cmd(socket_index, from, &command, &response); |
---|
135 | break; |
---|
136 | case GROUP_INTERFACE: |
---|
137 | resp_sent = ifc_process_cmd(socket_index, from, &command, &response); |
---|
138 | break; |
---|
139 | case GROUP_BASEBAND: |
---|
140 | resp_sent = baseband_process_cmd(socket_index, from, &command, &response); |
---|
141 | break; |
---|
142 | case GROUP_TRIGGER_MANAGER: |
---|
143 | resp_sent = trigmngr_process_cmd(socket_index, from, &command, &response); |
---|
144 | break; |
---|
145 | case GROUP_USER: |
---|
146 | resp_sent = user_process_cmd(socket_index, from, &command, &response); |
---|
147 | break; |
---|
148 | default: |
---|
149 | wl_printf(WL_PRINT_ERROR, print_type_node, "Unknown command group: %d\n", cmd_group); |
---|
150 | break; |
---|
151 | } |
---|
152 | |
---|
153 | // Adjust the length of the response to include the response data from the sub-system and the |
---|
154 | // response header |
---|
155 | // |
---|
156 | if((resp_sent == NO_RESP_SENT) || (resp_sent == NODE_NOT_READY)) { |
---|
157 | resp_length = (resp_hdr->length + sizeof(wl_cmd_resp_hdr)); |
---|
158 | |
---|
159 | // Keep the length and size of the response in sync since we are adding bytes to the buffer |
---|
160 | send_buffer->length += resp_length; |
---|
161 | send_buffer->size += resp_length; |
---|
162 | } |
---|
163 | |
---|
164 | // Endian swap the response header before returning |
---|
165 | resp_hdr->cmd = Xil_Ntohl(resp_hdr->cmd); |
---|
166 | resp_hdr->length = Xil_Ntohs(resp_hdr->length); |
---|
167 | resp_hdr->num_args = Xil_Ntohs(resp_hdr->num_args); |
---|
168 | |
---|
169 | // Return the status |
---|
170 | return resp_sent; |
---|
171 | } |
---|
172 | |
---|
173 | |
---|
174 | |
---|
175 | /*****************************************************************************/ |
---|
176 | /** |
---|
177 | * Node Send Early Response |
---|
178 | * |
---|
179 | * Allows a node to send a response back to the host before the command has |
---|
180 | * finished being processed. This is to minimize the latency between commands |
---|
181 | * since the node is able to finish processing the command during the time |
---|
182 | * it takes to communicate to the host and receive another command. |
---|
183 | * |
---|
184 | * @param socket_index - Index of the socket on which message was received |
---|
185 | * @param to - Pointer to socket address structure to which message will be sent |
---|
186 | * @param resp_hdr - Pointer to WARPLab Command / Response header for outgoing message |
---|
187 | * @param buffers - Pointer to array of IP/UDP buffers that contain the outgoing message |
---|
188 | * @param num_buffers - Number of IP/UDP buffers in the array |
---|
189 | * |
---|
190 | * @return None |
---|
191 | * |
---|
192 | * @note This function can only send one buffer at a time and will modify both the |
---|
193 | * response header and buffer length to create an appropriate outgoing message. |
---|
194 | * |
---|
195 | *****************************************************************************/ |
---|
196 | void node_send_early_resp(int socket_index, void * to, wl_cmd_resp_hdr * resp_hdr, void * buffer) { |
---|
197 | // |
---|
198 | // This function is used to send a response back to the host outside the normal command processing |
---|
199 | // (ie the response does not complete the steps in node_rx_from_transport() after distribution |
---|
200 | // to the different group processing commands), this method must perform the necessary manipulation |
---|
201 | // of the response header and the buffer size so that the message is ready to be sent and then |
---|
202 | // restore the contents so that everything is ready to be used if additional responses are required. |
---|
203 | // |
---|
204 | |
---|
205 | // wl_printf(WL_PRINT_DEBUG, print_type_node, "Send early response: cmd = 0x%08x length = %d\n", resp_hdr->cmd, resp_hdr->length); |
---|
206 | |
---|
207 | u32 tmp_cmd; |
---|
208 | u16 tmp_length; |
---|
209 | u16 tmp_num_args; |
---|
210 | u32 tmp_buffer_length; |
---|
211 | u32 tmp_buffer_size; |
---|
212 | |
---|
213 | warp_ip_udp_buffer * buffer_ptr; |
---|
214 | u32 resp_length; |
---|
215 | |
---|
216 | // Cast the buffer pointer so it is easier to use |
---|
217 | buffer_ptr = (warp_ip_udp_buffer *) buffer; |
---|
218 | |
---|
219 | // Get the current values in the buffer so we can restore them after transmission |
---|
220 | tmp_cmd = resp_hdr->cmd; |
---|
221 | tmp_length = resp_hdr->length; |
---|
222 | tmp_num_args = resp_hdr->num_args; |
---|
223 | tmp_buffer_length = buffer_ptr->length; |
---|
224 | tmp_buffer_size = buffer_ptr->size; |
---|
225 | |
---|
226 | // Adjust the length of the buffer |
---|
227 | resp_length = resp_hdr->length + sizeof(wl_cmd_resp_hdr); |
---|
228 | buffer_ptr->length += resp_length; |
---|
229 | buffer_ptr->size += resp_length; |
---|
230 | |
---|
231 | // Endian swap the response header before before transport sends it |
---|
232 | resp_hdr->cmd = Xil_Ntohl(tmp_cmd); |
---|
233 | resp_hdr->length = Xil_Ntohs(tmp_length); |
---|
234 | resp_hdr->num_args = Xil_Ntohs(tmp_num_args); |
---|
235 | |
---|
236 | // Send the packet |
---|
237 | transport_send(socket_index, (struct sockaddr *)to, (warp_ip_udp_buffer **)&buffer, 0x1); |
---|
238 | |
---|
239 | // Restore the values in the buffer |
---|
240 | resp_hdr->cmd = tmp_cmd; |
---|
241 | resp_hdr->length = tmp_length; |
---|
242 | resp_hdr->num_args = tmp_num_args; |
---|
243 | buffer_ptr->length = tmp_buffer_length; |
---|
244 | buffer_ptr->size = tmp_buffer_size; |
---|
245 | } |
---|
246 | |
---|
247 | |
---|
248 | |
---|
249 | /*****************************************************************************/ |
---|
250 | /** |
---|
251 | * Global initialization function |
---|
252 | * |
---|
253 | * Global_initialize is the subset of initialization commands that are safe |
---|
254 | * to execute multiple times when a user simply wants to reset stats on the board |
---|
255 | * |
---|
256 | * @param None. |
---|
257 | * |
---|
258 | * @return int - Status of the command: |
---|
259 | * XST_SUCCESS - Command completed successfully |
---|
260 | * XST_FAILURE - There was an error in the command |
---|
261 | * |
---|
262 | *****************************************************************************/ |
---|
263 | int global_initialize(){ |
---|
264 | int status = XST_SUCCESS; |
---|
265 | |
---|
266 | status = ifc_init(); |
---|
267 | if(status != XST_SUCCESS) { |
---|
268 | wl_printf(WL_PRINT_ERROR, print_type_node, "Interface initialization error! Exiting\n"); |
---|
269 | return XST_FAILURE; |
---|
270 | } |
---|
271 | |
---|
272 | status = baseband_init(dram_present, configure_buffers); |
---|
273 | if(status != XST_SUCCESS) { |
---|
274 | wl_printf(WL_PRINT_ERROR, print_type_node, "Baseband initialization error! Exiting\n"); |
---|
275 | return XST_FAILURE; |
---|
276 | } else { |
---|
277 | configure_buffers = 0; // Only need to configure the buffers once |
---|
278 | } |
---|
279 | |
---|
280 | status = user_init(); |
---|
281 | if(status != XST_SUCCESS) { |
---|
282 | wl_printf(WL_PRINT_ERROR, print_type_node, "User initialization error! Exiting\n"); |
---|
283 | return XST_FAILURE; |
---|
284 | } |
---|
285 | |
---|
286 | status = trigmngr_init(); |
---|
287 | if(status != XST_SUCCESS) { |
---|
288 | wl_printf(WL_PRINT_ERROR, print_type_node, "Trigger Manager initialization error! Exiting\n"); |
---|
289 | return XST_FAILURE; |
---|
290 | } |
---|
291 | return status; |
---|
292 | } |
---|
293 | |
---|
294 | |
---|
295 | |
---|
296 | |
---|
297 | /**********************************************************************************************************************/ |
---|
298 | /** |
---|
299 | * @brief WARP v3 Specific Functions |
---|
300 | * |
---|
301 | **********************************************************************************************************************/ |
---|
302 | |
---|
303 | #ifdef WARP_HW_VER_v3 |
---|
304 | |
---|
305 | /***************************** Include Files *********************************/ |
---|
306 | |
---|
307 | #include <w3_userio.h> |
---|
308 | #include <w3_clock_controller.h> |
---|
309 | #include <w3_iic_eeprom.h> |
---|
310 | #include <xil_cache.h> |
---|
311 | |
---|
312 | #ifdef XPAR_XSYSMON_NUM_INSTANCES |
---|
313 | #include <xsysmon_hw.h> |
---|
314 | #endif |
---|
315 | |
---|
316 | |
---|
317 | /*************************** Constant Definitions ****************************/ |
---|
318 | |
---|
319 | /*********************** Global Variable Definitions *************************/ |
---|
320 | |
---|
321 | /*************************** Variable Definitions ****************************/ |
---|
322 | |
---|
323 | // Hardware LED state |
---|
324 | u8 use_leds; |
---|
325 | u8 red_led_state; |
---|
326 | u8 green_led_state; |
---|
327 | |
---|
328 | |
---|
329 | /*************************** Functions Prototypes ****************************/ |
---|
330 | |
---|
331 | /******************************** Functions **********************************/ |
---|
332 | |
---|
333 | /*****************************************************************************/ |
---|
334 | /** |
---|
335 | * Process Node Commands |
---|
336 | * |
---|
337 | * This function is part of the Ethernet processing system and will process the |
---|
338 | * various node related commands. |
---|
339 | * |
---|
340 | * @param socket_index - Index of the socket on which to send message |
---|
341 | * @param from - Pointer to socket address structure (struct sockaddr *) where command is from |
---|
342 | * @param command - Pointer to WARPLab Command |
---|
343 | * @param response - Pointer to WARPLab Response |
---|
344 | * |
---|
345 | * @return int - Status of the command: |
---|
346 | * NO_RESP_SENT - No response has been sent |
---|
347 | * RESP_SENT - A response has been sent |
---|
348 | * |
---|
349 | * @note See on-line documentation for more information about the Ethernet |
---|
350 | * packet structure for WARPLab: www.warpproject.org |
---|
351 | * |
---|
352 | *****************************************************************************/ |
---|
353 | int node_process_cmd(int socket_index, void * from, wl_cmd_resp * command, wl_cmd_resp * response) { |
---|
354 | |
---|
355 | // |
---|
356 | // IMPORTANT ENDIAN NOTES: |
---|
357 | // - command |
---|
358 | // - header - Already endian swapped by the framework (safe to access directly) |
---|
359 | // - args - Must be endian swapped as necessary by code (framework does not know the contents of the command) |
---|
360 | // - response |
---|
361 | // - header - Will be endian swapped by the framework (safe to write directly) |
---|
362 | // - args - Must be endian swapped as necessary by code (framework does not know the contents of the response) |
---|
363 | // |
---|
364 | |
---|
365 | // Standard variables |
---|
366 | u32 resp_sent = NO_RESP_SENT; |
---|
367 | |
---|
368 | wl_cmd_resp_hdr * cmd_hdr = command->header; |
---|
369 | u32 * cmd_args_32 = command->args; |
---|
370 | u32 cmd_id = WL_CMD_TO_CMDID(cmd_hdr->cmd); |
---|
371 | |
---|
372 | wl_cmd_resp_hdr * resp_hdr = response->header; |
---|
373 | u32 * resp_args_32 = response->args; |
---|
374 | u32 resp_index = 0; |
---|
375 | |
---|
376 | // Specific command variables |
---|
377 | u32 eth_dev_num; |
---|
378 | int status; |
---|
379 | int numblinks; |
---|
380 | u8 node_ip_addr[IP_ADDR_LEN]; |
---|
381 | u8 node_hw_addr[ETH_MAC_ADDR_LEN]; |
---|
382 | |
---|
383 | u32 msg_cmd; |
---|
384 | u32 default_response; |
---|
385 | |
---|
386 | u32 mem_addr; |
---|
387 | u32 mem_length; |
---|
388 | u32 mem_index; |
---|
389 | |
---|
390 | // Set up the response header |
---|
391 | resp_hdr->cmd = cmd_hdr->cmd; |
---|
392 | resp_hdr->length = 0; |
---|
393 | resp_hdr->num_args = 0; |
---|
394 | |
---|
395 | // Get the Ethernet device number of the socket |
---|
396 | eth_dev_num = socket_get_eth_dev_num(socket_index); |
---|
397 | |
---|
398 | // Populate the IP / MAC addresses of the Ethernet device |
---|
399 | eth_get_ip_addr(eth_dev_num, (u8 *)&node_ip_addr); |
---|
400 | eth_get_hw_addr(eth_dev_num, (u8 *)&node_hw_addr); |
---|
401 | |
---|
402 | // Process the command |
---|
403 | switch(cmd_id){ |
---|
404 | |
---|
405 | //--------------------------------------------------------------------- |
---|
406 | case CMDID_NODE_INITIALIZE: |
---|
407 | |
---|
408 | // Set the decimal point of the rigth hex display |
---|
409 | userio_write_hexdisp_right(USERIO_BASEADDR, (userio_read_hexdisp_right( USERIO_BASEADDR ) | W3_USERIO_HEXDISP_DP ) ); |
---|
410 | |
---|
411 | // Initialize all the sub-systems of the node. |
---|
412 | // NOTE: The error condition is the same as during main() |
---|
413 | // |
---|
414 | status = global_initialize(); |
---|
415 | |
---|
416 | if(status != XST_SUCCESS) { |
---|
417 | wl_printf(WL_PRINT_ERROR, print_type_node, "Error in global_initialize()! Exiting...\n"); |
---|
418 | set_node_error_status(0x2); // Set user IO |
---|
419 | blink_node(0, 250000); // Infinite blink |
---|
420 | } |
---|
421 | break; |
---|
422 | |
---|
423 | |
---|
424 | //--------------------------------------------------------------------- |
---|
425 | case CMDID_NODE_INFO: |
---|
426 | |
---|
427 | // Send all node information |
---|
428 | // NOTE: This must match the expectations of the host |
---|
429 | |
---|
430 | resp_args_32[resp_index++] = Xil_Htonl(w3_eeprom_readSerialNum(EEPROM_BASEADDR)); |
---|
431 | resp_args_32[resp_index++] = Xil_Htonl(userio_read_fpga_dna_msb(USERIO_BASEADDR)); |
---|
432 | resp_args_32[resp_index++] = Xil_Htonl(userio_read_fpga_dna_lsb(USERIO_BASEADDR)); |
---|
433 | resp_args_32[resp_index++] = Xil_Htonl( (node_hw_addr[0] << 8) | node_hw_addr[1] ); |
---|
434 | resp_args_32[resp_index++] = Xil_Htonl( (node_hw_addr[2] << 24) | (node_hw_addr[3] << 16) | (node_hw_addr[4] << 8) | node_hw_addr[5] ); |
---|
435 | resp_args_32[resp_index++] = Xil_Htonl((3 << 24)|(WARPLAB_VER_MAJOR << 16)|(WARPLAB_VER_MINOR << 8)|(WARPLAB_VER_REV)); |
---|
436 | resp_args_32[resp_index++] = Xil_Htonl((wl_bb_get_supported_tx_length() + 1)); |
---|
437 | resp_args_32[resp_index++] = Xil_Htonl((wl_bb_get_supported_rx_length() + 1)); |
---|
438 | resp_args_32[resp_index++] = Xil_Htonl((wl_bb_get_tx_length() + 1)); |
---|
439 | resp_args_32[resp_index++] = Xil_Htonl((wl_bb_get_rx_length() + 1)); |
---|
440 | resp_args_32[resp_index++] = Xil_Htonl(trigger_proc_get_core_info()); |
---|
441 | resp_args_32[resp_index++] = Xil_Htonl(1); // num_interfaceGroups |
---|
442 | |
---|
443 | // Set the number of interfaces |
---|
444 | #if WARPLAB_CONFIG_4RF |
---|
445 | resp_args_32[resp_index++] = Xil_Htonl(4); |
---|
446 | #else |
---|
447 | resp_args_32[resp_index++] = Xil_Htonl(2); |
---|
448 | #endif |
---|
449 | |
---|
450 | resp_hdr->length += (resp_index * sizeof(resp_args_32)); |
---|
451 | resp_hdr->num_args = resp_index; |
---|
452 | break; |
---|
453 | |
---|
454 | |
---|
455 | //--------------------------------------------------------------------- |
---|
456 | case CMDID_NODE_IDENTIFY: |
---|
457 | |
---|
458 | // Send the response early so that M-Code does not hang waiting for the node to stop blinking |
---|
459 | // |
---|
460 | // NOTE: The host must wait the appropriate amount of time (see below) to send another |
---|
461 | // command. Otherwise, the node will possibly cause a transport timeout given that it |
---|
462 | // is still busy with this command and will ignore subsequent commands until it is done |
---|
463 | // with this command. |
---|
464 | // |
---|
465 | node_send_early_resp(socket_index, from, response->header, response->buffer); |
---|
466 | |
---|
467 | // Set LED initial state: Green = "ALL OFF"; Red = "ALL ON" |
---|
468 | userio_write_leds_green(USERIO_BASEADDR, 0x0); |
---|
469 | userio_write_leds_red(USERIO_BASEADDR, 0xF); |
---|
470 | |
---|
471 | // Currently, this code will toggle the Red and Green LEDs for "numblinks" times and pause for 0.1 |
---|
472 | // seconds between each loop. We have chosen the magic number of 10 "blinks", which results in this |
---|
473 | // command taking roughly 1 second. The reason for this choice is: 1) a second is long enough to |
---|
474 | // easily see which node is blinking but not overly long; 2) currently the transport timeout is 1 |
---|
475 | // second. By making this command roughly equivalent to the transport timeout, we limit the |
---|
476 | // probability that the host will error out if it doesn't wait after the 'identify' command to send |
---|
477 | // the next command due to the fact that the transport retransmits commands once after a timeout. |
---|
478 | // |
---|
479 | for (numblinks = 0; numblinks < 10; numblinks++) { |
---|
480 | userio_toggle_leds_red(USERIO_BASEADDR, 0xF); |
---|
481 | userio_toggle_leds_green(USERIO_BASEADDR, 0xF); |
---|
482 | usleep(100000); |
---|
483 | } |
---|
484 | |
---|
485 | // Set LEDs back to "ALL OFF" |
---|
486 | userio_write_leds_red(USERIO_BASEADDR, 0x0); |
---|
487 | userio_write_leds_green(USERIO_BASEADDR, 0x0); |
---|
488 | |
---|
489 | // Tell the transport that the command has already sent a response |
---|
490 | resp_sent = RESP_SENT; |
---|
491 | break; |
---|
492 | |
---|
493 | |
---|
494 | //--------------------------------------------------------------------- |
---|
495 | case CMDID_NODE_TEMPERATURE: |
---|
496 | |
---|
497 | #ifdef XPAR_XSYSMON_NUM_INSTANCES |
---|
498 | resp_args_32[resp_index++] = Xil_Htonl(XSysMon_ReadReg(SYSMON_BASEADDR, XSM_TEMP_OFFSET)); |
---|
499 | resp_args_32[resp_index++] = Xil_Htonl(XSysMon_ReadReg(SYSMON_BASEADDR, XSM_MIN_TEMP_OFFSET)); |
---|
500 | resp_args_32[resp_index++] = Xil_Htonl(XSysMon_ReadReg(SYSMON_BASEADDR, XSM_MAX_TEMP_OFFSET)); |
---|
501 | #else |
---|
502 | resp_args_32[resp_index++] = 0; |
---|
503 | resp_args_32[resp_index++] = 0; |
---|
504 | resp_args_32[resp_index++] = 0; |
---|
505 | #endif |
---|
506 | |
---|
507 | resp_hdr->length += (resp_index * sizeof(resp_args_32)); |
---|
508 | resp_hdr->num_args = resp_index; |
---|
509 | break; |
---|
510 | |
---|
511 | |
---|
512 | //--------------------------------------------------------------------- |
---|
513 | case CMDID_NODE_CONFIG_SETUP: |
---|
514 | // NODE_CONFIG_SETUP Packet Format: |
---|
515 | // - Note: All u32 parameters in cmd_args_32 are byte swapped so use Xil_Ntohl() |
---|
516 | // |
---|
517 | // - cmd_args_32[0] - Serial Number |
---|
518 | // - cmd_args_32[1] - Node ID |
---|
519 | // - cmd_args_32[2] - IP Address |
---|
520 | // |
---|
521 | // NOTE: This command will only execute on the node if it is in the "Network Reset" state |
---|
522 | // (ie the node is set to 0xFFFF). |
---|
523 | // |
---|
524 | if (node == 0xFFFF) { |
---|
525 | |
---|
526 | // Only update the parameters if the serial numbers match |
---|
527 | if (w3_eeprom_readSerialNum(EEPROM_BASEADDR) == Xil_Ntohl(cmd_args_32[0])) { |
---|
528 | |
---|
529 | node = Xil_Ntohl(cmd_args_32[1]) & 0xFFFF; |
---|
530 | |
---|
531 | wl_printf(WL_PRINT_NONE, NULL, " New Node ID : %d \n", node); |
---|
532 | |
---|
533 | // Set the hex display to the new Node ID |
---|
534 | userio_write_control(USERIO_BASEADDR, (userio_read_control(USERIO_BASEADDR) | (W3_USERIO_HEXDISP_L_MAPMODE | W3_USERIO_HEXDISP_R_MAPMODE))); |
---|
535 | userio_write_hexdisp_left(USERIO_BASEADDR, (node / 10)); |
---|
536 | userio_write_hexdisp_right(USERIO_BASEADDR, (node % 10)); |
---|
537 | |
---|
538 | // Get the new IP address |
---|
539 | node_ip_addr[0] = (Xil_Ntohl(cmd_args_32[2]) >> 24) & 0xFF; |
---|
540 | node_ip_addr[1] = (Xil_Ntohl(cmd_args_32[2]) >> 16) & 0xFF; |
---|
541 | node_ip_addr[2] = (Xil_Ntohl(cmd_args_32[2]) >> 8) & 0xFF; |
---|
542 | node_ip_addr[3] = (Xil_Ntohl(cmd_args_32[2]) ) & 0xFF; |
---|
543 | |
---|
544 | wl_printf(WL_PRINT_NONE, NULL, " New IP Address: %d.%d.%d.%d \n", node_ip_addr[0], node_ip_addr[1],node_ip_addr[2],node_ip_addr[3]); |
---|
545 | |
---|
546 | // Configure the transport with the node ID and IP address |
---|
547 | eth_set_ip_addr(eth_dev_num, node_ip_addr); |
---|
548 | |
---|
549 | status = transport_config_sockets(eth_dev_num, (NODE_UDP_UNICAST_PORT_BASE + node), NODE_UDP_MCAST_BASE); |
---|
550 | |
---|
551 | if(status != XST_SUCCESS) { |
---|
552 | wl_printf(WL_PRINT_ERROR, print_type_node, "Error binding transport...\n"); |
---|
553 | } |
---|
554 | |
---|
555 | } else { |
---|
556 | wl_printf(WL_PRINT_INFO, print_type_node, "NODE_IP_SETUP Packet with Serial Number %d ignored. My serial number is %d \n", Xil_Ntohl(cmd_args_32[0]), w3_eeprom_readSerialNum(EEPROM_BASEADDR)); |
---|
557 | } |
---|
558 | } |
---|
559 | break; |
---|
560 | |
---|
561 | |
---|
562 | //--------------------------------------------------------------------- |
---|
563 | case CMDID_NODE_CONFIG_RESET: |
---|
564 | // NODE_CONFIG_RESET Packet Format: |
---|
565 | // - Note: All u32 parameters in cmd_args_32 are byte swapped so use Xil_Ntohl() |
---|
566 | // |
---|
567 | // - cmd_args_32[0] - Serial Number |
---|
568 | // |
---|
569 | |
---|
570 | // Send the response early so that M-Code does not hang when IP address changes |
---|
571 | node_send_early_resp(socket_index, from, response->header, response->buffer); |
---|
572 | |
---|
573 | // Only update the parameters if the serial numbers match |
---|
574 | if (w3_eeprom_readSerialNum(EEPROM_BASEADDR) == Xil_Ntohl(cmd_args_32[0])) { |
---|
575 | |
---|
576 | // Reset node to 0xFFFF |
---|
577 | node = 0xFFFF; |
---|
578 | |
---|
579 | wl_printf(WL_PRINT_NONE, NULL, "\n!!! Reseting Network Configuration !!! \n\n"); |
---|
580 | |
---|
581 | // Reset transport; This will update the IP Address back to default and rebind the sockets |
---|
582 | transport_get_hw_info(eth_dev_num, node_ip_addr, node_hw_addr); |
---|
583 | |
---|
584 | eth_set_ip_addr(eth_dev_num, node_ip_addr); |
---|
585 | |
---|
586 | status = transport_config_sockets(eth_dev_num, NODE_UDP_UNICAST_PORT_BASE, NODE_UDP_MCAST_BASE); |
---|
587 | |
---|
588 | if(status != XST_SUCCESS) { |
---|
589 | wl_printf(WL_PRINT_ERROR, print_type_node, "Error binding transport...\n"); |
---|
590 | } |
---|
591 | |
---|
592 | // Update User IO |
---|
593 | wl_printf(WL_PRINT_NONE, NULL, "\n!!! Waiting for Network Configuration via Matlab !!! \n\n"); |
---|
594 | |
---|
595 | // Turn off hex mapping; set the center LED: "--" |
---|
596 | // NOTE: The hex mapping will be re-enabled when the broadcast packet is processed to set the node ID |
---|
597 | // |
---|
598 | userio_write_control(USERIO_BASEADDR, (userio_read_control(USERIO_BASEADDR) & (~(W3_USERIO_HEXDISP_L_MAPMODE | W3_USERIO_HEXDISP_R_MAPMODE)))); |
---|
599 | userio_write_hexdisp_left(USERIO_BASEADDR, 0x40); |
---|
600 | userio_write_hexdisp_right(USERIO_BASEADDR, 0x40); |
---|
601 | |
---|
602 | } else { |
---|
603 | wl_printf(WL_PRINT_INFO, print_type_node, "NODE_IP_RESET Packet with Serial Number %d ignored. My serial number is %d \n", Xil_Ntohl(cmd_args_32[0]), w3_eeprom_readSerialNum(EEPROM_BASEADDR)); |
---|
604 | } |
---|
605 | |
---|
606 | // Tell the transport that the command has already sent a response |
---|
607 | resp_sent = RESP_SENT; |
---|
608 | break; |
---|
609 | |
---|
610 | |
---|
611 | //--------------------------------------------------------------------- |
---|
612 | case CMDID_NODE_MEM_RW: |
---|
613 | // Read / write arbitrary memory location |
---|
614 | // |
---|
615 | // Write Message format: |
---|
616 | // cmd_args_32[0] Command == CMD_PARAM_WRITE_VAL |
---|
617 | // cmd_args_32[1] Address |
---|
618 | // cmd_args_32[2] Length (number of u32 words to write) |
---|
619 | // cmd_args_32[3:] Values to write (integral number of u32 words) |
---|
620 | // Response format: |
---|
621 | // resp_args_32[0] Status |
---|
622 | // |
---|
623 | // Read Message format: |
---|
624 | // cmd_args_32[0] Command == CMD_PARAM_READ_VAL |
---|
625 | // cmd_args_32[1] Address |
---|
626 | // cmd_args_32[2] Length (number of u32 words to read) |
---|
627 | // Response format: |
---|
628 | // resp_args_32[0] Status |
---|
629 | // resp_args_32[1] Length (number of u32 values) |
---|
630 | // resp_args_32[2:] Memory values (length u32 values) |
---|
631 | // |
---|
632 | msg_cmd = Xil_Ntohl(cmd_args_32[0]); |
---|
633 | mem_addr = Xil_Ntohl(cmd_args_32[1]); |
---|
634 | mem_length = Xil_Ntohl(cmd_args_32[2]); |
---|
635 | status = CMD_PARAM_SUCCESS; |
---|
636 | default_response = WL_TRUE; |
---|
637 | |
---|
638 | switch (msg_cmd) { |
---|
639 | case CMD_PARAM_WRITE_VAL: |
---|
640 | wl_printf(WL_PRINT_INFO, print_type_node, "Write CPU High Mem\n"); |
---|
641 | wl_printf(WL_PRINT_INFO, print_type_node, " Addr: 0x%08x\n", mem_addr); |
---|
642 | wl_printf(WL_PRINT_INFO, print_type_node, " Len: %d\n", mem_length); |
---|
643 | |
---|
644 | // Don't bother if length is clearly bogus |
---|
645 | if (mem_length < CMD_PARAM_NODE_MEM_RW_MAX_BYTES) { |
---|
646 | for (mem_index = 0; mem_index < mem_length; mem_index++) { |
---|
647 | wl_printf(WL_PRINT_INFO, print_type_node, " W[%2d]: 0x%08x\n", mem_index, Xil_Ntohl(cmd_args_32[3 + mem_index])); |
---|
648 | Xil_Out32((mem_addr + (mem_index * sizeof(u32))), Xil_Ntohl(cmd_args_32[3 + mem_index])); |
---|
649 | } |
---|
650 | } else { |
---|
651 | wl_printf(WL_PRINT_ERROR, print_type_node, "NODE_MEM_RW write longer than %d bytes\n", CMD_PARAM_NODE_MEM_RW_MAX_BYTES); |
---|
652 | status = CMD_PARAM_ERROR; |
---|
653 | } |
---|
654 | break; |
---|
655 | |
---|
656 | case CMD_PARAM_READ_VAL: |
---|
657 | wl_printf(WL_PRINT_INFO, print_type_node, "Read CPU High Mem:\n"); |
---|
658 | wl_printf(WL_PRINT_INFO, print_type_node, " Addr: 0x%08x\n", mem_addr); |
---|
659 | wl_printf(WL_PRINT_INFO, print_type_node, " Len: %d\n", mem_length); |
---|
660 | |
---|
661 | // Add payload to response |
---|
662 | if(mem_length < CMD_PARAM_NODE_MEM_RW_MAX_BYTES) { |
---|
663 | |
---|
664 | // Don't set the default response / Don't send any response |
---|
665 | default_response = WL_FALSE; |
---|
666 | |
---|
667 | // Add length argument to response |
---|
668 | resp_args_32[resp_index++] = Xil_Htonl(status); |
---|
669 | resp_args_32[resp_index++] = Xil_Htonl(mem_length); |
---|
670 | |
---|
671 | resp_hdr->length += (resp_index * sizeof(resp_args_32)); |
---|
672 | resp_hdr->num_args = resp_index; |
---|
673 | |
---|
674 | for (mem_index = 0; mem_index < mem_length; mem_index++) { |
---|
675 | resp_args_32[resp_index + mem_index] = Xil_Ntohl(Xil_In32((void*)(mem_addr) + (mem_index * sizeof(u32)))); |
---|
676 | } |
---|
677 | |
---|
678 | // Update response header with payload length |
---|
679 | resp_hdr->length += (mem_length * sizeof(u32)); |
---|
680 | resp_hdr->num_args += mem_length; |
---|
681 | } else { |
---|
682 | wl_printf(WL_PRINT_ERROR, print_type_node, "NODE_MEM_RW read longer than %d bytes\n", CMD_PARAM_NODE_MEM_RW_MAX_BYTES); |
---|
683 | status = CMD_PARAM_ERROR; |
---|
684 | } |
---|
685 | break; |
---|
686 | |
---|
687 | default: |
---|
688 | wl_printf(WL_PRINT_ERROR, print_type_node, "Unknown command for 0x%6x: %d\n", cmd_id, msg_cmd); |
---|
689 | status = CMD_PARAM_ERROR; |
---|
690 | break; |
---|
691 | } |
---|
692 | |
---|
693 | if (default_response == WL_TRUE) { |
---|
694 | // Send default response |
---|
695 | resp_args_32[resp_index++] = Xil_Htonl(status); |
---|
696 | |
---|
697 | resp_hdr->length += (resp_index * sizeof(resp_args_32)); |
---|
698 | resp_hdr->num_args = resp_index; |
---|
699 | } |
---|
700 | break; |
---|
701 | |
---|
702 | |
---|
703 | //--------------------------------------------------------------------- |
---|
704 | default: |
---|
705 | wl_printf(WL_PRINT_ERROR, print_type_node, "Unknown node command: %d\n", cmd_id); |
---|
706 | break; |
---|
707 | } |
---|
708 | |
---|
709 | return resp_sent; |
---|
710 | } |
---|
711 | |
---|
712 | |
---|
713 | |
---|
714 | /*****************************************************************************/ |
---|
715 | /** |
---|
716 | * Node Clock Initialization Function |
---|
717 | * |
---|
718 | * This function will initialize the clock module |
---|
719 | * |
---|
720 | * @param None |
---|
721 | * |
---|
722 | * @return int - Status of the command: |
---|
723 | * XST_SUCCESS - Command completed successfully |
---|
724 | * XST_FAILURE - There was an error in the command |
---|
725 | * |
---|
726 | * @note This function works with w3_clock_controller v4.00.a |
---|
727 | * |
---|
728 | *****************************************************************************/ |
---|
729 | int node_clk_initialize() { |
---|
730 | int status = XST_SUCCESS; |
---|
731 | u32 clkmod_status; |
---|
732 | |
---|
733 | // Initialize w3_clock_controller hardware and AD9512 buffers |
---|
734 | // NOTE: The clock initialization will set the clock divider to 2 (for 40MHz clock) to RF A/B AD9963's |
---|
735 | status = clk_init(CLK_BASEADDR, 2); |
---|
736 | if(status != XST_SUCCESS) { |
---|
737 | wl_printf(WL_PRINT_ERROR, print_type_node, "Clock initialization failed with error code: %d\n", status); |
---|
738 | return XST_FAILURE; |
---|
739 | } |
---|
740 | |
---|
741 | // Check for a clock module and configure clock inputs, outputs and dividers as needed |
---|
742 | clkmod_status = clk_config_read_clkmod_status(CLK_BASEADDR); |
---|
743 | |
---|
744 | switch(clkmod_status & CM_STATUS_SW) { |
---|
745 | case CM_STATUS_DET_NOCM: |
---|
746 | case CM_STATUS_DET_CMPLL_BYPASS: |
---|
747 | // No clock module - default config from HDL/driver is good as-is |
---|
748 | wl_printf(WL_PRINT_NONE, NULL, "No clock module detected - selecting on-board clocks\n\n"); |
---|
749 | break; |
---|
750 | |
---|
751 | case CM_STATUS_DET_CMMMCX_CFG_A: |
---|
752 | // CM-MMCX config A: |
---|
753 | // Samp clk: on-board, RF clk: on-board |
---|
754 | // Samp MMCX output: 80MHz, RF MMCX output: 80MHz |
---|
755 | wl_printf(WL_PRINT_NONE, NULL, "CM-MMCX Config A Detected:\n"); |
---|
756 | wl_printf(WL_PRINT_NONE, NULL, " RF: On-board\n Samp: On-board\n MMCX Outputs: Enabled\n\n"); |
---|
757 | |
---|
758 | clk_config_outputs(CLK_BASEADDR, CLK_OUTPUT_ON, (CLK_SAMP_OUTSEL_CLKMODHDR | CLK_RFREF_OUTSEL_CLKMODHDR)); |
---|
759 | clk_config_dividers(CLK_BASEADDR, 1, CLK_SAMP_OUTSEL_CLKMODHDR | CLK_RFREF_OUTSEL_CLKMODHDR); |
---|
760 | break; |
---|
761 | |
---|
762 | case CM_STATUS_DET_CMMMCX_CFG_B: |
---|
763 | // CM-MMCX config B: |
---|
764 | // Samp clk: off-board, RF clk: off-board |
---|
765 | // Samp MMCX output: 80MHz, RF MMCX output: 80MHz |
---|
766 | wl_printf(WL_PRINT_NONE, NULL, "CM-MMCX Config B Detected:\n"); |
---|
767 | wl_printf(WL_PRINT_NONE, NULL, " RF: Off-board\n Samp: Off-board\n MMCX Outputs: Enabled\n\n"); |
---|
768 | |
---|
769 | clk_config_input_rf_ref(CLK_BASEADDR, CLK_INSEL_CLKMOD); |
---|
770 | clk_config_outputs(CLK_BASEADDR, CLK_OUTPUT_ON, (CLK_SAMP_OUTSEL_CLKMODHDR | CLK_RFREF_OUTSEL_CLKMODHDR)); |
---|
771 | clk_config_dividers(CLK_BASEADDR, 1, (CLK_SAMP_OUTSEL_CLKMODHDR | CLK_RFREF_OUTSEL_CLKMODHDR)); |
---|
772 | break; |
---|
773 | |
---|
774 | case CM_STATUS_DET_CMMMCX_CFG_C: |
---|
775 | // CM-MMCX config C: |
---|
776 | // Samp clk: off-board, RF clk: off-board |
---|
777 | // Samp MMCX output: Off, RF MMCX output: Off |
---|
778 | wl_printf(WL_PRINT_NONE, NULL, "CM-MMCX Config C Detected:\n"); |
---|
779 | wl_printf(WL_PRINT_NONE, NULL, " RF: Off-board\n Samp: Off-board\n MMCX Outputs: Disabled\n\n"); |
---|
780 | |
---|
781 | clk_config_input_rf_ref(CLK_BASEADDR, CLK_INSEL_CLKMOD); |
---|
782 | clk_config_outputs(CLK_BASEADDR, CLK_OUTPUT_OFF, (CLK_SAMP_OUTSEL_CLKMODHDR | CLK_RFREF_OUTSEL_CLKMODHDR)); |
---|
783 | break; |
---|
784 | |
---|
785 | case CM_STATUS_DET_CMPLL_CFG_A: |
---|
786 | // CM-PLL config A: |
---|
787 | // Samp clk: clock module PLL |
---|
788 | // RF clk: on-board |
---|
789 | wl_printf(WL_PRINT_NONE, NULL, "CM-PLL Config A Detected:\n"); |
---|
790 | wl_printf(WL_PRINT_NONE, NULL, " RF: On-board\n Samp: clock module PLL\n"); |
---|
791 | |
---|
792 | // No changes from configuration applied by HDL and clk_init() |
---|
793 | break; |
---|
794 | |
---|
795 | case CM_STATUS_DET_CMPLL_CFG_B: |
---|
796 | // CM-PLL config B: |
---|
797 | // Samp clk: clock module PLL |
---|
798 | // RF clk: clock module PLL |
---|
799 | wl_printf(WL_PRINT_NONE, NULL, "CM-PLL Config B Detected:\n"); |
---|
800 | wl_printf(WL_PRINT_NONE, NULL, " RF: clock module PLL\n Samp: clock module PLL\n"); |
---|
801 | |
---|
802 | clk_config_input_rf_ref(CLK_BASEADDR, CLK_INSEL_CLKMOD); |
---|
803 | break; |
---|
804 | |
---|
805 | case CM_STATUS_DET_CMPLL_CFG_C: |
---|
806 | // CM-PLL config C: |
---|
807 | // Samp clk: clock module PLL |
---|
808 | // RF clk: clock module PLL |
---|
809 | wl_printf(WL_PRINT_NONE, NULL, "CM-PLL Config C Detected:\n"); |
---|
810 | wl_printf(WL_PRINT_NONE, NULL, " RF: clock module PLL\n Samp: clock module PLL\n"); |
---|
811 | |
---|
812 | clk_config_input_rf_ref(CLK_BASEADDR, CLK_INSEL_CLKMOD); |
---|
813 | break; |
---|
814 | |
---|
815 | default: |
---|
816 | // Should be impossible |
---|
817 | wl_printf(WL_PRINT_ERROR, print_type_node, "ERROR: Invalid clock module switch settings! (0x%08x)\n", clkmod_status); |
---|
818 | return XST_FAILURE; |
---|
819 | break; |
---|
820 | } |
---|
821 | |
---|
822 | #if WARPLAB_CONFIG_4RF |
---|
823 | // Turn on clocks to FMC |
---|
824 | clk_config_outputs(CLK_BASEADDR, CLK_OUTPUT_ON, (CLK_SAMP_OUTSEL_FMC | CLK_RFREF_OUTSEL_FMC)); |
---|
825 | |
---|
826 | // FMC samp clock divider = 2 (40MHz sampling reference, same as on-board AD9963 ref clk) |
---|
827 | clk_config_dividers(CLK_BASEADDR, 2, CLK_SAMP_OUTSEL_FMC); |
---|
828 | |
---|
829 | // FMC RF ref clock divider = 2 (40MHz RF reference, same as on-board MAX2829 ref clk) |
---|
830 | clk_config_dividers(CLK_BASEADDR, 2, CLK_RFREF_OUTSEL_FMC); |
---|
831 | #endif |
---|
832 | |
---|
833 | return status; |
---|
834 | } |
---|
835 | |
---|
836 | |
---|
837 | |
---|
838 | /*****************************************************************************/ |
---|
839 | /** |
---|
840 | * Node Initialization Function |
---|
841 | * |
---|
842 | * This function will initialize many aspects of the node. |
---|
843 | * |
---|
844 | * @param None. |
---|
845 | * |
---|
846 | * @return int - Status of the command: |
---|
847 | * XST_SUCCESS - Command completed successfully |
---|
848 | * XST_FAILURE - There was an error in the command |
---|
849 | * |
---|
850 | *****************************************************************************/ |
---|
851 | int node_init(){ |
---|
852 | int status = XST_SUCCESS; |
---|
853 | u64 timestamp; |
---|
854 | |
---|
855 | // Configure Microblaze |
---|
856 | microblaze_enable_exceptions(); |
---|
857 | Xil_DCacheDisable(); |
---|
858 | Xil_ICacheDisable(); |
---|
859 | |
---|
860 | // Initialize hardware components |
---|
861 | wl_timer_initialize(); |
---|
862 | wl_gpio_debug_initialize(); |
---|
863 | wl_sysmon_initialize(); |
---|
864 | wl_uart_initialize(); |
---|
865 | iic_eeprom_init(EEPROM_BASEADDR, 0x64); |
---|
866 | |
---|
867 | // Initialize LED global variables |
---|
868 | use_leds = 1; |
---|
869 | red_led_state = 0; |
---|
870 | green_led_state = 0; |
---|
871 | |
---|
872 | // Initialize the central DMA (CDMA) driver |
---|
873 | // NOTE: Need to die if not successful |
---|
874 | status = wl_cdma_initialize(); |
---|
875 | if (status != XST_SUCCESS) { |
---|
876 | return XST_FAILURE; |
---|
877 | } |
---|
878 | |
---|
879 | // Initialize the hardware clocking |
---|
880 | status = node_clk_initialize(); |
---|
881 | if (status != XST_SUCCESS) { |
---|
882 | return XST_FAILURE; |
---|
883 | } |
---|
884 | |
---|
885 | // Populate the Node ID |
---|
886 | node = userio_read_inputs(USERIO_BASEADDR) & W3_USERIO_DIPSW; |
---|
887 | |
---|
888 | // If the node has dip switch value of 0xF, then set the node to 0xFFFF |
---|
889 | if ( node == 0xF ) { |
---|
890 | node = 0xFFFF; |
---|
891 | } else { |
---|
892 | userio_write_hexdisp_left(USERIO_BASEADDR, ((node+1)/10) ); |
---|
893 | userio_write_hexdisp_right(USERIO_BASEADDR, (node+1)%10); |
---|
894 | } |
---|
895 | |
---|
896 | // Check the WARPLab version |
---|
897 | if( (wl_get_design_ver()&0xFFFF00) != ((REQ_WARPLAB_HW_VER)&0xFFFF00) ){ |
---|
898 | wl_printf(WL_PRINT_ERROR, print_type_node, "HW/SW Version Mismatch! Expected HW Ver: 0x%x -- Actual HW Ver: 0x%x\n\n", (REQ_WARPLAB_HW_VER), wl_get_design_ver()); |
---|
899 | return XST_FAILURE; |
---|
900 | } |
---|
901 | |
---|
902 | // Print node information |
---|
903 | wl_printf(WL_PRINT_NONE, print_type_node, "W3-a-%05d using Node ID: %d\n", w3_eeprom_readSerialNum(EEPROM_BASEADDR), node); |
---|
904 | |
---|
905 | // Test to see if DRAM SODIMM is connected to board |
---|
906 | timestamp = get_usec_timestamp(); |
---|
907 | |
---|
908 | while((get_usec_timestamp() - timestamp) < 100000){ |
---|
909 | if(wl_get_dram_init_done() == 1){ |
---|
910 | wl_printf(WL_PRINT_NONE, NULL, "DRAM SODIMM detected ... \n"); |
---|
911 | |
---|
912 | if(ddr_sodim_memory_test() == XST_SUCCESS){ |
---|
913 | dram_present = 1; |
---|
914 | |
---|
915 | if (CLEAR_DDR_ON_BOOT) { |
---|
916 | clear_ddr(WL_VERBOSE); |
---|
917 | } else { |
---|
918 | wl_printf(WL_PRINT_NONE, NULL, " Contents not cleared\n"); |
---|
919 | } |
---|
920 | } else { |
---|
921 | dram_present = 0; |
---|
922 | wl_printf(WL_PRINT_NONE, NULL, " Memory test failed; Will not use DRAM\n"); |
---|
923 | } |
---|
924 | break; |
---|
925 | } |
---|
926 | } |
---|
927 | |
---|
928 | if (wl_get_dram_init_done() != 1) { |
---|
929 | wl_printf(WL_PRINT_NONE, NULL, "DRAM SODIMM not detected.\n"); |
---|
930 | } |
---|
931 | |
---|
932 | return status; |
---|
933 | } |
---|
934 | |
---|
935 | |
---|
936 | |
---|
937 | /*****************************************************************************/ |
---|
938 | /** |
---|
939 | * Set Node Error Status |
---|
940 | * |
---|
941 | * This function will set the LEDs to be 0x5 and then set the hex display |
---|
942 | * to Ex, where x is the value of the status error |
---|
943 | * |
---|
944 | * @param status - Number from 0 - 0xF to indicate status error |
---|
945 | * |
---|
946 | * @return None |
---|
947 | * |
---|
948 | *****************************************************************************/ |
---|
949 | void set_node_error_status(int status) { |
---|
950 | |
---|
951 | userio_write_leds_red(USERIO_BASEADDR, 0x5); |
---|
952 | userio_write_hexdisp_left(USERIO_BASEADDR, 0xE); |
---|
953 | userio_write_hexdisp_right(USERIO_BASEADDR, status); |
---|
954 | } |
---|
955 | |
---|
956 | |
---|
957 | |
---|
958 | /*****************************************************************************/ |
---|
959 | /** |
---|
960 | * LED Functions |
---|
961 | * |
---|
962 | * Blink Node: |
---|
963 | * For WARP v3 Hardware, this function will toggle LEDs. The pattern depends |
---|
964 | * on the value of the LEDs before the function was called. |
---|
965 | * |
---|
966 | * @param num_blinks - Number of blinks (0 means blink forever) |
---|
967 | * blink_time - Time in us between blinks |
---|
968 | * |
---|
969 | * @return None |
---|
970 | * |
---|
971 | *****************************************************************************/ |
---|
972 | void blink_node(int num_blinks, int blink_time) { |
---|
973 | int i; |
---|
974 | use_leds = 0; |
---|
975 | |
---|
976 | if ( num_blinks > 0 ) { |
---|
977 | // Perform standard blink |
---|
978 | for( i = 0; i < num_blinks; i++ ) { |
---|
979 | userio_toggle_leds_green(USERIO_BASEADDR, 0xF); |
---|
980 | usleep( blink_time ); |
---|
981 | } |
---|
982 | } else { |
---|
983 | // Perform an infinite blink |
---|
984 | while(1){ |
---|
985 | userio_toggle_leds_red(USERIO_BASEADDR, 0xF); |
---|
986 | usleep( blink_time ); |
---|
987 | } |
---|
988 | } |
---|
989 | |
---|
990 | use_leds = 1; |
---|
991 | } |
---|
992 | |
---|
993 | |
---|
994 | // Increment the green LEDs in a one-hot manner (ie 0x1 -> 0x2 -> 0x4 -> 0x8 -> 0x1 ...) |
---|
995 | void increment_green_leds_one_hot() { |
---|
996 | if (use_leds) { |
---|
997 | userio_write_leds_green(USERIO_BASEADDR, (1 << green_led_state)); |
---|
998 | green_led_state = (green_led_state + 1) % 4; |
---|
999 | } |
---|
1000 | } |
---|
1001 | |
---|
1002 | |
---|
1003 | // Increment the red LEDs in a one-hot manner (ie 0x1 -> 0x2 -> 0x4 -> 0x8 -> 0x1 ...) |
---|
1004 | void increment_red_leds_one_hot() { |
---|
1005 | if (use_leds) { |
---|
1006 | userio_write_leds_red(USERIO_BASEADDR, (1 << red_led_state)); |
---|
1007 | red_led_state = (red_led_state + 1) % 4; |
---|
1008 | } |
---|
1009 | } |
---|
1010 | |
---|
1011 | |
---|
1012 | |
---|
1013 | /*****************************************************************************/ |
---|
1014 | /** |
---|
1015 | * Process Received UART characters |
---|
1016 | * |
---|
1017 | * Due to the need to use interrupts for IQ processing, we decided to also connect and |
---|
1018 | * initialize the UART interrupt. This allows the user to communicate with the board |
---|
1019 | * via a terminal program, such as Putty. Each character typed in the terminal program |
---|
1020 | * will cause the function below to be called with the ACSII value of the character. |
---|
1021 | * |
---|
1022 | * Currently, a few characters have been allocated for various debug modes, but no |
---|
1023 | * characters have been officially allocated for particular functions. Therefore, any |
---|
1024 | * user can feel free to extend this method if needed for their experiment. By default, |
---|
1025 | * the node will echo all characters back to the terminal. |
---|
1026 | * |
---|
1027 | * @param rx_byte - Character received from the UART via the terminal program on the host |
---|
1028 | * |
---|
1029 | * @return None |
---|
1030 | * |
---|
1031 | * @note Some characters have been allocated for various debug functions that are enabled |
---|
1032 | * through defines found in wl_node.c and wl_common.h: |
---|
1033 | * |
---|
1034 | * _DEBUG_STORAGE_ |
---|
1035 | * - 'c' - Clear the debug storage (ie reset the storage) |
---|
1036 | * - 'p' - Print the contents of the debug storage |
---|
1037 | * |
---|
1038 | * ALLOW_ETHERNET_PAUSE |
---|
1039 | * - 's' - Toggles the state of Ethernet receptions; Will either pause or un-pause |
---|
1040 | * the reception of Ethernet packets based on the current state. By default, |
---|
1041 | * Ethernet receptions are un-paused. |
---|
1042 | * |
---|
1043 | *****************************************************************************/ |
---|
1044 | void uart_rx(u8 rx_byte){ |
---|
1045 | char character = rx_byte; |
---|
1046 | |
---|
1047 | // Process the received character |
---|
1048 | switch(character) { |
---|
1049 | |
---|
1050 | #if _DEBUG_STORAGE_ |
---|
1051 | case 'c': |
---|
1052 | reset_debug_storage(); |
---|
1053 | break; |
---|
1054 | |
---|
1055 | case 'p': |
---|
1056 | print_debug_storage(); |
---|
1057 | break; |
---|
1058 | #endif |
---|
1059 | |
---|
1060 | #if ALLOW_ETHERNET_PAUSE |
---|
1061 | case 's': |
---|
1062 | if (ethernet_pause == 1) { |
---|
1063 | ethernet_pause = 0; |
---|
1064 | } else { |
---|
1065 | ethernet_pause = 1; |
---|
1066 | } |
---|
1067 | break; |
---|
1068 | #endif |
---|
1069 | |
---|
1070 | // Echo any unknown characters back to the terminal |
---|
1071 | default: |
---|
1072 | wl_printf(WL_PRINT_NONE, NULL, "%c", rx_byte); |
---|
1073 | break; |
---|
1074 | } |
---|
1075 | } |
---|
1076 | |
---|
1077 | |
---|
1078 | |
---|
1079 | /*****************************************************************************/ |
---|
1080 | /** |
---|
1081 | * @brief Node initialization |
---|
1082 | * |
---|
1083 | * This is the main function of the embedded C code. It will initialize the |
---|
1084 | * board and then begin a infinite polling loop on the Ethernet peripheral to |
---|
1085 | * process any commands that are sent to the board. |
---|
1086 | * |
---|
1087 | * @param None |
---|
1088 | * |
---|
1089 | * @return None. Implements an infinite while loop |
---|
1090 | * |
---|
1091 | * @note The hex display values during boot should be as follows: |
---|
1092 | * OFF - Bit stream is being downloaded to the board |
---|
1093 | * 00 - Initial power up of the downloaded bit stream |
---|
1094 | * 01 to 99 - ID value of the node. |
---|
1095 | * -- - Node is ready to recieve network configuration |
---|
1096 | * Ex - Error condition where x is the value of the status error |
---|
1097 | * Please plug in a USB cable for further debug messages |
---|
1098 | * |
---|
1099 | * A value of 01 to 99 or -- indicates a successful boot and is ready to |
---|
1100 | * receive commands. |
---|
1101 | * |
---|
1102 | ******************************************************************************/ |
---|
1103 | int main() { |
---|
1104 | int status; |
---|
1105 | u32 tmp_eth_dev_num; |
---|
1106 | volatile u32 link_status = LINK_NOT_READY; |
---|
1107 | u32 init_transport = 1; // Does the transport need to be initialized |
---|
1108 | |
---|
1109 | // ------------------------------------------ |
---|
1110 | // Initialize global variables |
---|
1111 | // |
---|
1112 | dram_present = 0; |
---|
1113 | configure_buffers = 1; |
---|
1114 | |
---|
1115 | #if ALLOW_ETHERNET_PAUSE |
---|
1116 | ethernet_pause = 0; |
---|
1117 | #endif |
---|
1118 | |
---|
1119 | // Set the print level |
---|
1120 | wl_set_print_level(DEFAULT_DEBUG_PRINT_LEVEL); |
---|
1121 | |
---|
1122 | |
---|
1123 | // ------------------------------------------ |
---|
1124 | // Print initial message to UART |
---|
1125 | // |
---|
1126 | wl_printf(WL_PRINT_NONE, NULL, "\fWARPLab v%d.%d.%d (compiled %s %s)\n", WARPLAB_VER_MAJOR, WARPLAB_VER_MINOR, WARPLAB_VER_REV, __DATE__, __TIME__); |
---|
1127 | |
---|
1128 | if(WARPLAB_CONFIG_4RF) { |
---|
1129 | wl_printf(WL_PRINT_NONE, NULL, "Configured for 4 RF Interfaces - FMC-RF-2X245 FMC module must be installed\n"); |
---|
1130 | } else { |
---|
1131 | wl_printf(WL_PRINT_NONE, NULL, "Configured for 2 RF Interfaces - Using WARP v3 on-board RF interfaces\n"); |
---|
1132 | } |
---|
1133 | |
---|
1134 | |
---|
1135 | // ------------------------------------------ |
---|
1136 | // Check that right shift works correctly |
---|
1137 | // Issue with -Os in Xilinx SDK 14.7 |
---|
1138 | if (microblaze_right_shift_test() != XST_SUCCESS) { |
---|
1139 | wl_printf(WL_PRINT_ERROR, print_type_node, "Node right shift error! Exiting...\n"); |
---|
1140 | set_node_error_status(0x0); // Set user IO |
---|
1141 | blink_node(0, 250000); // Infinite blink |
---|
1142 | } |
---|
1143 | |
---|
1144 | |
---|
1145 | // ------------------------------------------ |
---|
1146 | // Node initialization |
---|
1147 | // NOTE: These errors are fatal and status error will be displayed |
---|
1148 | // on the hex display. Also, please attach a USB cable for |
---|
1149 | // terminal debug messages. |
---|
1150 | // |
---|
1151 | status = node_init(); |
---|
1152 | |
---|
1153 | if(status != XST_SUCCESS) { |
---|
1154 | wl_printf(WL_PRINT_ERROR, print_type_node, "Node initialization error! Exiting...\n"); |
---|
1155 | set_node_error_status(0x1); // Set user IO |
---|
1156 | blink_node(0, 250000); // Infinite blink |
---|
1157 | } |
---|
1158 | |
---|
1159 | |
---|
1160 | // ------------------------------------------ |
---|
1161 | // Global initialization |
---|
1162 | // NOTE: These errors are fatal and status error will be displayed |
---|
1163 | // on the hex display. Also, please attach a USB cable for |
---|
1164 | // terminal debug messages. |
---|
1165 | // |
---|
1166 | status = global_initialize(); |
---|
1167 | |
---|
1168 | if(status != XST_SUCCESS) { |
---|
1169 | wl_printf(WL_PRINT_ERROR, print_type_node, "Global initialization error! Exiting...\n"); |
---|
1170 | set_node_error_status(0x2); // Set user IO |
---|
1171 | blink_node(0, 250000); // Infinite blink |
---|
1172 | } |
---|
1173 | |
---|
1174 | |
---|
1175 | // ------------------------------------------ |
---|
1176 | // Transport initialization |
---|
1177 | // NOTE: These errors are fatal and status error will be displayed |
---|
1178 | // on the hex display. Also, please attach a USB cable for |
---|
1179 | // terminal debug messages. |
---|
1180 | // |
---|
1181 | if (WL_USE_ETH_A) { |
---|
1182 | status = transport_init(WL_ETH_A, init_transport); |
---|
1183 | init_transport = 0; |
---|
1184 | } |
---|
1185 | |
---|
1186 | if ((status == XST_SUCCESS) && WL_USE_ETH_B) { |
---|
1187 | status = transport_init(WL_ETH_B, init_transport); |
---|
1188 | init_transport = 0; |
---|
1189 | } |
---|
1190 | |
---|
1191 | if(status != XST_SUCCESS) { |
---|
1192 | wl_printf(WL_PRINT_ERROR, print_type_node, "Transport initialization error! Exiting...\n"); |
---|
1193 | set_node_error_status(0x3); // Set user IO |
---|
1194 | blink_node(0, 250000); // Infinite blink |
---|
1195 | } |
---|
1196 | |
---|
1197 | // A common error that can occur when regenerating the linker script, is that the linker |
---|
1198 | // script puts the global data structures for the WARP IP/UDP transport in to a memory |
---|
1199 | // that is not accessible by the Ethernet DMA. Unfortunately, this is an extremely fatal |
---|
1200 | // error, so this check was added to catch this. |
---|
1201 | // |
---|
1202 | if (WL_USE_ETH_A) { |
---|
1203 | status = eth_not_in_memory_range(WL_ETH_A, XPAR_MICROBLAZE_0_D_BRAM_CTRL_HIGHADDR, XPAR_MICROBLAZE_0_D_BRAM_CTRL_BASEADDR); |
---|
1204 | tmp_eth_dev_num = WL_ETH_A; |
---|
1205 | } |
---|
1206 | |
---|
1207 | if ((status == WARP_IP_UDP_SUCCESS) && WL_USE_ETH_B) { |
---|
1208 | status = eth_not_in_memory_range(WL_ETH_B, XPAR_MICROBLAZE_0_D_BRAM_CTRL_HIGHADDR, XPAR_MICROBLAZE_0_D_BRAM_CTRL_BASEADDR); |
---|
1209 | tmp_eth_dev_num = WL_ETH_B; |
---|
1210 | } |
---|
1211 | |
---|
1212 | if(status != WARP_IP_UDP_SUCCESS) { |
---|
1213 | wl_printf(WL_PRINT_ERROR, print_type_node, "Ethernet device %c: \n", warp_conv_eth_dev_num(tmp_eth_dev_num)); |
---|
1214 | wl_printf(WL_PRINT_ERROR, print_type_node, " Global data structures not accessible by DMA.\n\n"); |
---|
1215 | wl_printf(WL_PRINT_ERROR, print_type_node, "Please update your linker command file to put buffers in shared BRAM. Exiting...\n"); |
---|
1216 | set_node_error_status(0x4); // Set user IO |
---|
1217 | blink_node(0, 250000); // Infinite blink |
---|
1218 | } |
---|
1219 | |
---|
1220 | |
---|
1221 | // ------------------------------------------ |
---|
1222 | // Interrupt initialization |
---|
1223 | // NOTE: These errors are fatal and status error will be displayed |
---|
1224 | // on the hex display. Also, please attach a USB cable for |
---|
1225 | // terminal debug messages. |
---|
1226 | status = wl_interrupt_init(); |
---|
1227 | |
---|
1228 | if(status != XST_SUCCESS) { |
---|
1229 | wl_printf(WL_PRINT_ERROR, print_type_node, "Interrupt initialization error! Exiting...\n"); |
---|
1230 | set_node_error_status(0x5); // Set user IO |
---|
1231 | blink_node(0, 250000); // Infinite blink |
---|
1232 | } |
---|
1233 | |
---|
1234 | |
---|
1235 | // ------------------------------------------ |
---|
1236 | // Wait for Ethernet to finish initializing the link |
---|
1237 | // |
---|
1238 | if (WL_WAIT_FOR_ETH) { |
---|
1239 | wl_printf(WL_PRINT_NONE, NULL, "\nWaiting for Ethernet link ...\n"); |
---|
1240 | |
---|
1241 | while(link_status == LINK_NOT_READY) { |
---|
1242 | |
---|
1243 | // Check the link status of each Ethernet device in use |
---|
1244 | if (((transport_link_status(WL_ETH_A) == LINK_NOT_READY) && WL_USE_ETH_A) || |
---|
1245 | ((transport_link_status(WL_ETH_B) == LINK_NOT_READY) && WL_USE_ETH_B)) { |
---|
1246 | |
---|
1247 | link_status = LINK_NOT_READY; |
---|
1248 | } else { |
---|
1249 | link_status = LINK_READY; |
---|
1250 | } |
---|
1251 | |
---|
1252 | // Update LEDs for a visual cue that we are waiting on the Ethernet device |
---|
1253 | userio_toggle_leds_green(USERIO_BASEADDR, 0x1); |
---|
1254 | usleep(100000); |
---|
1255 | } |
---|
1256 | |
---|
1257 | } else { |
---|
1258 | xil_printf(" Not waiting for Ethernet link. Current status:\n"); |
---|
1259 | |
---|
1260 | if ((transport_link_status(WL_ETH_A) == LINK_READY) && WL_USE_ETH_A) { |
---|
1261 | xil_printf(" ETH A ready\n"); |
---|
1262 | } else { |
---|
1263 | xil_printf(" ETH A not ready\n"); |
---|
1264 | } |
---|
1265 | |
---|
1266 | if ((transport_link_status(WL_ETH_B) == LINK_READY) && WL_USE_ETH_B) { |
---|
1267 | xil_printf(" ETH B ready\n"); |
---|
1268 | } else { |
---|
1269 | xil_printf(" ETH B not ready\n"); |
---|
1270 | } |
---|
1271 | |
---|
1272 | xil_printf("\n Make sure link is ready before using WARPLab.\n"); |
---|
1273 | } |
---|
1274 | |
---|
1275 | wl_printf(WL_PRINT_NONE, NULL, "\nInitialization Successful - Waiting for Commands from MATLAB\n\n"); |
---|
1276 | |
---|
1277 | |
---|
1278 | // ------------------------------------------ |
---|
1279 | // Assign the transport receive callback (how to process received Ethernet packets) |
---|
1280 | // IMPORTANT: Must be called after transport_init() |
---|
1281 | transport_set_process_hton_msg_callback((void *)node_rx_from_transport); |
---|
1282 | |
---|
1283 | |
---|
1284 | // ------------------------------------------ |
---|
1285 | // Assign the uart receive callback (how to process received uart characters) |
---|
1286 | // IMPORTANT: Must be called after node_init() |
---|
1287 | wl_set_uart_rx_callback((void*)uart_rx); |
---|
1288 | |
---|
1289 | |
---|
1290 | // ------------------------------------------ |
---|
1291 | // Enable all interrupts |
---|
1292 | status = wl_interrupt_restore_state(INTERRUPTS_ENABLED); |
---|
1293 | |
---|
1294 | if(status != XST_SUCCESS) { |
---|
1295 | wl_printf(WL_PRINT_ERROR, print_type_node, "Cannot enable interrupts! Exiting...\n"); |
---|
1296 | set_node_error_status(0x6); // Set user IO |
---|
1297 | blink_node(0, 250000); // Infinite blink |
---|
1298 | } |
---|
1299 | |
---|
1300 | |
---|
1301 | // ------------------------------------------ |
---|
1302 | // Blink LEDs to show we are done |
---|
1303 | // |
---|
1304 | userio_write_leds_green(USERIO_BASEADDR, 0x5); |
---|
1305 | blink_node(10, 100000); // Blink 10 times |
---|
1306 | userio_write_leds_red(USERIO_BASEADDR, 0x0); |
---|
1307 | userio_write_leds_green(USERIO_BASEADDR, 0x0); |
---|
1308 | |
---|
1309 | // If you are in configure over network mode, then indicate that to the user |
---|
1310 | if ( node == 0xFFFF ) { |
---|
1311 | wl_printf(WL_PRINT_NONE, NULL, "!!! Waiting for Network Configuration via Matlab !!! \n\n"); |
---|
1312 | |
---|
1313 | // Turn off hex mapping; set the center LED |
---|
1314 | // NOTE: hex mapping will be re-enabled when the bcast packet is processed to set the node ID |
---|
1315 | userio_write_control(USERIO_BASEADDR, (userio_read_control(USERIO_BASEADDR) & (~(W3_USERIO_HEXDISP_L_MAPMODE | W3_USERIO_HEXDISP_R_MAPMODE)))); |
---|
1316 | userio_write_hexdisp_left(USERIO_BASEADDR, 0x40); |
---|
1317 | userio_write_hexdisp_right(USERIO_BASEADDR, 0x40); |
---|
1318 | } |
---|
1319 | |
---|
1320 | |
---|
1321 | // ------------------------------------------ |
---|
1322 | // Go into infinite while loop |
---|
1323 | // |
---|
1324 | while(1){ |
---|
1325 | |
---|
1326 | #if ALLOW_ETHERNET_PAUSE |
---|
1327 | if (ethernet_pause) { |
---|
1328 | // Indicate visually to the user that the node is not accepting Ethernet packets |
---|
1329 | increment_red_leds_one_hot(); |
---|
1330 | usleep(100000); |
---|
1331 | } else { |
---|
1332 | // Process Ethernet packets. |
---|
1333 | // NOTE: This is polling based and not interrupt based. |
---|
1334 | // NOTE: #if is used in this case vs standard if() statement since this section of |
---|
1335 | // code is performance critical. |
---|
1336 | // |
---|
1337 | #if WL_USE_ETH_A |
---|
1338 | transport_poll(WL_ETH_A); |
---|
1339 | #endif |
---|
1340 | #if WL_USE_ETH_B |
---|
1341 | transport_poll(WL_ETH_B); |
---|
1342 | #endif |
---|
1343 | } |
---|
1344 | #else |
---|
1345 | // Process Ethernet packets. |
---|
1346 | // NOTE: This is polling based and not interrupt based. |
---|
1347 | // NOTE: #if is used in this case vs standard if() statement since this section of |
---|
1348 | // code is performance critical. |
---|
1349 | // |
---|
1350 | #if WL_USE_ETH_A |
---|
1351 | transport_poll(WL_ETH_A); |
---|
1352 | #endif |
---|
1353 | #if WL_USE_ETH_B |
---|
1354 | transport_poll(WL_ETH_B); |
---|
1355 | #endif |
---|
1356 | |
---|
1357 | #endif |
---|
1358 | |
---|
1359 | } |
---|
1360 | |
---|
1361 | return XST_SUCCESS; |
---|
1362 | } |
---|
1363 | |
---|
1364 | #endif |
---|
1365 | |
---|
1366 | // |
---|
1367 | // |
---|
1368 | // NOTE: As of WARPLab 7.6.0, the code associated with WARP v2 has been removed. If you are using WARP v2, please use |
---|
1369 | // WARPLab 7.5.1 or earlier. |
---|
1370 | // |
---|
1371 | // |
---|