1 | %------------------------------------------------------------------------- |
---|
2 | % WARPLab Framework |
---|
3 | % |
---|
4 | % Copyright 2013, Mango Communications. All rights reserved. |
---|
5 | % Distributed under the WARP license (http://warpproject.org/license) |
---|
6 | % |
---|
7 | % Chris Hunter (chunter [at] mangocomm.com) |
---|
8 | % Patrick Murphy (murphpo [at] mangocomm.com) |
---|
9 | % Erik Welsh (welsh [at] mangocomm.com) |
---|
10 | %------------------------------------------------------------------------- |
---|
11 | |
---|
12 | classdef wl_node < handle_light |
---|
13 | % The WARPLab node class represents one node in a WARPLab network |
---|
14 | % wl_node is the primary interface for interacting with WARPLab nodes. |
---|
15 | % This class provides methods for sending commands, exchanging samples |
---|
16 | % and checking the status of WARPLab nodes. |
---|
17 | % wl_node wraps the six underlying components that comprise a WARPLab |
---|
18 | % node implementation: node, baseband, interface, transport, trigger |
---|
19 | % setup and user extensions. |
---|
20 | |
---|
21 | properties (SetAccess = protected) |
---|
22 | ID; % Unique identification for this node |
---|
23 | description; % String description of this node (auto-generated) |
---|
24 | serialNumber; % Node's serial number, read from EEPROM on hardware |
---|
25 | eth_MAC_addr; % Node's MAC address (for ETH_A on WARP v3) |
---|
26 | fpgaDNA; % Node's FPGA's unique identification (on select hardware) |
---|
27 | hwVer; % WARP hardware version of this node |
---|
28 | wlVer_major; % WARPLab version running on this node |
---|
29 | wlVer_minor; |
---|
30 | wlVer_revision; |
---|
31 | num_interfacesGroups; % # of interface groups attached to node |
---|
32 | num_interfaces; % Vector of length num_interfaceGroups with |
---|
33 | % # of interfaces per group |
---|
34 | interfaceGroups; % Node's interface group object(s) |
---|
35 | interfaceIDs; % Vector of interface identifiers |
---|
36 | baseband; % Node's baseband object |
---|
37 | trigger_manager; % Node's trigger configuration object |
---|
38 | transport; % Node's transport object |
---|
39 | user; % Node's user extension object |
---|
40 | end |
---|
41 | |
---|
42 | properties (SetAccess = public) |
---|
43 | name; % User specified name for node; user scripts supply this |
---|
44 | end |
---|
45 | |
---|
46 | properties(Hidden = true,Constant = true) |
---|
47 | % Command Groups - Most Significant Byte of Command ID |
---|
48 | GRPID_NODE = hex2dec('00'); |
---|
49 | GRPID_TRANS = hex2dec('10'); |
---|
50 | GRPID_IFC = hex2dec('20'); |
---|
51 | GRPID_BB = hex2dec('30'); |
---|
52 | GRPID_TRIGMNGR = hex2dec('40'); |
---|
53 | GRPID_USER = hex2dec('50'); |
---|
54 | end |
---|
55 | |
---|
56 | properties(Hidden = true,Constant = true) |
---|
57 | % These constants define specific command IDs used by this object. |
---|
58 | % Their C counterparts are found in wl_node.h |
---|
59 | GRP = 'node'; |
---|
60 | CMD_INITIALIZE = 1; % 0x000001 |
---|
61 | CMD_INFO = 2; % 0x000002 |
---|
62 | CMD_IDENTIFY = 3; % 0x000003 |
---|
63 | CMD_TEMPERATURE = 4; % 0x000004 |
---|
64 | CMD_NODE_CONFIG_SETUP = 5; % 0x000005 |
---|
65 | CMD_NODE_CONFIG_RESET = 6; % 0x000006 |
---|
66 | |
---|
67 | CMD_MEM_RW = 16; % 0x000010 |
---|
68 | end |
---|
69 | |
---|
70 | methods |
---|
71 | function obj = wl_node() |
---|
72 | % The constructor is intentionally blank for wl_node objects. |
---|
73 | % Instead, the objects are configured via the separate applyConfiguration method. |
---|
74 | end |
---|
75 | |
---|
76 | |
---|
77 | function applyConfiguration(objVec, IDVec) |
---|
78 | % Set all the node object parameters |
---|
79 | |
---|
80 | % Apply Configuration only operates on one object at a time |
---|
81 | if ((length(objVec) > 1) || (length(IDVec) > 1)) |
---|
82 | error('applyConfiguration only operates on a single object with a single ID. Provided parameters with lengths: %d and %d', length(objVec), length(IDVec) ); |
---|
83 | end |
---|
84 | |
---|
85 | % Apply configuration will finish setting properties for a specific hardware node |
---|
86 | obj = objVec(1); |
---|
87 | currID = IDVec(1); |
---|
88 | |
---|
89 | % currID can be either a structure containing node information or a number |
---|
90 | switch (class(currID)) |
---|
91 | case 'struct' |
---|
92 | if ( ~strcmp( currID.serialNumber, '' ) ) |
---|
93 | obj.serialNumber = sscanf( currID.serialNumber, 'W3-a-%d' ); % Only store the last 5 digits ( "W3-a-" is not stored ) |
---|
94 | else |
---|
95 | error('Unknown argument. Serial Number provided is blank'); |
---|
96 | end |
---|
97 | |
---|
98 | if ( ~strcmp( currID.ID, '' ) ) |
---|
99 | obj.ID = sscanf( currID.ID, '%d'); |
---|
100 | else |
---|
101 | error('Unknown argument. Node ID provided is blank'); |
---|
102 | end |
---|
103 | |
---|
104 | if ( ~strcmp( currID.name, '' ) ) % Name is an optional parameter in the structure |
---|
105 | obj.name = currID.name; |
---|
106 | end |
---|
107 | |
---|
108 | case 'double' |
---|
109 | obj.ID = currID; % The node ID must match the DIP switch on the WARP board |
---|
110 | |
---|
111 | otherwise |
---|
112 | error('Unknown argument. IDVec is of type "%s", need "struct", or "double"', class(currID)); |
---|
113 | end |
---|
114 | |
---|
115 | |
---|
116 | % Get ini configuration file |
---|
117 | configFile = which('wl_config.ini'); |
---|
118 | if(isempty(configFile)) |
---|
119 | error('cannot find wl_config.ini. please run wl_setup.m'); |
---|
120 | end |
---|
121 | |
---|
122 | % Use Ethernet/UDP transport for all wl_nodes. The specific type of transport |
---|
123 | % is specified in the user's wl_config.ini file that is created via wl_setup.m |
---|
124 | readKeys = {'network','','transport',''}; |
---|
125 | transportType = inifile(configFile,'read',readKeys); |
---|
126 | transportType = transportType{1}; |
---|
127 | |
---|
128 | switch(transportType) |
---|
129 | case 'java' |
---|
130 | obj.transport = wl_transport_eth_udp_java; |
---|
131 | case 'wl_mex_udp' |
---|
132 | obj.transport = wl_transport_eth_udp_mex; |
---|
133 | end |
---|
134 | |
---|
135 | if(isempty(obj.trigger_manager)) |
---|
136 | obj.wl_setTriggerManager('wl_trigger_manager_proc'); |
---|
137 | end |
---|
138 | |
---|
139 | readKeys = {'network','','host_address',''}; |
---|
140 | IP = inifile(configFile,'read',readKeys); |
---|
141 | IP = IP{1}; |
---|
142 | IP = sscanf(IP,'%d.%d.%d.%d'); |
---|
143 | |
---|
144 | readKeys = {'network','','host_ID',''}; |
---|
145 | hostID = inifile(configFile,'read',readKeys); |
---|
146 | hostID = hostID{1}; |
---|
147 | hostID = sscanf(hostID,'%d'); |
---|
148 | |
---|
149 | readKeys = {'network','','unicast_starting_port',''}; |
---|
150 | unicast_starting_port = inifile(configFile,'read',readKeys); |
---|
151 | unicast_starting_port = unicast_starting_port{1}; |
---|
152 | unicast_starting_port = sscanf(unicast_starting_port,'%d'); |
---|
153 | |
---|
154 | % Configure transport with settings associated with provided ID |
---|
155 | obj.transport.hdr.srcID = hostID; |
---|
156 | obj.transport.hdr.destID = obj.ID; |
---|
157 | |
---|
158 | % Determine IP address based on the input parameter |
---|
159 | switch( class(currID) ) |
---|
160 | case 'struct' |
---|
161 | if ( ~strcmp( currID.ipAddress, '' ) ) |
---|
162 | obj.transport.setAddress(currID.ipAddress); |
---|
163 | else |
---|
164 | error('Unknown argument. IP Address provided is blank'); |
---|
165 | end |
---|
166 | case 'double' |
---|
167 | obj.transport.setAddress(sprintf('%d.%d.%d.%d', IP(1), IP(2), IP(3), (obj.ID + 1))); |
---|
168 | end |
---|
169 | |
---|
170 | obj.transport.setPort(unicast_starting_port); |
---|
171 | |
---|
172 | obj.transport.open(); |
---|
173 | obj.transport.hdr.srcID = hostID; % ????? Redundant ????? - TBD |
---|
174 | obj.transport.hdr.destID = obj.ID; % ????? Redundant ????? - TBD |
---|
175 | |
---|
176 | obj.wl_nodeCmd('initialize'); |
---|
177 | obj.wl_transportCmd('ping'); % Confirm the WARP node is online |
---|
178 | obj.wl_transportCmd('payload_size_test'); % Perform a test to figure out max payload size |
---|
179 | |
---|
180 | obj.interfaceIDs = []; |
---|
181 | |
---|
182 | if(isempty(obj.baseband)) |
---|
183 | % Instantiate baseband object |
---|
184 | obj.wl_setBaseband('wl_baseband_buffers'); |
---|
185 | end |
---|
186 | |
---|
187 | % Read details from the hardware (serial number, etc) and save to local properties |
---|
188 | obj.wl_nodeCmd('get_hardware_info'); |
---|
189 | |
---|
190 | % Instantiate interfaces group. |
---|
191 | if(isempty(obj.interfaceGroups)) |
---|
192 | obj.interfaceGroups{1} = wl_interface_group_X245(1:obj.num_interfaces, 'w3'); |
---|
193 | end |
---|
194 | |
---|
195 | % Extract the interface IDs from the interface group. These IDs |
---|
196 | % will be supplied to user scripts to identify individual |
---|
197 | % interfaces for interface and baseband commands |
---|
198 | for ifcGroupIndex = 1:length(obj.interfaceGroups) |
---|
199 | obj.interfaceIDs = [obj.interfaceIDs, obj.interfaceGroups{1}.ID(:).']; |
---|
200 | end |
---|
201 | |
---|
202 | % Populate the description property with a human-readable |
---|
203 | % description of the node |
---|
204 | obj.description = sprintf('WARP v%d Node - ID %d', obj.hwVer, obj.ID); |
---|
205 | end |
---|
206 | |
---|
207 | |
---|
208 | function wl_setUserExtension(obj,module) |
---|
209 | % Sets the User Extension module to a user-provided object or |
---|
210 | % string of that object's class name. |
---|
211 | % |
---|
212 | makeComparison = 1; |
---|
213 | |
---|
214 | if(module == 0) % No module attached |
---|
215 | module = 'double(0)'; |
---|
216 | makeComparison = 0; |
---|
217 | elseif(~ischar(module)) % Input is an actual instance of an object |
---|
218 | module = class(module); |
---|
219 | end |
---|
220 | |
---|
221 | if(makeComparison && ~any(strcmp(superclasses(module),'wl_user_ext'))) |
---|
222 | error('Module is not a wl_user_ext type'); |
---|
223 | end |
---|
224 | |
---|
225 | for n = 1:length(obj) |
---|
226 | obj(n).user = eval(module); |
---|
227 | end |
---|
228 | end |
---|
229 | |
---|
230 | |
---|
231 | function wl_setBaseband(obj,module) |
---|
232 | % Sets the Baseband module to a user-provided object or |
---|
233 | % string of that object's class name. |
---|
234 | % |
---|
235 | makeComparison = 1; |
---|
236 | |
---|
237 | if(module == 0) % No module attached |
---|
238 | module = 'double(0)'; |
---|
239 | makeComparison = 0; |
---|
240 | elseif(~ischar(module)) % Input is an actual instance of an object |
---|
241 | module = class(module); |
---|
242 | end |
---|
243 | |
---|
244 | if(makeComparison && ~any(strcmp(superclasses(module),'wl_baseband'))) |
---|
245 | error('Module is not a wl_baseband type'); |
---|
246 | end |
---|
247 | |
---|
248 | for n = 1:length(obj) |
---|
249 | obj(n).baseband = eval(module); |
---|
250 | end |
---|
251 | end |
---|
252 | |
---|
253 | |
---|
254 | function wl_setTriggerManager(obj,module) |
---|
255 | % Sets the Trigger Manager module to a user-provided object or |
---|
256 | % string of that object's class name. |
---|
257 | % |
---|
258 | makeComparison = 1; |
---|
259 | |
---|
260 | if(module == 0) % No module attached |
---|
261 | module = 'double(0)'; |
---|
262 | makeComparison = 0; |
---|
263 | elseif(~ischar(module)) % Input is an actual instance of an object |
---|
264 | module = class(module); |
---|
265 | end |
---|
266 | |
---|
267 | if(makeComparison && ~any(strcmp(superclasses(module),'wl_trigger_manager'))) |
---|
268 | error('Module is not a wl_trigger_manager type'); |
---|
269 | end |
---|
270 | |
---|
271 | for n = 1:length(obj) |
---|
272 | obj(n).trigger_manager = eval(module); |
---|
273 | end |
---|
274 | end |
---|
275 | |
---|
276 | |
---|
277 | function out = wl_basebandCmd(obj, varargin) |
---|
278 | % Sends commands to the baseband object. |
---|
279 | % This method is safe to call with multiple wl_node objects as |
---|
280 | % its first argument. For example, let node0 and node1 be |
---|
281 | % wl_node objects: |
---|
282 | % |
---|
283 | % wl_basebandCmd(node0, args ) |
---|
284 | % wl_basebandCmd([node0, node1], args ) |
---|
285 | % node0.wl_basebandCmd( args ) |
---|
286 | % |
---|
287 | % are all valid ways to call this method. Note, when calling |
---|
288 | % this method for multiple node objects, the interpretation of |
---|
289 | % other vectored arguments is left up to the underlying |
---|
290 | % components. |
---|
291 | % |
---|
292 | nodes = obj; |
---|
293 | numNodes = numel(nodes); |
---|
294 | |
---|
295 | for n = numNodes:-1:1 |
---|
296 | currNode = nodes(n); |
---|
297 | if(any(strcmp(superclasses(currNode.baseband),'wl_baseband'))) |
---|
298 | out(n) = currNode.baseband.procCmd(n, currNode, varargin{:}); |
---|
299 | else |
---|
300 | error('Node %d does not have an attached baseband module',currNode.ID); |
---|
301 | end |
---|
302 | end |
---|
303 | |
---|
304 | if((length(out) == 1) && iscell(out)) |
---|
305 | out = out{1}; % Strip away the cell if it's just a single element. |
---|
306 | end |
---|
307 | end |
---|
308 | |
---|
309 | |
---|
310 | function out = wl_nodeCmd(obj, varargin) |
---|
311 | %Sends commands to the node object. |
---|
312 | % This method is safe to call with multiple wl_node objects as |
---|
313 | % its first argument. For example, let node0 and node1 be |
---|
314 | % wl_node objects: |
---|
315 | % |
---|
316 | % wl_nodeCmd(node0, args ) |
---|
317 | % wl_nodeCmd([node0, node1], args ) |
---|
318 | % node0.wl_nodeCmd( args ) |
---|
319 | % |
---|
320 | % are all valid ways to call this method. Note, when calling |
---|
321 | % this method for multiple node objects, the interpretation of |
---|
322 | % other vectored arguments is left up to the underlying |
---|
323 | % components. |
---|
324 | % |
---|
325 | nodes = obj; |
---|
326 | numNodes = numel(nodes); |
---|
327 | |
---|
328 | for n = numNodes:-1:1 |
---|
329 | currNode = nodes(n); |
---|
330 | out(n) = currNode.procCmd(n, currNode, varargin{:}); |
---|
331 | end |
---|
332 | |
---|
333 | if((length(out) == 1) && iscell(out)) |
---|
334 | out = out{1}; % Strip away the cell if it's just a single element. |
---|
335 | end |
---|
336 | end |
---|
337 | |
---|
338 | |
---|
339 | function out = wl_transportCmd(obj, varargin) |
---|
340 | % Sends commands to the transport object. |
---|
341 | % This method is safe to call with multiple wl_node objects as |
---|
342 | % its first argument. For example, let node0 and node1 be |
---|
343 | % wl_node objects: |
---|
344 | % |
---|
345 | % wl_transportCmd(node0, args ) |
---|
346 | % wl_transportCmd([node0, node1], args ) |
---|
347 | % node0.wl_transportCmd( args ) |
---|
348 | % |
---|
349 | % are all valid ways to call this method. Note, when calling |
---|
350 | % this method for multiple node objects, the interpretation of |
---|
351 | % other vectored arguments is left up to the underlying |
---|
352 | % components. |
---|
353 | % |
---|
354 | nodes = obj; |
---|
355 | numNodes = numel(nodes); |
---|
356 | |
---|
357 | for n = numNodes:-1:1 |
---|
358 | currNode = nodes(n); |
---|
359 | if(any(strcmp(superclasses(currNode.transport),'wl_transport'))) |
---|
360 | out(n) = currNode.transport.procCmd(n, currNode, varargin{:}); |
---|
361 | else |
---|
362 | error('Node %d does not have an attached transport module',currNode.ID); |
---|
363 | end |
---|
364 | |
---|
365 | end |
---|
366 | |
---|
367 | if((length(out) == 1) && iscell(out)) |
---|
368 | out = out{1}; % Strip away the cell if it's just a single element. |
---|
369 | end |
---|
370 | end |
---|
371 | |
---|
372 | |
---|
373 | function out = wl_userExtCmd(obj, varargin) |
---|
374 | % Sends commands to the user extension object. |
---|
375 | % This method is safe to call with multiple wl_node objects as |
---|
376 | % its first argument. For example, let node0 and node1 be |
---|
377 | % wl_node objects: |
---|
378 | % |
---|
379 | % wl_userExtCmd(node0, args ) |
---|
380 | % wl_userExtCmd([node0, node1], args ) |
---|
381 | % node0.wl_userExtCmd( args ) |
---|
382 | % |
---|
383 | % are all valid ways to call this method. Note, when calling |
---|
384 | % this method for multiple node objects, the interpretation of |
---|
385 | % other vectored arguments is left up to the underlying |
---|
386 | % components. |
---|
387 | % |
---|
388 | nodes = obj; |
---|
389 | numNodes = numel(nodes); |
---|
390 | |
---|
391 | for n = numNodes:-1:1 |
---|
392 | currNode = nodes(n); |
---|
393 | if(any(strcmp(superclasses(currNode.user),'wl_user_ext'))) |
---|
394 | out(n) = {currNode.user.procCmd(n, currNode, varargin{:})}; |
---|
395 | else |
---|
396 | error('Node %d does not have an attached user extension module',currNode.ID); |
---|
397 | end |
---|
398 | end |
---|
399 | |
---|
400 | if((length(out) == 1) && iscell(out)) |
---|
401 | out = out{1}; % Strip away the cell if it's just a single element. |
---|
402 | end |
---|
403 | end |
---|
404 | |
---|
405 | |
---|
406 | function out = wl_triggerManagerCmd(obj, varargin) |
---|
407 | % Sends commands to the trigger manager object. |
---|
408 | % This method is safe to call with multiple wl_node objects as |
---|
409 | % its first argument. For example, let node0 and node1 be |
---|
410 | % wl_node objects: |
---|
411 | % |
---|
412 | % wl_triggerManagerCmd(node0, args ) |
---|
413 | % wl_triggerManagerCmd([node0, node1], args ) |
---|
414 | % node0.wl_triggerManagerCmd( args ) |
---|
415 | % |
---|
416 | % are all valid ways to call this method. Note, when calling |
---|
417 | % this method for multiple node objects, the interpretation of |
---|
418 | % other vectored arguments is left up to the underlying |
---|
419 | % components. |
---|
420 | % |
---|
421 | nodes = obj; |
---|
422 | numNodes = numel(nodes); |
---|
423 | |
---|
424 | for n = numNodes:-1:1 |
---|
425 | currNode = nodes(n); |
---|
426 | if(any(strcmp(superclasses(currNode.trigger_manager),'wl_trigger_manager'))) |
---|
427 | out(n) = currNode.trigger_manager.procCmd(n, currNode, varargin{:}); |
---|
428 | else |
---|
429 | error('Node %d does not have an attached trigger manager module',currNode.ID); |
---|
430 | end |
---|
431 | end |
---|
432 | |
---|
433 | if((length(out) == 1) && iscell(out)) |
---|
434 | out = out{1}; % Strip away the cell if it's just a single element. |
---|
435 | end |
---|
436 | end |
---|
437 | |
---|
438 | |
---|
439 | function out = wl_interfaceCmd(obj, rfSel, varargin) |
---|
440 | % Sends commands to the interface object. |
---|
441 | % This method must be called with RF selection masks as an |
---|
442 | % argument. These masks are returned by the wl_getInterfaceIDs |
---|
443 | % method. The RFMASKS argument must be a scaler or vector of |
---|
444 | % bit-OR'd interface IDs from a single interface group |
---|
445 | % |
---|
446 | % This method is safe to call with multiple wl_node objects as |
---|
447 | % its first argument. For example, let node0 and node1 be |
---|
448 | % wl_node objects: |
---|
449 | % |
---|
450 | % wl_interfaceCmd(node0, RFMASKS, args ) |
---|
451 | % wl_interfaceCmd([node0, node1], RFMASKS, args ) |
---|
452 | % node0.wl_trigConfCmd(RFMASKS, args ) |
---|
453 | % |
---|
454 | % are all valid ways to call this method. Note, when calling |
---|
455 | % this method for multiple node objects, the interpretation of |
---|
456 | % other vectored arguments is left up to the underlying |
---|
457 | % components. |
---|
458 | % |
---|
459 | nodes = obj; |
---|
460 | numNodes = numel(nodes); |
---|
461 | |
---|
462 | ifcIndex = 1; |
---|
463 | for nodeIndex = numNodes:-1:1 |
---|
464 | currNode = nodes(nodeIndex); |
---|
465 | if(any(strcmp(superclasses(currNode.interfaceGroups{ifcIndex}), 'wl_interface_group'))) |
---|
466 | out(nodeIndex) = currNode.interfaceGroups{ifcIndex}.procCmd(nodeIndex, currNode, rfSel, varargin{:}); |
---|
467 | else |
---|
468 | error('Node %d does not have an attached interface group module',currNode.ID); |
---|
469 | end |
---|
470 | end |
---|
471 | |
---|
472 | if((length(out) == 1) && iscell(out)) |
---|
473 | out = out{1}; % Strip away the cell if it's just a single element. |
---|
474 | end |
---|
475 | end |
---|
476 | |
---|
477 | |
---|
478 | function varargout = wl_getInterfaceIDs(obj) |
---|
479 | % Returns the interfaces IDs that can be used as inputs to all interface commands, some |
---|
480 | % baseband commands and possibly some user extension commands. |
---|
481 | % |
---|
482 | % The interface IDs are returned in a structure that contains fields for individual |
---|
483 | % interfaces and combinations of interfaces. When a node only supports 2 interfaces, |
---|
484 | % the fields for RFC and RFD (ie the fields specific to a 4 interface node) are not |
---|
485 | % present in the structure. |
---|
486 | % |
---|
487 | % The fields in the structure are: |
---|
488 | % - Scalar fields: |
---|
489 | % - RF_A |
---|
490 | % - RF_B |
---|
491 | % - RF_C |
---|
492 | % - RF_D |
---|
493 | % - RF_ON_BOARD NOTE: RF_ON_BOARD = RF_A + RF_B |
---|
494 | % - RF_FMC NOTE: RF_FMC = RF_C + RF_D |
---|
495 | % - RF_ALL NOTE: 2RF: RF_ALL = RF_A + RF_B |
---|
496 | % 4RF: RF_ALL = RF_A + RF_B + RF_C + RF_D |
---|
497 | % - Vector fields: |
---|
498 | % - RF_ON_BOARD_VEC NOTE: RF_ON_BOARD_VEC = [RF_A, RF_B] |
---|
499 | % - RF_FMC_VEC NOTE: RF_FMC_VEC = [RF_C, RF_D] |
---|
500 | % - RF_ALL_VEC NOTE: 2RF: RF_ALL_VEC = [RF_A, RF_B] |
---|
501 | % 4RF: RF_ALL_VEC = [RF_A, RF_B, RF_C, RF_D] |
---|
502 | % |
---|
503 | % NOTE: Due to Matlab behavior, the scalar fields for RF_A, RF_B, RF_C, and RF_D can be |
---|
504 | % used as vectors and therefore do not need separate vector fields in the structure |
---|
505 | % |
---|
506 | % Examples: |
---|
507 | % Get the interface ID structure (let node be a wl_node object): |
---|
508 | % ifc_ids = wl_getInterfaceIDs(node); |
---|
509 | % ifc_ids = node.wl_getInterfaceIDs(); |
---|
510 | % |
---|
511 | % Use the interface ID structure: |
---|
512 | % 1) Enable RF_A for transmit: |
---|
513 | % wl_interfaceCmd(node, ifc_ids.RF_A, 'tx_en'); |
---|
514 | % |
---|
515 | % 2) Get 1000 samples of Read IQ data from all interfaces: |
---|
516 | % rx_IQ = wl_basebandCmd(node, ifc_ids.RF_ALL_VEC, 'read_IQ', 0, 1000); |
---|
517 | % |
---|
518 | |
---|
519 | if (nargout > 1) |
---|
520 | % Legacy code for compatibility |
---|
521 | % |
---|
522 | % Returns a vector of interface IDs that can be used as inputs to all interface |
---|
523 | % commands and some baseband or user extension commands. |
---|
524 | % |
---|
525 | % Let node0 be a wl_node object: |
---|
526 | % [RFA, RFB] = wl_getInterfaceIDs(node0) |
---|
527 | % [RFA, RFB] = node0.wl_getInterfaceIDs(node0) |
---|
528 | % |
---|
529 | % Issues a warning that this syntax will be deprecated in future releases. |
---|
530 | % |
---|
531 | if(nargout > obj.num_interfaces) |
---|
532 | error('Node %d has only %d interfaces. User has requested %d interface IDs', obj.ID, obj.num_interfaces, nargout); |
---|
533 | end |
---|
534 | |
---|
535 | varargout = num2cell(obj.interfaceIDs(1:nargout)); |
---|
536 | |
---|
537 | % Print warning that this syntax will be deprecated |
---|
538 | try |
---|
539 | temp = evalin('base', 'wl_get_interface_ids_did_warn'); |
---|
540 | catch |
---|
541 | fprintf('WARNING: This syntax for wl_getInterfaceIDs() is being deprecated.\n'); |
---|
542 | fprintf('WARNING: Please use: ifc_ids = wl_getInterfaceIDs(node);\n'); |
---|
543 | fprintf('WARNING: where ifc_ids is a structure that contains the interface IDs\n'); |
---|
544 | fprintf('WARNING: See WARPLab documentation for more information\n\n'); |
---|
545 | |
---|
546 | assignin('base', 'wl_get_interface_ids_did_warn', 1) |
---|
547 | end |
---|
548 | |
---|
549 | else |
---|
550 | ifc_ids = struct(); |
---|
551 | |
---|
552 | switch(obj.num_interfaces) |
---|
553 | |
---|
554 | %--------------------------------------------------------- |
---|
555 | case 2 |
---|
556 | % Structure contains: |
---|
557 | % Scalar variables: RF_A, RF_B, RF_ON_BOARD, RF_ALL |
---|
558 | % Vector variables: RF_ON_BOARD_VEC, RF_ALL_VEC |
---|
559 | % |
---|
560 | |
---|
561 | % Scalar variables |
---|
562 | ifc_ids(1).RF_A = obj.interfaceIDs(1); |
---|
563 | ifc_ids(1).RF_B = obj.interfaceIDs(2); |
---|
564 | |
---|
565 | ifc_ids(1).RF_ON_BOARD = obj.interfaceIDs(1) + obj.interfaceIDs(2); |
---|
566 | |
---|
567 | ifc_ids(1).RF_ALL = obj.interfaceIDs(1) + obj.interfaceIDs(2); |
---|
568 | |
---|
569 | % Vector variables |
---|
570 | ifc_ids(1).RF_ON_BOARD_VEC = [obj.interfaceIDs(1), obj.interfaceIDs(2)]; |
---|
571 | |
---|
572 | ifc_ids(1).RF_ALL_VEC = [obj.interfaceIDs(1), obj.interfaceIDs(2)]; |
---|
573 | |
---|
574 | %--------------------------------------------------------- |
---|
575 | case 4 |
---|
576 | % Structure contains: |
---|
577 | % Scalar variables: RF_A, RF_B, RF_C, RF_D, RF_ON_BOARD, RF_FMC, RF_ALL |
---|
578 | % Vector variables: RF_ON_BOARD_VEC, RF_FMC_VEC, RF_ALL_VEC |
---|
579 | % |
---|
580 | |
---|
581 | % Scalar variables |
---|
582 | ifc_ids(1).RF_A = obj.interfaceIDs(1); |
---|
583 | ifc_ids(1).RF_B = obj.interfaceIDs(2); |
---|
584 | ifc_ids(1).RF_C = obj.interfaceIDs(3); |
---|
585 | ifc_ids(1).RF_D = obj.interfaceIDs(4); |
---|
586 | |
---|
587 | ifc_ids(1).RF_ON_BOARD = obj.interfaceIDs(1) + obj.interfaceIDs(2); |
---|
588 | ifc_ids(1).RF_FMC = obj.interfaceIDs(3) + obj.interfaceIDs(4); |
---|
589 | |
---|
590 | ifc_ids(1).RF_ALL = obj.interfaceIDs(1) + obj.interfaceIDs(2) + obj.interfaceIDs(3) + obj.interfaceIDs(4); |
---|
591 | |
---|
592 | % Vector variables |
---|
593 | ifc_ids(1).RF_ON_BOARD_VEC = [obj.interfaceIDs(1), obj.interfaceIDs(2)]; |
---|
594 | ifc_ids(1).RF_FMC_VEC = [obj.interfaceIDs(3), obj.interfaceIDs(4)]; |
---|
595 | |
---|
596 | ifc_ids(1).RF_ALL_VEC = [obj.interfaceIDs(1), obj.interfaceIDs(2), obj.interfaceIDs(3), obj.interfaceIDs(4)]; |
---|
597 | |
---|
598 | %--------------------------------------------------------- |
---|
599 | otherwise |
---|
600 | error( 'Number of interfaces not supported. Node reported %d interfaces, should be in [2, 4].', obj.num_interfaces); |
---|
601 | end |
---|
602 | |
---|
603 | varargout = {ifc_ids}; |
---|
604 | end |
---|
605 | end |
---|
606 | |
---|
607 | |
---|
608 | function varargout = wl_getTriggerInputIDs(obj) |
---|
609 | % Returns the trigger input IDs that can be used as inputs to trigger manager commands |
---|
610 | % |
---|
611 | % The trigger input IDs are returned in a structure that contains fields for individual |
---|
612 | % triggers. |
---|
613 | % |
---|
614 | % The fields in the structure are: |
---|
615 | % - Scalar fields: |
---|
616 | % - ETH_A - Ethernet Port A |
---|
617 | % - ETH_B - Ethernet Port B |
---|
618 | % - ENERGY_DET - Energy detection (See 'energy_config_*' commands in the Trigger Manager documentation) |
---|
619 | % - AGC_DONE - Automatic Gain Controller complete |
---|
620 | % - SW_REG - Software register (ie Memory mapped registers that can be triggered by a CPU write) |
---|
621 | % - EXT_IN_P0 - External Input Pin 0 |
---|
622 | % - EXT_IN_P1 - External Input Pin 1 |
---|
623 | % - EXT_IN_P2 - External Input Pin 2 |
---|
624 | % - EXT_IN_P3 - External Input Pin 3 |
---|
625 | % |
---|
626 | % Examples: |
---|
627 | % Get the trigger input ID structure (let node be a wl_node object): |
---|
628 | % trig_in_ids = wl_getTriggerInputIDs(node); |
---|
629 | % trig_in_ids = node.wl_getTriggerInputIDs(); |
---|
630 | % |
---|
631 | % Use the trigger input ID structure: |
---|
632 | % 1) Enable baseband and agc output triggers to be triggered on the Ethernet A input trigger: |
---|
633 | % wl_triggerManagerCmd(nodes, 'output_config_input_selection', [trig_out_ids.BASEBAND, trig_out_ids.AGC], [trig_in_ids.ETH_A]); |
---|
634 | % |
---|
635 | |
---|
636 | if (nargout > 1) |
---|
637 | % Legacy code for compatibility |
---|
638 | % |
---|
639 | % Returns a vector of trigger input IDs that can be used as inputs to trigger manager commands |
---|
640 | % |
---|
641 | % Let node0 be a wl_node object |
---|
642 | % [T_IN_ETH_A, T_IN_ENERGY, T_IN_AGCDONE, T_IN_REG, T_IN_D0, T_IN_D1, T_IN_D2, T_IN_D3, T_IN_ETH_B] = wl_getTriggerInputIDs(node0) |
---|
643 | % [T_IN_ETH_A, T_IN_ENERGY, T_IN_AGCDONE, T_IN_REG, T_IN_D0, T_IN_D1, T_IN_D2, T_IN_D3, T_IN_ETH_B] = node0.wl_getTriggerInputIDs(node0) |
---|
644 | % |
---|
645 | if(nargout > length(obj.trigger_manager.triggerInputIDs)) |
---|
646 | error('Node %d has only %d trigger inputs. User has requested %d trigger input IDs',obj.ID,length(obj.trigger_manager.triggerInputIDs),nargout); |
---|
647 | end |
---|
648 | |
---|
649 | varargout = num2cell(obj.trigger_manager.triggerInputIDs(1:nargout)); |
---|
650 | |
---|
651 | % Print warning that this syntax will be deprecated |
---|
652 | try |
---|
653 | temp = evalin('base', 'wl_get_trigger_input_ids_did_warn'); |
---|
654 | catch |
---|
655 | fprintf('WARNING: This syntax for wl_getTriggerInputIDs() is being deprecated.\n'); |
---|
656 | fprintf('WARNING: Please use: trig_in_ids = wl_getTriggerInputIDs(node);\n'); |
---|
657 | fprintf('WARNING: where trig_in_ids is a structure that contains the trigger input IDs\n'); |
---|
658 | fprintf('WARNING: See WARPLab documentation for more information\n\n'); |
---|
659 | |
---|
660 | assignin('base', 'wl_get_trigger_input_ids_did_warn', 1) |
---|
661 | end |
---|
662 | |
---|
663 | else |
---|
664 | % Structure contains: |
---|
665 | % Scalar variables: ETH_A, ETH_B, ENERGY, AGC_DONE, SW_REG, DEBUG_0, DEBUG_1, DEBUG_2, DEBUG_3 |
---|
666 | % |
---|
667 | trig_in_ids = struct(); |
---|
668 | |
---|
669 | if(length(obj.trigger_manager.triggerInputIDs) ~= 9) |
---|
670 | error('Node %d has %d trigger inputs. Expected 9.', obj.ID, length(obj.trigger_manager.triggerInputIDs)); |
---|
671 | end |
---|
672 | |
---|
673 | % Scalar variables |
---|
674 | trig_in_ids(1).ETH_A = obj.trigger_manager.triggerInputIDs(1); |
---|
675 | trig_in_ids(1).ENERGY_DET = obj.trigger_manager.triggerInputIDs(2); |
---|
676 | trig_in_ids(1).AGC_DONE = obj.trigger_manager.triggerInputIDs(3); |
---|
677 | trig_in_ids(1).SW_REG = obj.trigger_manager.triggerInputIDs(4); |
---|
678 | trig_in_ids(1).EXT_IN_P0 = obj.trigger_manager.triggerInputIDs(5); |
---|
679 | trig_in_ids(1).EXT_IN_P1 = obj.trigger_manager.triggerInputIDs(6); |
---|
680 | trig_in_ids(1).EXT_IN_P2 = obj.trigger_manager.triggerInputIDs(7); |
---|
681 | trig_in_ids(1).EXT_IN_P3 = obj.trigger_manager.triggerInputIDs(8); |
---|
682 | trig_in_ids(1).ETH_B = obj.trigger_manager.triggerInputIDs(9); |
---|
683 | |
---|
684 | varargout = {trig_in_ids}; |
---|
685 | end |
---|
686 | end |
---|
687 | |
---|
688 | |
---|
689 | function varargout = wl_getTriggerOutputIDs(obj) |
---|
690 | % Returns the trigger output IDs that can be used as inputs to trigger manager commands |
---|
691 | % |
---|
692 | % The trigger output IDs are returned in a structure that contains fields for individual |
---|
693 | % triggers. |
---|
694 | % |
---|
695 | % The fields in the structure are: |
---|
696 | % - Scalar fields: |
---|
697 | % - BASEBAND - Baseband buffers module |
---|
698 | % - AGC - Automatic Gain Controller module |
---|
699 | % - EXT_OUT_P0 - External Output Pin 0 |
---|
700 | % - EXT_OUT_P1 - External Output Pin 1 |
---|
701 | % - EXT_OUT_P2 - External Output Pin 2 |
---|
702 | % - EXT_OUT_P3 - External Output Pin 3 |
---|
703 | % |
---|
704 | % Examples: |
---|
705 | % Get the trigger output ID structure (let node be a wl_node object): |
---|
706 | % trig_out_ids = wl_getTriggerInputIDs(node); |
---|
707 | % trig_out_ids = node.wl_getTriggerInputIDs(); |
---|
708 | % |
---|
709 | % Use the trigger input ID structure: |
---|
710 | % 1) Enable baseband and agc output triggers to be triggered on the Ethernet A input trigger: |
---|
711 | % wl_triggerManagerCmd(node, 'output_config_input_selection', [trig_out_ids.BASEBAND, trig_out_ids.AGC], [trig_in_ids.ETH_A]); |
---|
712 | % |
---|
713 | % 2) Configure output delay for the baseband: |
---|
714 | % node.wl_triggerManagerCmd('output_config_delay', [trig_out_ids.BASEBAND], 0); |
---|
715 | % |
---|
716 | |
---|
717 | if (nargout > 1) |
---|
718 | % Legacy code for compatibility |
---|
719 | % |
---|
720 | % Returns a vector of trigger output IDs that can be used as inputs to trigger manager commands |
---|
721 | % |
---|
722 | % Let node0 be a wl_node object: |
---|
723 | % [T_OUT_BASEBAND, T_OUT_AGC, T_OUT_D0, T_OUT_D1, T_OUT_D2, T_OUT_D3] = wl_getTriggerOutputIDs(node0) |
---|
724 | % [T_OUT_BASEBAND, T_OUT_AGC, T_OUT_D0, T_OUT_D1, T_OUT_D2, T_OUT_D3] = node0.wl_getTriggerOutputIDs(node0) |
---|
725 | % |
---|
726 | if(nargout > length(obj.trigger_manager.triggerOutputIDs)) |
---|
727 | error('Node %d has only %d trigger outputs. User has requested %d trigger output IDs',obj.ID,length(obj.trigger_manager.triggerOutputIDs),nargout); |
---|
728 | end |
---|
729 | |
---|
730 | varargout = num2cell(obj.trigger_manager.triggerOutputIDs(1:nargout)); |
---|
731 | |
---|
732 | % Print warning that this syntax will be deprecated |
---|
733 | try |
---|
734 | temp = evalin('base', 'wl_get_trigger_output_ids_did_warn'); |
---|
735 | catch |
---|
736 | fprintf('WARNING: This syntax for wl_getTriggerOutputIDs() is being deprecated.\n'); |
---|
737 | fprintf('WARNING: Please use: trig_out_ids = wl_getTriggerOutputIDs(node);\n'); |
---|
738 | fprintf('WARNING: where trig_out_ids is a structure that contains the trigger output IDs\n'); |
---|
739 | fprintf('WARNING: See WARPLab documentation for more information\n\n'); |
---|
740 | |
---|
741 | assignin('base', 'wl_get_trigger_output_ids_did_warn', 1) |
---|
742 | end |
---|
743 | |
---|
744 | else |
---|
745 | % Structure contains: |
---|
746 | % Scalar variables: BASEBAND, AGC, DEBUG_0, DEBUG_1, DEBUG_2, DEBUG_3 |
---|
747 | % |
---|
748 | trig_out_ids = struct(); |
---|
749 | |
---|
750 | if(length(obj.trigger_manager.triggerOutputIDs) ~= 6) |
---|
751 | error('Node %d has %d trigger outputs. Expected 6.', obj.ID, length(obj.trigger_manager.triggerOutputIDs)); |
---|
752 | end |
---|
753 | |
---|
754 | % Scalar variables |
---|
755 | trig_out_ids(1).BASEBAND = obj.trigger_manager.triggerOutputIDs(1); |
---|
756 | trig_out_ids(1).AGC = obj.trigger_manager.triggerOutputIDs(2); |
---|
757 | trig_out_ids(1).EXT_OUT_P0 = obj.trigger_manager.triggerOutputIDs(3); |
---|
758 | trig_out_ids(1).EXT_OUT_P1 = obj.trigger_manager.triggerOutputIDs(4); |
---|
759 | trig_out_ids(1).EXT_OUT_P2 = obj.trigger_manager.triggerOutputIDs(5); |
---|
760 | trig_out_ids(1).EXT_OUT_P3 = obj.trigger_manager.triggerOutputIDs(6); |
---|
761 | |
---|
762 | varargout = {trig_out_ids}; |
---|
763 | end |
---|
764 | end |
---|
765 | |
---|
766 | |
---|
767 | function verify_writeIQ_checksum(obj, checksum) |
---|
768 | % This is a callback to verify the WriteIQ checksum in the case that WriteIQ is called by a broadcast |
---|
769 | % transport. |
---|
770 | if ( numel(checksum) > 1 ) |
---|
771 | error('Cannot verify more than one checksum at a time.') |
---|
772 | end |
---|
773 | |
---|
774 | % Get the current checksum on the node |
---|
775 | node_checksum = obj.wl_basebandCmd('write_iq_checksum'); |
---|
776 | |
---|
777 | if ( node_checksum ~= checksum ) |
---|
778 | warning('Checksums do not match on node %d: %d != %d', obj.ID, node_checksum, checksum) |
---|
779 | end |
---|
780 | end |
---|
781 | |
---|
782 | |
---|
783 | function delete(obj) |
---|
784 | % Clears the transport object to close any open socket |
---|
785 | % connections in the event that the node object is deleted. |
---|
786 | if(~isempty(obj.transport)) |
---|
787 | obj.transport.delete(); |
---|
788 | obj.transport = []; |
---|
789 | end |
---|
790 | end |
---|
791 | end |
---|
792 | |
---|
793 | |
---|
794 | methods(Static=true,Hidden=true) |
---|
795 | function command_out = calcCmd_helper(group,command) |
---|
796 | % Performs the actual command calculation for calcCmd |
---|
797 | command_out = uint32(bitor(bitshift(group,24),command)); |
---|
798 | end |
---|
799 | end |
---|
800 | |
---|
801 | |
---|
802 | methods(Hidden=true) |
---|
803 | % These methods are hidden because users are not intended to call |
---|
804 | % them directly from their WARPLab scripts. |
---|
805 | |
---|
806 | function out = calcCmd(obj, grp, cmdID) |
---|
807 | % Takes a group ID and a cmd ID to form a single |
---|
808 | % uint32 command. Every WARPLab module calls this method |
---|
809 | % to construct their outgoing commands. |
---|
810 | switch(lower(grp)) |
---|
811 | case 'node' |
---|
812 | out = obj.calcCmd_helper(obj.GRPID_NODE, cmdID); |
---|
813 | case 'interface' |
---|
814 | out = obj.calcCmd_helper(obj.GRPID_IFC, cmdID); |
---|
815 | case 'baseband' |
---|
816 | out = obj.calcCmd_helper(obj.GRPID_BB, cmdID); |
---|
817 | case 'trigger_manager' |
---|
818 | out = obj.calcCmd_helper(obj.GRPID_TRIGMNGR, cmdID); |
---|
819 | case 'transport' |
---|
820 | out = obj.calcCmd_helper(obj.GRPID_TRANS, cmdID); |
---|
821 | case 'user_extension' |
---|
822 | out = obj.calcCmd_helper(obj.GRPID_USER, cmdID); |
---|
823 | end |
---|
824 | end |
---|
825 | |
---|
826 | |
---|
827 | function out = procCmd(obj, nodeInd, node, cmdStr, varargin) |
---|
828 | % wl_node procCmd(obj, nodeInd, node, varargin) |
---|
829 | % obj: Node object (when called using dot notation) |
---|
830 | % nodeInd: Index of the current node, when wl_node is iterating over nodes |
---|
831 | % node: Current node object |
---|
832 | % cmdStr: Command string of the interface command |
---|
833 | % varargin: |
---|
834 | % [1:N} Command arguments |
---|
835 | % |
---|
836 | out = []; |
---|
837 | |
---|
838 | cmdStr = lower(cmdStr); |
---|
839 | switch(cmdStr) |
---|
840 | |
---|
841 | %--------------------------------------------------------- |
---|
842 | case 'get_hardware_info' |
---|
843 | % Reads details from the WARP hardware and updates node object parameters |
---|
844 | % |
---|
845 | % Arguments: none |
---|
846 | % Returns: none (access updated node parameters if needed) |
---|
847 | % |
---|
848 | % Hardware support: |
---|
849 | % All: |
---|
850 | % WARPLab design version |
---|
851 | % Hardware version |
---|
852 | % Ethernet MAC Address |
---|
853 | % Number of Interface Groups |
---|
854 | % Number of Interfaces |
---|
855 | % |
---|
856 | % WARP v3: |
---|
857 | % Serial number |
---|
858 | % Virtex-6 FPGA DNA |
---|
859 | % |
---|
860 | [MAJOR, MINOR, REVISION, XTRA] = wl_ver(); |
---|
861 | |
---|
862 | myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_INFO)); |
---|
863 | resp = node.sendCmd(myCmd); |
---|
864 | resp = resp.getArgs(); |
---|
865 | |
---|
866 | % Response payload (all u32): |
---|
867 | % 1: Serial number |
---|
868 | % 2: FPGA DNA MSB |
---|
869 | % 3: FPGA DNA LSB |
---|
870 | % 4: MAC address bytes 5:4 |
---|
871 | % 5: MAC address bytes 3:0 |
---|
872 | % 6: [hw_version wl_ver_major wl_ver_minor wl_ver_rev] |
---|
873 | % 7: Current txIQ Buffer Length |
---|
874 | % 8: Current rxIQ Buffer Length |
---|
875 | % 9: Maximum txIQ Buffer Length |
---|
876 | % 10: Maximum rxIQ Buffer Length |
---|
877 | % 11: [trigger manager coreID, trigger manager numOutputs, trigger manager numInputs] |
---|
878 | % 12: number of interface groups |
---|
879 | % 13:N: interface group descriptions (one u32 per group) |
---|
880 | |
---|
881 | % If the serial number was provided via the network setup, then check the serial number against the HW |
---|
882 | if ( ~isempty( obj.serialNumber ) ) |
---|
883 | if ( ~eq( obj.serialNumber, resp(1) ) ) |
---|
884 | error('Serial Number provided in config, W3-a-%d, does not match HW serial number W3-a-%d ', obj.serialNumber, resp(1)) |
---|
885 | end |
---|
886 | else |
---|
887 | obj.serialNumber = resp(1); |
---|
888 | end |
---|
889 | |
---|
890 | obj.fpgaDNA = bitshift(resp(2), 32) + resp(3); |
---|
891 | |
---|
892 | obj.eth_MAC_addr = 2^32*double(bitand(resp(4),2^16-1)) + double(resp(5)); |
---|
893 | |
---|
894 | obj.hwVer = double(bitand(bitshift(resp(6), -24), 255)); |
---|
895 | obj.wlVer_major = double(bitand(bitshift(resp(6), -16), 255)); |
---|
896 | obj.wlVer_minor = double(bitand(bitshift(resp(6), -8), 255)); |
---|
897 | obj.wlVer_revision = double(bitand(resp(6), 255)); |
---|
898 | |
---|
899 | if((obj.wlVer_major ~= MAJOR) || (obj.wlVer_minor ~= MINOR)) |
---|
900 | myErrorMsg = sprintf('Node %d reports WARPLab version %d.%d.%d while this PC is configured with %d.%d.%d', ... |
---|
901 | obj.ID, obj.wlVer_major, obj.wlVer_minor, obj.wlVer_revision, MAJOR, MINOR, REVISION); |
---|
902 | error(myErrorMsg); |
---|
903 | end |
---|
904 | |
---|
905 | if(obj.wlVer_revision ~= REVISION) |
---|
906 | myWarningMsg = sprintf('Node %d reports WARPLab version %d.%d.%d while this PC is configured with %d.%d.%d', ... |
---|
907 | obj.ID, obj.wlVer_major, obj.wlVer_minor, obj.wlVer_revision, MAJOR, MINOR, REVISION); |
---|
908 | warning(myWarningMsg); |
---|
909 | end |
---|
910 | |
---|
911 | % Get the maximum supported IQ lengths |
---|
912 | % obj.baseband.max_txIQLen = double(resp(7)); |
---|
913 | % obj.baseband.max_rxIQLen = double(resp(8)); |
---|
914 | % obj.baseband.max_rxRSSILen = (obj.baseband.max_rxIQLen) / 4; % RSSI is sampled at 1/4 the speed of IQ |
---|
915 | |
---|
916 | % Get the current supported IQ lengths |
---|
917 | obj.baseband.txIQLen = double(resp(9)); |
---|
918 | obj.baseband.rxIQLen = double(resp(10)); |
---|
919 | obj.baseband.rxRSSILen = double(resp(10))/4; |
---|
920 | |
---|
921 | % Trigger Manager -- core runs at different speed depending on HW version. |
---|
922 | obj.trigger_manager.setNumInputs(double(bitand(resp(11),255))); |
---|
923 | obj.trigger_manager.setNumOutputs(double(bitand(bitshift(resp(11),-8),255))); |
---|
924 | obj.trigger_manager.coreVersion = double(bitand(bitshift(resp(11),-16),255)); |
---|
925 | |
---|
926 | switch(obj.hwVer) |
---|
927 | %TODO: These parameters should be passed up from |
---|
928 | %the board |
---|
929 | case 1 |
---|
930 | error('WARP v1 Hardware is not supported by WARPLab 7'); |
---|
931 | case {2, 3} |
---|
932 | % Clock frequency of the sysgen core in the design |
---|
933 | % NOTE: In WARPLab 7.5.1, the WARP v2 sysgen clock was increased to 160 MHz |
---|
934 | % |
---|
935 | clock_freq = 160e6; |
---|
936 | trig_in_ids = obj.wl_getTriggerInputIDs(); |
---|
937 | trig_out_ids = obj.wl_getTriggerOutputIDs(); |
---|
938 | |
---|
939 | % Trigger output delays |
---|
940 | for k = length(obj.trigger_manager.triggerOutputIDs):-1:1 |
---|
941 | % With Trigger Processor v1.07.a, all delays were increased to 65535 |
---|
942 | obj.trigger_manager.output_delayStep_ns(k) = (1/(clock_freq))*1e9; |
---|
943 | obj.trigger_manager.output_delayMax_ns(k) = 65535 * obj.trigger_manager.output_delayStep_ns(k); |
---|
944 | end |
---|
945 | |
---|
946 | % Trigger input delays |
---|
947 | for k = length(obj.trigger_manager.triggerInputIDs):-1:1 |
---|
948 | obj.trigger_manager.input_delayStep_ns(k) = (1/(clock_freq))*1e9; |
---|
949 | obj.trigger_manager.input_delayMax_ns(k) = 31 * obj.trigger_manager.input_delayStep_ns(k); |
---|
950 | end |
---|
951 | end |
---|
952 | |
---|
953 | obj.num_interfacesGroups = resp(12); |
---|
954 | obj.num_interfaces = resp(13); |
---|
955 | |
---|
956 | %% TODO - parse each interface descriptor and create interface group objects |
---|
957 | |
---|
958 | %--------------------------------------------------------- |
---|
959 | case 'get_fpga_temperature' |
---|
960 | % Reads the temperature (in Celsius) from the FPGA |
---|
961 | % |
---|
962 | % Arguments: none |
---|
963 | % Returns: (double currTemp), (double minTemp), (double maxTemp) |
---|
964 | % currTemp - current temperature of FPGA in degrees Celsius |
---|
965 | % minTemp - minimum recorded temperature of FPGA in degrees Celsius |
---|
966 | % maxTemp - maximum recorded temperature temperature of FPGA in degrees Celsius |
---|
967 | % |
---|
968 | myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_TEMPERATURE)); |
---|
969 | resp = node.sendCmd(myCmd); |
---|
970 | resp = resp.getArgs(); |
---|
971 | |
---|
972 | % Convert from raw temperature to Celsius |
---|
973 | out = ((double(resp)/65536.0)/0.00198421639) - 273.15; |
---|
974 | |
---|
975 | %--------------------------------------------------------- |
---|
976 | case 'initialize' |
---|
977 | % Initializes the node; this must be called at least once per power cycle of the node |
---|
978 | % |
---|
979 | % Arguments: none |
---|
980 | % Returns: none |
---|
981 | % |
---|
982 | myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_INITIALIZE)); |
---|
983 | node.sendCmd(myCmd); |
---|
984 | |
---|
985 | %--------------------------------------------------------- |
---|
986 | case 'identify' |
---|
987 | % Blinks the user LEDs on the WARP node, to help identify MATLAB node-to-hardware node mapping |
---|
988 | % |
---|
989 | % Arguments: none |
---|
990 | % Returns: none |
---|
991 | % |
---|
992 | myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_IDENTIFY)); |
---|
993 | node.sendCmd(myCmd); |
---|
994 | |
---|
995 | %--------------------------------------------------------- |
---|
996 | case 'node_config_reset' |
---|
997 | % Resets the HW state of the node to accept a new node configuration |
---|
998 | % |
---|
999 | % Arguments: none |
---|
1000 | % Returns: none |
---|
1001 | % |
---|
1002 | myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_NODE_CONFIG_RESET)); |
---|
1003 | myCmd.addArgs(uint32(node.serialNumber)); |
---|
1004 | node.sendCmd(myCmd); |
---|
1005 | |
---|
1006 | %--------------------------------------------------------- |
---|
1007 | % 'node_config_setup' is only implemented in wl_initNodes.m |
---|
1008 | % |
---|
1009 | |
---|
1010 | %--------------------------------------------------------- |
---|
1011 | case 'node_mem_read' |
---|
1012 | % Read memory addresses in the node |
---|
1013 | % |
---|
1014 | % Arguments: (uint32 ADDRESS), (uint32 LENGTH) |
---|
1015 | % Returns: Vector of uint32 values read from the node |
---|
1016 | % |
---|
1017 | % ADDRESS: Address to start the read |
---|
1018 | % |
---|
1019 | % LENGTH: Number of uint32 words to read from the node |
---|
1020 | % |
---|
1021 | % NOTE: The node enforces a maximum number of words that can be transferred for a |
---|
1022 | % given read. This is typically on the order of 350 words. |
---|
1023 | % |
---|
1024 | % NOTE: Please use the C code files, such as xparameters.h and other header files, |
---|
1025 | % to understand the addresses of various registers in the system and the meaning |
---|
1026 | % of the bits within those registers. |
---|
1027 | % |
---|
1028 | myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_MEM_RW)); |
---|
1029 | |
---|
1030 | % Check arguments |
---|
1031 | if(length(varargin) ~= 2) |
---|
1032 | error('%s: Requires two arguments: Address, Length (in uint32 words of the read)', cmdStr); |
---|
1033 | end |
---|
1034 | |
---|
1035 | address = varargin{1}; |
---|
1036 | read_length = varargin{2}; |
---|
1037 | |
---|
1038 | myCmd.addArgs(myCmd.CMD_PARAM_READ_VAL); |
---|
1039 | |
---|
1040 | % Check arguments |
---|
1041 | if ((address < 0) || (address > hex2dec('FFFFFFFF'))) |
---|
1042 | error('%s: Address must be in [0, 0xFFFFFFFF].\n', cmdStr); |
---|
1043 | end |
---|
1044 | |
---|
1045 | if ((read_length < 0) || (read_length > 350)) |
---|
1046 | error('%s: Length must be in [0, 350] words.\n', cmdStr); |
---|
1047 | end |
---|
1048 | |
---|
1049 | % Send command to the node |
---|
1050 | myCmd.addArgs(address); |
---|
1051 | myCmd.addArgs(read_length); |
---|
1052 | |
---|
1053 | resp = node.sendCmd(myCmd); |
---|
1054 | |
---|
1055 | % Process response from the node. Return arguments: |
---|
1056 | % [1] - Status |
---|
1057 | % [2] - Length |
---|
1058 | % [ 3: N] - Values |
---|
1059 | % |
---|
1060 | for i = 1:numel(resp) % Needed for unicast node_group support |
---|
1061 | ret = resp(i).getArgs(); |
---|
1062 | |
---|
1063 | if (ret(1) == myCmd.CMD_PARAM_SUCCESS) |
---|
1064 | if ((ret(2) + 2) == length(ret)) |
---|
1065 | if (i == 1) |
---|
1066 | out = ret(3:end); |
---|
1067 | else |
---|
1068 | out(:,i) = ret(3:end); |
---|
1069 | end |
---|
1070 | else |
---|
1071 | msg = sprintf('%s: Memory read length mismatch error in node %d: %d != %d\n', cmdStr, nodeInd, (ret(2) + 2), length(ret)); |
---|
1072 | error(msg); |
---|
1073 | end |
---|
1074 | else |
---|
1075 | msg = sprintf('%s: Memory read error in node %d.\n', cmdStr, nodeInd); |
---|
1076 | error(msg); |
---|
1077 | end |
---|
1078 | end |
---|
1079 | |
---|
1080 | %--------------------------------------------------------- |
---|
1081 | case 'node_mem_write' |
---|
1082 | % Write memory addresses in the node |
---|
1083 | % |
---|
1084 | % Arguments: (uint32 ADDRESS), (uint32 VALUES) |
---|
1085 | % Returns: none |
---|
1086 | % |
---|
1087 | % ADDRESS: Address to start the write |
---|
1088 | % |
---|
1089 | % VALUES: Vector of uint32 words to write to the node |
---|
1090 | % |
---|
1091 | % NOTE: The node enforces a maximum number of words that can be transferred for a |
---|
1092 | % given write. This is typically on the order of 350 words. |
---|
1093 | % |
---|
1094 | % NOTE: Please use the C code files, such as xparameters.h and other header files, |
---|
1095 | % to understand the addresses of various registers in the system and the meaning |
---|
1096 | % of the bits within those registers. |
---|
1097 | % |
---|
1098 | myCmd = wl_cmd(node.calcCmd(obj.GRP, obj.CMD_MEM_RW)); |
---|
1099 | |
---|
1100 | % Check arguments |
---|
1101 | if(length(varargin) ~= 2) |
---|
1102 | error('%s: Requires two arguments: Address, Vector of uint32 words to write', cmdStr); |
---|
1103 | end |
---|
1104 | |
---|
1105 | address = varargin{1}; |
---|
1106 | values = varargin{2}; |
---|
1107 | |
---|
1108 | myCmd.addArgs(myCmd.CMD_PARAM_WRITE_VAL); |
---|
1109 | |
---|
1110 | % Check arguments |
---|
1111 | if ((address < 0) || (address > hex2dec('FFFFFFFF'))) |
---|
1112 | error('%s: Address must be in [0, 0xFFFFFFFF].\n', cmdStr); |
---|
1113 | end |
---|
1114 | |
---|
1115 | if ((length(values) < 0) || (length(values) > 350)) |
---|
1116 | error('%s: Length of VALUES vector must be in [0, 350] words.\n', cmdStr); |
---|
1117 | end |
---|
1118 | |
---|
1119 | % Send command to the node |
---|
1120 | if(~isempty(values)) |
---|
1121 | myCmd.addArgs(address); |
---|
1122 | myCmd.addArgs(length(values)); |
---|
1123 | myCmd.addArgs(values); |
---|
1124 | |
---|
1125 | resp = node.sendCmd(myCmd); |
---|
1126 | |
---|
1127 | % Process response from the node. Return arguments: |
---|
1128 | % [1] - Status |
---|
1129 | % |
---|
1130 | for i = 1:numel(resp) % Needed for unicast node_group support |
---|
1131 | ret = resp(i).getArgs(); |
---|
1132 | |
---|
1133 | if (ret(1) ~= myCmd.CMD_PARAM_SUCCESS) |
---|
1134 | msg = sprintf('%s: Memory write error in node %d.\n', cmdStr, nodeInd); |
---|
1135 | error(msg); |
---|
1136 | end |
---|
1137 | end |
---|
1138 | end |
---|
1139 | |
---|
1140 | %--------------------------------------------------------- |
---|
1141 | otherwise |
---|
1142 | error( 'unknown node command %s', cmdStr); |
---|
1143 | end |
---|
1144 | |
---|
1145 | if((iscell(out) == 0) && (numel(out) ~= 1)) |
---|
1146 | out = {out}; |
---|
1147 | end |
---|
1148 | end |
---|
1149 | |
---|
1150 | |
---|
1151 | function out = sendCmd(obj, cmd) |
---|
1152 | % This method is responsible for serializing the command |
---|
1153 | % objects provided by each of the components of WARPLab into a |
---|
1154 | % vector of values that the transport object can send. This |
---|
1155 | % method is used when the board must at least provide a |
---|
1156 | % transport-level acknowledgement. If the response has actual |
---|
1157 | % payload that needs to be provided back to the calling method, |
---|
1158 | % that response is passed as an output of this method. |
---|
1159 | % |
---|
1160 | resp = obj.transport.send(cmd.serialize(), true); |
---|
1161 | |
---|
1162 | out = wl_resp(resp); |
---|
1163 | end |
---|
1164 | |
---|
1165 | |
---|
1166 | function sendCmd_noresp(obj, cmd) |
---|
1167 | % This method is responsible for serializing the command |
---|
1168 | % objects provided by each of the components of WARPLab into a |
---|
1169 | % vector of values that the transport object can send. This |
---|
1170 | % method is used when a board should not send an immediate |
---|
1171 | % response. The transport object is non-blocking -- it will send |
---|
1172 | % the command and then immediately return. |
---|
1173 | % |
---|
1174 | obj.transport.send(cmd.serialize(), false); |
---|
1175 | end |
---|
1176 | |
---|
1177 | |
---|
1178 | function out = receiveResp(obj) |
---|
1179 | % This method will return a vector of responses that are |
---|
1180 | % sitting in the host's receive queue. It will empty the queue |
---|
1181 | % and return them all to the calling method. |
---|
1182 | % |
---|
1183 | resp = obj.transport.receive(); |
---|
1184 | |
---|
1185 | if(~isempty(resp)) |
---|
1186 | % Create vector of response objects if received string of bytes is a concatenation of many responses |
---|
1187 | done = false; |
---|
1188 | index = 1; |
---|
1189 | resp_index = 1; |
---|
1190 | while ~done |
---|
1191 | out(index) = wl_resp(resp(resp_index:end)); |
---|
1192 | currRespLen = out(index).len(); |
---|
1193 | |
---|
1194 | resp_index = resp_index + currRespLen; |
---|
1195 | index = index + 1; |
---|
1196 | |
---|
1197 | if(resp_index >= length(resp)) |
---|
1198 | done = true; |
---|
1199 | end |
---|
1200 | end |
---|
1201 | else |
---|
1202 | out = []; |
---|
1203 | end |
---|
1204 | end |
---|
1205 | |
---|
1206 | |
---|
1207 | function out = repr(obj) |
---|
1208 | % Return a string representation of the wl_node |
---|
1209 | out = cell(1,length(obj)); |
---|
1210 | |
---|
1211 | for n = 1:length(obj) |
---|
1212 | currObj = obj(n); |
---|
1213 | |
---|
1214 | if(isempty(currObj.ID)) |
---|
1215 | out(n) = {'Node has not been initialized'}; |
---|
1216 | else |
---|
1217 | ID = sprintf('%d', currObj.ID); |
---|
1218 | |
---|
1219 | if(currObj.hwVer == 3) |
---|
1220 | SN = sprintf('W3-a-%05d',currObj.serialNumber); |
---|
1221 | else |
---|
1222 | SN = 'N/A'; |
---|
1223 | end |
---|
1224 | |
---|
1225 | out(n) = {sprintf('%12s (ID = %3s)', SN, ID)}; |
---|
1226 | end |
---|
1227 | end |
---|
1228 | |
---|
1229 | if (length(obj) == 1) |
---|
1230 | out = out{1}; |
---|
1231 | end |
---|
1232 | end |
---|
1233 | |
---|
1234 | |
---|
1235 | function disp(obj) |
---|
1236 | % This is a "pretty print" method to summarize details about wl_node |
---|
1237 | % objects and print them in a table on Matlab's command line. |
---|
1238 | % |
---|
1239 | hasUserExt = 0; |
---|
1240 | strLen = 0; |
---|
1241 | |
---|
1242 | for n = 1:length(obj) |
---|
1243 | currObj = obj(n); |
---|
1244 | hasUserExt = hasUserExt || ~isempty(currObj.user); |
---|
1245 | |
---|
1246 | if(~isempty(currObj.user)) |
---|
1247 | strLen = max(strLen,length(class(currObj.user)) + 3); % +3 is for surrounding spaces and the | |
---|
1248 | end |
---|
1249 | end |
---|
1250 | |
---|
1251 | extraTitle = ''; |
---|
1252 | extraLine = ''; |
---|
1253 | extraArgs = ''; |
---|
1254 | |
---|
1255 | if(hasUserExt) |
---|
1256 | extraTitle = repmat(' ', 1, strLen-1); |
---|
1257 | extraLine = repmat('-', 1, strLen-1); |
---|
1258 | extraTitle = [extraTitle, '|']; |
---|
1259 | extraLine = [extraLine, '-']; |
---|
1260 | |
---|
1261 | newTitle = 'User Ext.'; |
---|
1262 | extraTitle((strLen - length(newTitle) - 1):(end - 2)) = newTitle; |
---|
1263 | end |
---|
1264 | fprintf('Displaying properties of %d wl_node objects:\n',length(obj)); |
---|
1265 | fprintf('| ID | WLVER | HWVER | Serial # | Ethernet MAC Addr | Address | %s\n', extraTitle) |
---|
1266 | fprintf('-------------------------------------------------------------------------------%s\n', extraLine) |
---|
1267 | for n = 1:length(obj) |
---|
1268 | currObj = obj(n); |
---|
1269 | |
---|
1270 | if(~isempty(currObj.user)) |
---|
1271 | myFormat = sprintf('%%%ds |',strLen-2); |
---|
1272 | extraArgs = sprintf(myFormat,class(currObj.user)); |
---|
1273 | elseif(hasUserExt) |
---|
1274 | extraArgs = extraLine; |
---|
1275 | extraArgs(end) = '|'; |
---|
1276 | end |
---|
1277 | |
---|
1278 | if(isempty(currObj.ID)) |
---|
1279 | fprintf('| N/A Node object has not been initialized |%s\n', extraArgs) |
---|
1280 | else |
---|
1281 | ID = sprintf('%d', currObj.ID); |
---|
1282 | WLVER = sprintf('%d.%d.%d', currObj.wlVer_major, currObj.wlVer_minor, currObj.wlVer_revision); |
---|
1283 | HWVER = sprintf('%d', currObj.hwVer); |
---|
1284 | |
---|
1285 | if(currObj.hwVer == 3) |
---|
1286 | SN = sprintf('W3-a-%05d',currObj.serialNumber); |
---|
1287 | else |
---|
1288 | SN = 'N/A'; |
---|
1289 | end |
---|
1290 | |
---|
1291 | temp = dec2hex(uint64(currObj.eth_MAC_addr),12); |
---|
1292 | MACADDR = sprintf('%2s-%2s-%2s-%2s-%2s-%2s',... |
---|
1293 | temp(1:2),temp(3:4),temp(5:6),temp(7:8),temp(9:10),temp(11:12)); |
---|
1294 | |
---|
1295 | if ( ~isempty( currObj.transport ) & ~isempty( currObj.transport.getAddress() ) ) |
---|
1296 | ADDR = currObj.transport.getAddress(); |
---|
1297 | else |
---|
1298 | ADDR = ''; |
---|
1299 | end |
---|
1300 | |
---|
1301 | fprintf('|%4s |%7s |%7s |%12s |%19s |%17s |%s\n', ID, WLVER, HWVER, SN, MACADDR, ADDR, extraArgs); |
---|
1302 | |
---|
1303 | end |
---|
1304 | fprintf('-------------------------------------------------------------------------------%s\n', extraLine) |
---|
1305 | end |
---|
1306 | end |
---|
1307 | end % end methods(Hidden) |
---|
1308 | end % end classdef |
---|