LLC2_API
|
00001 /* ------------------------------------------------------------------------- */ 00002 /* file ll2.h */ 00003 /* ------------------------------------------------------------------------- */ 00004 /* Copyright (C) 2011 Peter Milne, D-TACQ Solutions Ltd 00005 * <Peter dot Milne at D hyphen TACQ dot com> 00006 * Created on: Sep 14, 2011 00007 * Author: pgm 00008 00009 http://www.d-tacq.com 00010 00011 This program is free software; you can redistribute it and/or modify 00012 it under the terms of Version 2 of the GNU General Public License 00013 as published by the Free Software Foundation; 00014 00015 This program is distributed in the hope that it will be useful, 00016 but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 GNU General Public License for more details. 00019 00020 You should have received a copy of the GNU General Public License 00021 along with this program; if not, write to the Free Software 00022 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ 00023 /* ------------------------------------------------------------------------- */ 00024 00025 /** @file ll2.h API definition for LOWLATENCY2. 00026 * 00027 */ 00028 00029 /** 00030 00031 @mainpage llcontrol2 00032 00033 @author Peter Milne <peter.milne@d-tacq.com> 00034 00035 - Low Latency control operation: 00036 - Target push data transfer to slave memory on host card. 00037 - Data is delivered with minimum latency after the sample clock. 00038 00039 - This implementation uses the same firmware and mechanism as the original 00040 LLCONTROL, but it's implemented as an API rather than a framework. 00041 In other words, USER code calls the API rather than the FRAMEWORK call USER code 00042 as before. 00043 00044 00045 - The API is implemented in C++. This allows the same efficient interface to the 00046 device driver as before, while at the same time making for a clean dynamic system instantiation. 00047 - The API is callable from C and, appropriately packaged from another math environment eg Python or IDL. 00048 - In particular, the API allows the user to work with a single array of each 00049 of AI, AO, DI, DO across multiple cards, and the complexity of memory buffering 00050 is completely hidden by the API. 00051 - The user defined the system using a simple config_file (xml). 00052 -The system generates an xml representation of itself. This should be used by 00053 higher level code to establish position of channels in the data buffers. The channel position does not change 00054 unless the configuration changes, so it is reasonables to code this as a constant. 00055 - Alternatively the system can be instantiated in a few lines of C++, with direct access methods for the channel offsets. 00056 00057 - Implementing a control loop in C: 00058 -# Define the system cards and slots and operating parameters by calling RtSetup(); 00059 -# Arm the system by calling RtArm() 00060 -# Run the control loop calling RtIO() 00061 -# Terminate RtStop(); 00062 00063 - Implementing a control loop in C++ 00064 -# Define the system cards and slots, by creating an instance of LL_ControlSystem 00065 and populating it with ACQ_Cards. 00066 -# Define operating parameters using LL_ControlSystem::init(); 00067 -# Set up IO buffers 00068 -# Call LL_ControlSystem::Arm() 00069 -# Run the control loop calling LL_ControlSystem::IO() 00070 -# Terminate with LL_ControlSystem::Stop(); 00071 00072 - For overview, please refer to 00073 - http://www.d-tacq.com/resources/D-Tacq_2G_UserGuide.pdf 00074 00075 - Operation still relies on capture setup scripts as before, or the equivalent. 00076 - D-TACQ examples still use the same automated test environment of 00077 - MB : ACQ196 Master board 00078 - SB : optional ACQ196Slave board[s] 00079 - Sx : optional AO32 slave board[s] 00080 - CB : Clock Board - a further ACQ196 to provide external clock and trigger 00081 00082 - D-TACQ recommends example-dynamic.cpp, this has complete system definition in xml and is therefore the most general example. 00083 - To run example-dynamic from bash prompt. Assume system definition "xml/example1-krypton.xml": 00084 - lookup_xml exports a few environment variables used in scripting.. 00085 @verbatim 00086 ./xml_lookup xml/example1-krypton.xml system/exports 00087 CB=2 S1=3 MB=4 00088 export $(./xml_lookup xml/example1-krypton.xml system/exports) 00089 @endverbatim 00090 - Connect AO32 S1 AO0132 connector to ACQ196 MB AI0132 input 00091 - Connect AO32 S1 DO0132 connector to ACQ196 MB AI3364 input 00092 - Connect AO32 S1 DO3364 connector to ACQ196 MB AI6596 input 00093 - Host-side ACQ200 and AO32 drivers must be loaded. 00094 00095 - Validate driver load: 00096 @verbatim 00097 acqcmd -s MB hostname 00098 00099 set.ao32 $S1 AO_MODE M_AWGI 00100 get.ao32 $S1 AO_MODE 00101 # ... returns M_AWGI 00102 00103 @endverbatim 00104 - Build LLC Software- 00105 @verbatim 00106 cd LOWLATENCY2; make 00107 @endverbatim 00108 - define the system by editing a local xml system def file: 00109 -eg xml/example1-krypton-96AI.xml 00110 @verbatim 00111 # create a job control file, example: 00112 00113 # top-level-launcher 00114 cd PROJECTS/LOWLATENCY2/ 00115 export LL2_SYSFILE=xml/example1-krypton.xml 00116 export LL_OUTPUTS=data/ai-ramps.dat 00117 export $(./xml_lookup $LL2_SYSFILE system/exports) 00118 cd scripts 00119 ./setup.ao32 $MB $S1 00120 ./deploy-script set.llc.wd $MB 00121 acqcmd -s $MB set.llc.wd 00122 source ./setup.clocks2 $MB 0 96 $CB 00123 init_clocks 00124 @endverbatim 00125 - now configure and run the system 00126 @verbatim 00127 source ./top-level-launcher 00128 # options 00129 ./web-trigger 00130 # run the shot -n ITERATIONS ECM (sample interval in 1usec ticks) 00131 ./example-dynamic -v 2 -n 10000 100 00132 @endverbatim 00133 - sample output 00134 @verbatim 00135 [dt100@krypton LOWLATENCY2]$ LL_OUTPUTS=data/ai-ramps.dat ./example-dynamic -v 2 -n 100 1000 00136 slot:2 class:acq196 00137 slot:3 class:ao32 00138 ECM 1000 00139 mmap 0xb7f4b000 offset 16 00140 data: 00000004 37400000 ffffffff 00000ec6 00141 mmapDmaBuffer() 01: open /dev/acq32/acq32.2.host 00142 mmapDmaBuffer() ask for full bigbuf portion 400000 00143 mmapDmaBuffer() fd_in:4 physaddr 0x37400000 vaddr 0xb7b42000 len 4194304 00144 mmapDmaBuffer() written host0.dat, zeroed 00145 speed=1197 ticks per microsecond 00146 ACQ196::init(0x9d49e1c) mbox 0x9d48ee8 00147 M_SYNC_2V sample size 256 00148 appEnterLLC_SYNC_2V() va:0xb7b42000 pa:0x37400000 MASTER slaves 1 00149 enterLLC() 00150 llSetAddr [2] 0x37400000 00151 leave LLC 00152 ~LL_ControlSystemI() 00153 @endverbatim 00154 - Validation 00155 - Channel 33-64 DO64 loopback to AI, data in AI.dat 00156 @verbatim 00157 [dt100@krypton LOWLATENCY2]$ ./scripts/dump.ai.decimal ai.dat 33-64 | ./scripts/do-analyse | head 00158 1:00000000000000000000000000000000 00159 2:00000000000000000000000000000000 00160 3:11111111111111111111111111111111 00161 4:00000000000000000000000000000000 00162 5:11111111111111111111111111111111 00163 6:00000000000000000000000000000000 00164 7:11111111111111111111111111111111 00165 8:00000000000000000000000000000000 00166 @endverbatim 00167 - Timestamps 00168 @verbatim 00169 # NB: does not work on ACQ196-96-500, only ACQ196-96-250 00170 ./scripts/dump.status.decimal | cut -d\ -f 12 | tail 00171 9999920 00172 9999940 00173 9999960 00174 9999980 00175 10000000 00176 @endverbatim 00177 - AO/AI data. 00178 - AO32 loopback to AI01-32 Data saved to ai.dat 00179 - use gnu-octave to view the data 00180 @verbatim 00181 cat llplot.m 00182 00183 1; 00184 nchan=96 00185 tb=1:100; 00186 fp=fopen("ai.dat"); 00187 raw=fread(fp,Inf,"short"); 00188 len=length(raw) 00189 chx=reshape(raw,nchan,len/nchan); 00190 plot(tb,chx(1:32,tb)) 00191 octave 00192 octave:1> llplot 00193 00194 @endverbatim 00195 00196 */ 00197 00198 #ifndef LL2_H_ 00199 #define LL2_H_ 00200 00201 #include <iostream> 00202 #include <vector> 00203 #include <stdio.h> 00204 #include <string.h> 00205 00206 using namespace std; 00207 00208 /** id's the classes of IO. */ 00209 enum IO { 00210 00211 AI, AO, DI, DO, ST, IONUM 00212 }; 00213 00214 /** id's the slot numbers. 1:1 correspondence */ 00215 enum SLOTS { 00216 00217 SLOT1=1, SLOT2, SLOT3, SLOT4, SLOT5, SLOT6, SLOT7, SLOT8 00218 }; 00219 static const char* IO(enum IO io); 00220 00221 /** new zero'd data - avoids valgrind errors. */ 00222 template <class T> T* znew(int nwords) { 00223 T *buf = new T [nwords]; 00224 memset(buf, 0, nwords*sizeof(T)); 00225 return buf; 00226 } 00227 00228 #define NO_VALUE 0 /**< null buffer arg (ignore). */ 00229 #define AI_SIZE sizeof(short) 00230 #define AO_SIZE sizeof(short) 00231 #define DO_SIZE sizeof(u32) 00232 #define DI_SIZE sizeof(u32) 00233 #define STATUS_SIZE sizeof(u32) 00234 00235 #define BITS_PER_DX_WORD 32 /**< DX packed into 32 bit words */ 00236 00237 /** base class for cards and system. */ 00238 class ACQ { 00239 00240 protected: 00241 const char* id; 00242 const int ai_count; 00243 const int di_count; 00244 const int ao_count; 00245 const int do_count; 00246 00247 00248 ACQ(int _ai_count, int _di_count, int _ao_count, int _do_count): 00249 ai_count(_ai_count), 00250 di_count(_di_count), 00251 ao_count(_ao_count), 00252 do_count(_do_count) 00253 {} 00254 00255 public: 00256 virtual int getAI(short* ai_values) { 00257 return -1; 00258 } 00259 virtual int putAO(const short* ao_values) { 00260 return -1; 00261 } 00262 virtual int putDO(const unsigned *do_values){ 00263 return -1; 00264 } 00265 virtual int getDI(unsigned *di_values){ 00266 return -1; 00267 } 00268 virtual int getStatus(unsigned *status_values){ 00269 return -1; 00270 } 00271 virtual void print() { 00272 cerr << id << " [" << "AI=" << ai_count << "," 00273 << "DI=" << di_count << "," 00274 << "AO=" << ao_count << "," 00275 << "DO=" << do_count << "]" << endl; 00276 } 00277 00278 virtual int getAI_count(void) const { return ai_count; } 00279 virtual int getAO_count(void) const { return ao_count; } 00280 virtual int getDI_count(void) const { return di_count; } 00281 virtual int getDO_count(void) const { return do_count; } 00282 virtual int getStatus_count(void) const { return 0; } 00283 virtual ~ACQ() {} 00284 }; 00285 00286 /** opaque structure hooks to old llcontrol code. */ 00287 struct Card; 00288 00289 00290 /** base class for physical cards. */ 00291 class ACQ_Card: public ACQ { 00292 00293 protected: 00294 const int slot; 00295 void registerCard(int slot); 00296 00297 public: 00298 ACQ_Card( 00299 int _slot, 00300 int _ai_count, int _di_count, int _ao_count, int _do_count): 00301 ACQ(_ai_count, _di_count, _ao_count, _do_count), 00302 slot(_slot) 00303 { 00304 registerCard(slot); 00305 } 00306 Card *card; 00307 virtual void init(Card *card) {} 00308 00309 int getSlot() const { return slot; } 00310 static ACQ_Card* getCard(int slot); 00311 }; 00312 00313 00314 class ACQ196 : public ACQ_Card { 00315 00316 public: 00317 virtual int getAI(short* ai_values); 00318 virtual int putAO(const short* ao_values); 00319 virtual int putDO(const unsigned *do_values); 00320 virtual int getDI(unsigned *di_values); 00321 virtual int getStatus(unsigned *status_values); 00322 void toggle_wd(); 00323 00324 00325 virtual void init(Card *card); 00326 00327 ACQ196(int _slot, int _ai_count=96, int _di_count=32, 00328 int _ao_count=16, int _do_count=0) : 00329 ACQ_Card(_slot, _ai_count, _di_count, _ao_count, _do_count) 00330 { 00331 char* _id = new char[64]; 00332 sprintf(_id, "acq196.%d", slot); 00333 id = _id; 00334 } 00335 virtual ~ACQ196() 00336 { 00337 delete [] id; 00338 } 00339 int getStatus_count(void) const; 00340 }; 00341 00342 class AO32 : public ACQ_Card { 00343 ACQ196* master; /**< master card controlling this AO32. */ 00344 int seq; /**< AO32 card # in set. */ 00345 00346 public: 00347 virtual int putAO(const short* ao_values); 00348 virtual int putDO(const unsigned *do_values); 00349 00350 AO32(int _slot, int _ao_count=32, int _do_count=64) : 00351 ACQ_Card(_slot, 0, 0, _ao_count, _do_count), 00352 master(0), seq(0) 00353 { 00354 char* _id = new char[64]; 00355 sprintf(_id, "ao32.%d", slot); 00356 id = _id; 00357 } 00358 00359 void setMaster(ACQ196* _master) { 00360 master = _master; 00361 } 00362 void setIndex(int _index) { 00363 seq = _index; 00364 } 00365 virtual ~AO32() 00366 { 00367 delete [] id; 00368 } 00369 }; 00370 00371 00372 00373 /** abstract factory class - implementation detail hidden */ 00374 class LL_ControlSystem : public ACQ { 00375 00376 protected: 00377 LL_ControlSystem(const char *_id = "LL_ControlSystem"): 00378 ACQ(0,0,0,0) 00379 { 00380 id = _id; 00381 } 00382 00383 public: 00384 virtual int addCard(ACQ196* _acq196) = 0; 00385 /**< add ACQ196 card to system. */ 00386 virtual int addCard(AO32* _ao32) = 0; 00387 /**< add AO32 card to system. */ 00388 00389 virtual void print() = 0; 00390 /**< print a summary of the system. 00391 * xml output could be used to auto initialise application buffers 00392 */ 00393 00394 virtual void init(int argc, const char* argv[]) = 0; 00395 /**< initialise capture parameters. */ 00396 00397 virtual int Arm( 00398 const short* ao_values_init = NO_VALUE, 00399 const unsigned* do_values_init = NO_VALUE) = 0; 00400 /**< Arm the capture with initial conditions. 00401 * @param ao_values_init ensure initial AO condition. 00402 * @param do_values_init ensure initial DO condition. 00403 */ 00404 virtual int IO( 00405 const short* ao_values, const unsigned* do_values, 00406 short* ai_values, unsigned* di_values, unsigned *status) = 0; 00407 /**< Block and return on next sample. 00408 * Sets outputs before blocking, returns inputs. 00409 */ 00410 virtual int Stop() = 0; 00411 /**< Stop the capture. */ 00412 00413 virtual int getSamples() const = 0; 00414 /**< returns samples to capture set in init(). */ 00415 00416 virtual int getOffset(ACQ_Card* card, enum IO io) = 0; 00417 /**< return offset in client vector for <card> with IO type <io>. */ 00418 static LL_ControlSystem& create(const char *_id); 00419 /**< factory method 00420 * @param _id name to identify system 00421 * */ 00422 00423 static LL_ControlSystem& createFromFile(const char *config_file); 00424 /**< factory method 00425 * @param config_file names xml state file that pre-defines the system. 00426 * */ 00427 00428 static void closedown(LL_ControlSystem& sys); 00429 /**< cleanup */ 00430 }; 00431 00432 #endif /* LL2_H_ */