//------------------------------------------------------------------------------ // Communication system master -- // (C) Piero Giubilato 2008-2010, Berkeley Lab -- //------------------------------------------------------------------------------ //______________________________________________________________________________ // {Trace} // [File name] "comm_Master.cpp" // [Author] "Piero Giubilato" // [Version] "1.0" // [Modified by] "Piero Giubilato" // [Last revision] "17 Feb 2009" // [Language] "C++" // [Compiler] "Visual C++ 9.x" // [Member of] "Cool SEAL" // [Project] "SEAL" // [Description] "Manages the parallel buffering system by glueing the USB" // "level buffering with the data set. Also applies the geometry." // [Key documentation] // "Root user's guide, writing a graphical interface" // "Visual C++ Reference Help" // {Trace} //______________________________________________________________________________ //______________________________________________________________________________ // // WARNING: this is a special class, singleton implemented, which is NOT thread // safe and can NOT be initialized more than once (hence the singleton...). //______________________________________________________________________________ // Standard libs #include #include // Application components #include "global.h" #include "cool.h" #include "comm_USB.h" #include "comm_Reg.h" #include "comm_Master.h" //______________________________________________________________________________ // Static member instantiation // Singleton comm_Master* comm_Master::inst_Handle = 0; int comm_Master::inst_Count = 0; // Multi device references int comm_Master::comm_ON = 0; int comm_Master::dev_ID = 0; // Multi threading handles for the burst reading Int_t comm_Master::burst_read_Flag = 0; HANDLE comm_Master::burst_read_Hnd = 0; UInt_t comm_Master::burst_read_ID = 0; UInt_t comm_Master::burst_read_TOut = 1000; clock_t comm_Master::burst_read_TRef = 0; // Reading double buffering management data_Burst* comm_Master::burst_read_Ref = NULL; Int_t comm_Master::cnk_WR = 1; Int_t comm_Master::cnk_RD = 0; UShort_t* comm_Master::cnk_Buf[2] = {NULL, NULL}; UInt_t comm_Master::cnk_Pos[2] = {0, 0}; UInt_t comm_Master::cnk_Len[2] = {0, 0}; // Chunk reading data, data_Burst structure UInt_t comm_Master::brs_frm_Count = 0; UInt_t comm_Master::brs_lyr_Count = 0; UInt_t comm_Master::brs_chn_Count = 0; UInt_t* comm_Master::brs_gmt_Layer = NULL; UInt_t** comm_Master::brs_gmt_Front = NULL; UShort_t** comm_Master::brs_lyr_Front = NULL; UInt_t comm_Master::lyr_dp_Idx = 0; UInt_t comm_Master::brs_word_Count = 0; UInt_t comm_Master::brs_word_Left = 0; UInt_t comm_Master::frm_word_Count = 0; // USB inner buffering int comm_Master::usb_buf_Len = 262144; int comm_Master::usb_buf_tOut = 1000; // Chunk reading data, chunk structure UInt_t comm_Master::cnk_buf_Len = 0; UShort_t* comm_Master::cnk_rd_Buf = NULL; UInt_t comm_Master::cnk_rd_Idx = 0; UInt_t comm_Master::cnk_rd_Len = 0; UInt_t comm_Master::cnk_rd_Stop = 0; // Chunk reading data, packet structure UInt_t comm_Master::pck_Idx = 0; UInt_t comm_Master::pck_tStamp = 0; UInt_t comm_Master::pck_Count = 0; UInt_t comm_Master::pck_dp_Idx = 0; UInt_t comm_Master::pck_dp_Count = 0; UInt_t comm_Master::pck_chn_Idx = 0; UInt_t comm_Master::pck_chn_Count = 0; UInt_t comm_Master::pck_chn_dp_Idx = 0; UInt_t comm_Master::pck_chn_dp_Count = 0; UInt_t comm_Master::pck_head_Idx = 0; UInt_t comm_Master::pck_head_Count = 0; UInt_t comm_Master::pck_foot_Idx = 0; UInt_t comm_Master::pck_foot_Count = 0; UInt_t comm_Master::pck_Timestamp = 0; UInt_t comm_Master::pck_chk_Flag = 0; UInt_t comm_Master::pck_chk_Count = 0; //______________________________________________________________________________ // Provides a safe self-destruction mechanism in case of missed destruction class CommCleaner { public: ~CommCleaner() {comm_Master::instance_Destroy();} } CommCleanerInst; //______________________________________________________________________________ comm_Master* comm_Master::instance_Load() { // Loads one instance of the class dbg_Print("comm_Master::instance_Load:()", DBG_LVL_ZERO); // Just loads one class instance if (inst_Count == 0) { inst_Handle = new comm_Master(); inst_Count ++; } // Returns the instance handle return inst_Handle; } //______________________________________________________________________________ void comm_Master::instance_Destroy() { // Kills the active instance dbg_Print("comm_Master::instance_Destroy:()", DBG_LVL_ZERO); // Deletes the current class instance if (inst_Handle) { inst_Handle = 0; inst_Count = 0; } } //______________________________________________________________________________ comm_Master::comm_Master() { // Default constructor dbg_Print("comm_Master::comm_Master:()", DBG_LVL_ZERO); } //______________________________________________________________________________ comm_Master::~comm_Master() { // Default destructor dbg_Print("comm_Master::~comm_Master:()", DBG_LVL_ZERO); } //______________________________________________________________________________ int comm_Master::Open() { // Opens the communications dbg_Print("comm_Master::Open:()", DBG_LVL_MAKE); // Searches for all compliant devices cool::Out->Print("\nCOMM: searching connected devices\n", LWHITE); int dev_Count = comm_USB::Open(comm_Const::vendor_ID, comm_Const::device_ID); // Checks for no devices if (dev_Count <= 0) { cool::Out->Print("COMM: no compliant devices found!\n", LYELLOW); return -1; } // Gives some info cool::Out->Print("COMM: Connection is open, "); cool::Out->Print(dev_Count, LWHITE); cool::Out->Print(" device found\n"); // At the moment, we are able to just handle one device at time from // this layer (comm_USB can actually handle up to 8 devices at the same // time through multi-threading). comm_Master::dev_ID = 0; // Info cool::Out->Print("COMM: testing communication\n"); // Preset all the devices int bSize = 1024 * 1024, tOut= 1000; comm_USB::Reset(); comm_USB::buffer_Len(dev_ID, &bSize); comm_USB::timeout_Len(dev_ID, &tOut); comm_USB::Clear(); // Test the connectivity if (Test()) { cool::Out->Print("COMM: communication does not work properly!\n", LYELLOW); comm_Master::comm_ON = 0; return -1; } // Tune buffer size and timeout //if (comm_USB::Tune()) { // dbg_Print("COMM: communication error found!\n", LRED); // return -1; //} // Confirm that the communication is ON and OK cool::Out->Print("COMM: communication is up and running!\n\n", LGREEN); comm_Master::comm_ON = 1; return 0; } //______________________________________________________________________________ int comm_Master::Test() { // Test the functionality of the communication dbg_Print("comm_Master::Test:()", DBG_LVL_FLOW); cool::Out->Print("COMM: performing registers test...\n"); // Re-initialize the random seed srand((unsigned)time(NULL)); // Perform 10 random writing/reading for (UShort_t reg_Idx = 0; reg_Idx < 4; reg_Idx++ ) { // Random pick UShort_t bRange = (int)((double)rand() / (RAND_MAX + 1) * 31); UShort_t bStart = (int)((double)rand() / (RAND_MAX + 1) * (127 - bRange)); // Read UInt_t old_Value = comm_Reg::reg_Value(reg_Idx,bStart, bStart + bRange); // Check UInt_t ref_Value = (unsigned int)pow((double)2, (int)bRange); comm_Reg::reg_Value(reg_Idx,bStart, bStart + bRange, ref_Value); UInt_t rdk_Value = comm_Reg::reg_Value(reg_Idx,bStart, bStart + bRange); // Info if (ref_Value != rdk_Value) { cool::Out->Print("COMM: exception found while testing registers!\n", LRED); return -1; } // Restore comm_Reg::reg_Value(reg_Idx,bStart, bStart + bRange, old_Value); } // Everything fine! cool::Out->Print("COMM: registers test passed\n"); return 0; } //______________________________________________________________________________ bool comm_Master::isOpen() { // Returns the current comm status return (bool)comm_Master::comm_ON; } //______________________________________________________________________________ int comm_Master::Close() { // Closes the communications dbg_Print("comm_Master::Close:()", DBG_LVL_FLOW); cool::Out->Print("\nCOMM: closing communication\n", LWHITE); // Closes the USB if (comm_USB::Close()) { cool::Out->Print("COMM: unable to properly close communication!\n", LRED); } // Everything ok return 0; } //______________________________________________________________________________ int comm_Master::Reset() { // Reset the communication status in the FPGA dbg_Print("comm_Master::Reset:()", DBG_LVL_FLOW); cool::Out->Print("\nCOMM: sending SOFT reset to FPGA\n", LYELLOW); int buf_Len = 16; UShort_t buffer[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; if (comm_Master::raw_Write(buffer, &buf_Len)) { cool::Out->Print("COMM: unable do deliver the SOFT reset!\n", LRED); return -1; } // Everything ok return 0; } //______________________________________________________________________________ int comm_Master::Purge() { // Purge the reading buffer dbg_Print("comm_Master::Purge:()", DBG_LVL_FLOW); // Reads until the buffer is free unsigned short* buffer = new unsigned short[65536]; const int buf_Len = 65536; int buf_Read = buf_Len; unsigned int w_Count = 0; // Search through the buffer while (buf_Read == buf_Len) { // Read buf_Read = buf_Len; if (comm_USB::Read(comm_Master::dev_ID, buffer, &buf_Read, true, 100)) break; // Keep this commented unless debugging //if (buf_Read > 0) { // for (int i= 0; i < buf_Read; i++) { // std::cout << "Word # " << (w_Count + i) << ": " << (unsigned short)buffer[i] << "\n"; // } //} else std::cout << "Purge: buffer is empty!\n"; // Updates total count w_Count += buf_Read; } cool::Out->Print("COMM: buffer purged: "); cool::Out->Print(w_Count, LWHITE); cool::Out->Print(" words read.\n"); // Releases the buffer delete []buffer; // Everything ok return w_Count; } //______________________________________________________________________________ int comm_Master::Clear() { // Reset the communication status in the FPGA; plus purge the buffer // until empty dbg_Print("comm_Master::Clear:()", DBG_LVL_FLOW); // Sends a reset Reset(); // Purge until empty while (Purge()) {} // Everything ok return 0; } //______________________________________________________________________________ int comm_Master::raw_Write(UShort_t* buffer, Int_t* buf_Len) { // Perform a raw writing into the USB dbg_Print("comm_Master::raw_Write:()", DBG_LVL_FLOW); // Writes return comm_USB::Write(comm_Master::dev_ID, buffer, buf_Len); } //______________________________________________________________________________ int comm_Master::raw_Read(UShort_t* buffer, Int_t* buf_Len, bool wait) { // Perform a raw reading from the USB dbg_Print("comm_Master::raw_Read:()", DBG_LVL_FLOW); // Reads comm_USB::Read(comm_Master::dev_ID, buffer, buf_Len, wait); // Everything ok return 0; } //______________________________________________________________________________ int comm_Master::burst_Read(data_Burst* burst_Ref, bool wait, unsigned int timeout) { // Reads a complete set of data and stores it into set_Ref. This function // will launch a separate thread, so freeing the application to perform // other operation. To check for the status of the reading use the burst_Busy // function, or the burst_Wait to wait to finishing the read. // If 'wait' is set true, after launching the separate therad the function // will wait for the thread to finish instead of returning to the caller. // 'timeout' sets the time limit behind which the reading thread will shut down // if no more data available. // Flag values are: 0 = thread is not committed // 1 = thread is running // 2 = thread completed successfully // 3 = thread completed with timeout // 16 = thread completed with error // Debug dbg_Print("comm_Master::burst_Read:(default)", DBG_LVL_FLOW); // Flag checking and asserting, thread cleaning in case if (burst_read_Flag == 1) { // Debug dbg_Print("comm_Master::burst_Read: ReadTR is still busy", DBG_LVL_WARN); // Device currently in use, cannot continue return 0; // Device has terminated without error } else if (burst_read_Flag == 2 || burst_read_Flag == 3) { // Close previous thread and continue CloseHandle(burst_read_Hnd); // Device has terminated with error } else if (burst_read_Flag == 16) { // Debug dbg_Print("comm_Master::burst_Read: error found on ReadTR", DBG_LVL_WARN); // Anyway close the thread but exit CloseHandle(burst_read_Hnd); } // Updates the timeout limit burst_read_TOut = timeout; // Sets the flag as running; this will prevent any other call to the thread. burst_read_Flag = comm_Const::brs_err_No; // Stores the reference to the data_Set buffer to be used burst_read_Ref = burst_Ref; // Makes the call to the threaded function, which in turn will call // the class library to read the device. Note how the passed argument // is a reference to a static variable, so even if the new threads // starts after the father thread has already been closed, the data are // still available. burst_read_Hnd = (HANDLE)_beginthreadex(NULL, 0, &burst_read_MT, NULL, 0, &burst_read_ID); // If we need, we can stop (kill sounds bad) the father: all the data // the child needs are stored in static vars, so they will survive the // closing of the father scope. Anyway, if wait = true, waits for the // end of the child thread if (wait) WaitForSingleObject(burst_read_Hnd, INFINITE); // Exits with the last flag values (meaningful only if in wait mode) return burst_read_Flag; } //______________________________________________________________________________ unsigned int __stdcall comm_Master::burst_read_MT(void* Args) { // This is the thread function called as a new thread each time a // reading has to be performed on a burst. It fills a data_Burst structure // with the raw (but packetized) data arriving from the USB. // This version is intended for filling data_Burst structures // brs_read_Flag: 0 = thread is not committed // 1 = thread is reading from device // 2 = thread completed successfully // 3 = thread completed with timeout // 16 = thread completed with error // Geometry rearrangement in brief. // the data_Burst structure is filled accordingly to the information stored // into the linked geom_Frame data. Basically, raw data from the DARK firware // are packetized by channel, where each channel refers to one hardware logical // channel. Now, more than one hardware channel can be used to feed data into // a single logical (data) layer and, furthermore, each hardware channel can // have a different mapping geometry. // To address this, the geom_Frame provides three reference elements that can // be used to describe the intended detector system. // The structure geom_Chip contains the geometrical mapping of one hardware // channel, i.e. the map which links each data point (dp) position into the // hardware channel to a geometrical position into the logical layer (ex: dp // #27 has to be placed at position #84 of the target layer). // The structure geom_Channel links the geom_Chip to the logical layer, i.e. // saying that the geom_Chip #2 is used to map the data coming from the hardware // channel #1 into the logical layer #1... seems complicate but it is not. // The geom_Layer objects is just used to store the dimensions of the logical // layer (NOT the data!) and other parameters like the spatial position of the // layer itself. // When a data_Burst structure is passed to the comm_Master read function, the // linked geom_Frame described above is used to remap the raw data coming from // the firmware directly into the data_Layer elements, so performing real-time // data geometry remapping // Brief raw protocol description: the protocol is a free-size packet one, so // every packet can have a different size. Each packet HAS one fixed header // of 'comm_Const::pck_head_Count' words, of which the last two state the // number of data point following the header (up to a 2^32). All the routine // takes no change if one changes the size of the header, so allowing for // protocol modifications. // The basic idea is that each packet is related to a frame of the data burst, // so the number of packets necessary to fulfill a burst equals the number // of frames inside the burst itself. Note how a buffer chunk (the basic unit // managed by the USB class) has fixed size, and this size off course is // different from the variable packet one, so packets have to be reconstructed // through different (but adiacent) data chunks. // A packet resembles this: // // +-------+-------------------------------------------+ // | Word# | Meaning | // +-------+-------------------------------------------+ // | 0 | Packet Count | // | 1 | Packet Index | // | 2 | Channel Count (chn_Count) | // | 3 | Channel dp Count (dp_Count 16 MSb) | // | 4 | Channel dp Count (dp_Count 16 LSb) | // | 5 | Timestamp 16 MSb | // | 6 | Timestamp 16 LSb | // | 7 | dp 0 of channel 0 | // | 8 | dp 0 of channel 1 | // | ... | dp 0 of channel ... | // | n-1 | dp 0 of channel chn_Count - 2 | // | n | dp 0 of channel chn_Count - 1 | // | n+1 | dp 1 of channel 0 | // | n+2 | dp 1 of channel 1 | // | ... | dp 1 of channel ... | // | m-1 | dp dp_Count - 1 of channel chn_Count - 2 | // | m | dp dp_Count - 1 of channel chn_Count - 1 | // | m+1 | Error flag (8 LSb) | // | m+2 | Data word count (16 MSb) | // | m+3 | Data word count (16 LSb | // +-------+-------------------------------------------+ // // Basically, the reading proceeds by incrementing two separate sets of // indexes, the chunk indexs (cnk_...) and the packet indexes (pck_...). // The two sets are indipendent, this coming from the desynchronization // between chunks (USB hardware) and packets (Data abstraction). // A third indexes set (brs_...) is used to verify that what readed suits // the shape of the given data_Burst. // // Note, all these variable are static inside the comm_Master class! // Sets up the acquisition parameters dbg_Print("comm_Master::burst_read_MT:(void*)", DBG_LVL_FLOW); burst_read_Setup(); // This is the first call to chunk_Read, and it is used both to start // the MT double buffering in the right order and to improve first // buffer read lag burst_read_Chunk(); // Then issues the acquire command to the FPGA dbg_Print("comm_Master::burst_read_MT: sending the ACQ command", DBG_LVL_STEP); int buf_Len = 16; UShort_t buffer[16] = {4,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; if (comm_Master::raw_Write(buffer, &buf_Len)) { dbg_Print("comm_Master::burst_read_MT: error sending the ACQ command", DBG_LVL_PLAY); burst_read_Flag = comm_Const::brs_err_Ask; } // Asks and manipulates all the data necessary to fill a data burst (set LOOP) while (pck_Idx < pck_Count && burst_read_Flag == comm_Const::brs_err_No) { // Ask for one more chunk every time burst_read_Chunk(); // Manipulates the data if anything in the last read chunk if (cnk_Len[cnk_RD]) { // Copies the fast references variables for this chunk cnk_rd_Buf = cnk_Buf[cnk_RD]; cnk_rd_Len = cnk_Len[cnk_RD]; cnk_rd_Idx = cnk_Pos[cnk_RD]; // *** DEBUG //std::cout << " --> RD buf: " << cnk_Len[cnk_RD] << "\n"; //std::cout << " --> WR buf: " << cnk_Len[cnk_WR] << "\n"; //std::cout << " --> cnk_RD: " << cnk_RD << "\n"; //std::cout << " --> cnk_rd_Len: " << cnk_rd_Len << "\n"; //std::cout << " --> cnk_rd_Idx: " << cnk_rd_Idx << "\n"; //std::cout << " --> pck_dp_Idx: " << pck_dp_Idx << "\n"; //std::cout << " --> pck_Idx: " << pck_Idx << "\n"; //std::cout << " --> pck_Count: " << pck_Count << "\n"; //if (cnk_rd_Len < 300) { // for (int i = 0; i < cnk_rd_Len; i++) { // std::cout << " Word # " << i << ": " << (UShort_t)cnk_rd_Buf[i] << "\n"; // } //} // *** END DEBUG // Runs through the chunk buffer, steps at each packet begin while (cnk_rd_Idx < cnk_rd_Len && pck_Idx < pck_Count && burst_read_Flag == comm_Const::brs_err_No) { burst_read_Header(); // Reads/checks the header burst_read_Check(); // Checks if the received data match the data structure burst_read_Bulk(); // Performs a bulk read whenever possible burst_read_Transition(); // Reads the data during a chunk transition burst_read_Footer(); // Checks for a packet/frame end } // End of the chunk loop } // End of chunk read IF // Stores information about chunk last read position cnk_Pos[cnk_RD] = cnk_rd_Idx; } // End of burst LOOP // Complete the reading burst_read_Exit(); // Exits the thread _endthreadex(0); return 0; } //______________________________________________________________________________ void comm_Master::burst_read_Setup() { // Sets up all the parameters necessary to the burst reading dbg_Print("comm_Master::burst_read_Startup:()", DBG_LVL_STEP); // Geometry and Data Access // ------------------------ // Retrieves all the geometry parameters brs_frm_Count = burst_read_Ref->frame_Count(); brs_chn_Count = burst_read_Ref->frame_Fit()->channel_Count(); brs_lyr_Count = burst_read_Ref->frame_Fit()->layer_Count(); brs_gmt_Front = new UInt_t*[brs_chn_Count]; brs_gmt_Layer = new UInt_t[brs_lyr_Count]; // Retrieves all the geometry front references & the channel target layers for (UInt_t ch_Idx = 0; ch_Idx < brs_chn_Count; ch_Idx++) { brs_gmt_Front[ch_Idx] = burst_read_Ref->frame_Fit()->chip(burst_read_Ref->frame_Fit()->channel(ch_Idx)->chip_Idx())->dp_Front(); brs_gmt_Layer[ch_Idx] = burst_read_Ref->frame_Fit()->channel(ch_Idx)->layer_Idx(); } // Retrieves all the data_Layer front references [F0L0, F0L1... F0Ln, F1L0, F1L1... F1Ln, FmL0, FmL1... FmLn] brs_lyr_Front = new UShort_t*[brs_lyr_Count * brs_frm_Count]; for (UInt_t f = 0; f < brs_frm_Count; f++) { for (UInt_t l = 0; l < brs_lyr_Count; l++) { brs_lyr_Front[f * brs_lyr_Count + l] = burst_read_Ref->frame(f)->layer(l)->dp_Front(); } } // All words expected to complete the burst frm_word_Count = burst_read_Ref->frame(0)->dp_Count() + comm_Const::pck_head_Count + comm_Const::pck_foot_Count; brs_word_Count = frm_word_Count * burst_read_Ref->frame_Count(); brs_word_Left = brs_word_Count; // Reading vars presets // -------------------- // Running, layer independent, dp index lyr_dp_Idx = 0; // Per layer dp, so indipendent from the layer Idx // Current packet description and status pck_Idx = 0; // Current packet index pck_tStamp = 0; // Current packet timestamp pck_Count = 1; // How many packets to complete a data burst transmission pck_dp_Idx = 0; // What dp inside the current packet pck_dp_Count = 0; // How many dp inside this packet pck_chn_Idx = 0; // Current packet layer index pck_chn_Count = 0; // How many channel inside the packet pck_chn_dp_Idx = 0; // What dp inside the current channel pck_chn_dp_Count = 0; // How many dp inside each channel pck_head_Idx = 0; // On header position pck_head_Count = comm_Const::pck_head_Count; // Header lenght pck_foot_Idx = 0; // On footer position pck_foot_Count = comm_Const::pck_foot_Count; // Footer lenght // Data double buffering preset // ---------------------------- // Sets an appropriate buffer size accordingly to the available // usb buffer space and the data load request if (brs_word_Count <= (comm_USB::buffer_Len(dev_ID) + 256)) { cnk_buf_Len = brs_word_Count; } else { //if frm_ cnk_buf_Len = comm_USB::buffer_Len(dev_ID); //int bLen = frm_word_Count + 256; //comm_USB::Set(dev_ID, &bLen, &tOut); } // *** DEBUG //cnk_buf_Len = 1024 * 1024; // This for DEBUG only! //cnk_buf_Len = 262144; // This for DEBUG only! //cnk_buf_Len = 4096; // This for DEBUG only! //std::cout << " *** Hard buffer lenght is: " << comm_USB::buffer_Len(dev_ID) << "\n"; //std::cout << " *** Soft chunk lenght is: " << cnk_buf_Len << "\n"; //std::cout << " *** Effective timeout is: " << tOut << "\n"; // *** END DEBUG // Resets buffers in case if (cnk_Buf[0] || cnk_Buf[1]) { delete []cnk_Buf[0]; delete []cnk_Buf[1]; } cnk_Buf[0] = new UShort_t[cnk_buf_Len]; cnk_Buf[1] = new UShort_t[cnk_buf_Len]; // Clears lenghts and pointers cnk_Len[0] = 0; cnk_Len[1] = 0; cnk_Pos[0] = 0; cnk_Pos[1] = 0; // Set startup position cnk_WR = 0; cnk_RD = 1; // Set timeout starting reference burst_read_TRef = clock(); } //______________________________________________________________________________ void comm_Master::burst_read_Chunk() { // Reads a chunk from the USB if (burst_read_Flag != comm_Const::brs_err_No) return; dbg_Print("comm_Master::burst_read_Chunk:()", DBG_LVL_STEP); // Waits until a new buffer is ready while (comm_USB::read_Busy(comm_Master::dev_ID)) {} // Check for single chunk timeout //if (comm_USB::read_Flag(comm_Master::dev_ID) == comm_USB::kf_tOut) { // // // Put info // cool::Out->Print("COMM: chunk completed with USB timeout!\n", LYELLOW); // cool::Out->Print("COMM: "); cool::Out->Print(comm_USB::read_Len(comm_Master::dev_ID)); // cool::Out->Print(" words from the last read\n"); // // // Updates the flag // burst_read_Flag = comm_Const::brs_err_Timeout; // Set timeout condition //} // Checks for general burst timeout if (cnk_Len[cnk_RD] == 0) { if ((UInt_t)(1000 * (clock() - burst_read_TRef) / CLOCKS_PER_SEC) > burst_read_TOut) { dbg_Print("comm_Master::burst_read_MT: timeout!", DBG_LVL_WARN); burst_read_Flag = comm_Const::brs_err_Timeout; // Set timeout condition } } else burst_read_TRef = clock(); // Switches between buffers at every chunk completion, to be performed as // first step to preserve the information about the last written chunk if (cnk_WR) {cnk_WR = 0; cnk_RD = 1;} else {cnk_WR = 1; cnk_RD = 0;} // Calculate how many words for the next chunk: note that now the just acquired // one (cnk_WR) is the ready to be parsed one (cnk_RD)! brs_word_Left -= cnk_Len[cnk_RD]; (brs_word_Left < cnk_buf_Len) ? cnk_Len[cnk_WR] = brs_word_Left : cnk_Len[cnk_WR] = cnk_buf_Len; // Makes the read call if more data are needed if (cnk_Len[cnk_WR]) { // *** DEBUG //std::cout << " *** HERE calling the USB read!\n"; //std::cout << " cmm_USB = " << comm_USB::read_Busy(comm_Master::dev_ID) << "\n"; //std::cout << " wrd_Left = " << brs_word_Left << "\n"; //std::cout << " cnk_WR = " << cnk_WR << "\n"; //std::cout << " cnk_Len = " << cnk_Len[cnk_WR] << "\n"; // *** END DEBUG // Read (note how tOut is not set here, but in burst_read_Setup) comm_USB::Read(dev_ID, cnk_Buf[cnk_WR], (int*)&cnk_Len[cnk_WR], false, 2000); // Reset the read pointer for this buffer cnk_Pos[cnk_WR] = 0; // *** DEBUG //std::cout << " cmm_USB = " << comm_USB::read_Busy(comm_Master::dev_ID) << "\n"; //std::cout << " cnk_Len = " << cnk_Len[cnk_WR] << "\n"; //std::cout << "\n"; // *** END DEBUG } // previous reading has completed, new one has been started! return; } //______________________________________________________________________________ void comm_Master::burst_read_Header() { // Reads the header of a packet, so deriving the information necessary in // order to read the content of the packet. Handles the transition in case // the header results spllitted between different chunks. if(burst_read_Flag != comm_Const::brs_err_No) return; dbg_Print("comm_Master::burst_read_Header:()", DBG_LVL_STEP); // Reads the header, chunk transition-proof mode while (pck_head_Idx < pck_head_Count && cnk_rd_Idx < cnk_rd_Len) { // Header values switch switch (pck_head_Idx) { // Word #0: Packets (frames) count case 0: pck_Count = (UInt_t)cnk_rd_Buf[cnk_rd_Idx]; pck_dp_Idx = 0; pck_chn_dp_Idx = 0; lyr_dp_Idx = 0; break; // Word #1: Packet (frame) Idx case 1: pck_Idx = (UInt_t)cnk_rd_Buf[cnk_rd_Idx]; break; // Word #2: Channel Count case 2: pck_chn_Count = (UInt_t)cnk_rd_Buf[cnk_rd_Idx]; pck_chn_Idx = pck_chn_Count; break; // Word #3: dp count per channel (MSB 16 bits) case 3: pck_chn_dp_Count = ((UInt_t)cnk_rd_Buf[cnk_rd_Idx]) << 16; break; // Word #3: dp count per channel (LSB 16 bits) case 4: pck_chn_dp_Count += (UInt_t)cnk_rd_Buf[cnk_rd_Idx]; pck_dp_Count = pck_chn_Count * pck_chn_dp_Count; break; // Word #5: timestamp (MSB 16 bits) case 5: pck_tStamp = ((UInt_t)cnk_rd_Buf[cnk_rd_Idx]) << 16; break; // Word #6: timestamp (LSB 16 bits) case 6: pck_tStamp += (UInt_t)cnk_rd_Buf[cnk_rd_Idx]; } // Increases the packet head and chunk buffer reading positions pck_head_Idx++; cnk_rd_Idx++; } // Reset relevant packet parameters pck_foot_Idx = 0; // Footer set to 0 // *** DEBUG //if (pck_head_Idx == pck_head_Count && pck_dp_Idx == 0) { // std::cout << " *** HEADER [pck " << pck_Idx << "]\n"; // std::cout << " Head 0: pck_Count = " << pck_Count << "\n"; // std::cout << " Head 1: pck_Idx = " << pck_Idx << "\n"; // std::cout << " Head 2: pck_chn_Count = " << pck_chn_Count << "\n"; // std::cout << " Head 3 & 4: pck_chn_dp_Count = " << pck_chn_dp_Count << "\n"; // std::cout << " Head 5 & 6: pck_tStamp = " << pck_tStamp << "\n"; //} // *** END DEBUG } //______________________________________________________________________________ void comm_Master::burst_read_Check() { // Cheks for mismatching between what we are getting from the data packet // and what we need to correctly fill the data_Burst object if (burst_read_Flag != comm_Const::brs_err_No) return; dbg_Print("comm_Master::burst_read_Check:()", DBG_LVL_STEP); // Checks if the received data match the data structure // only at the end of the packet if (pck_head_Idx == pck_head_Count) { // Possible error hints if (pck_Count != brs_frm_Count) burst_read_Flag = comm_Const::brs_err_Mismatch; if (pck_chn_Count != brs_chn_Count) burst_read_Flag = comm_Const::brs_err_Mismatch; // Error show if (burst_read_Flag != comm_Const::brs_err_No) { dbg_Print("comm_Master::burst_read_Check: mismatch found!", DBG_LVL_WARN); } } } //______________________________________________________________________________ void comm_Master::burst_read_Bulk() { // Reads a bulk of data from the current packet. This function reads the data // by flushing at each step a bulk data blok made by the dp belonging to all // present channels. if (burst_read_Flag != comm_Const::brs_err_No) return; // If we are NOT in a chunk transition, checks if a bulk read is possible if (pck_chn_Idx == pck_chn_Count && (cnk_rd_Len - cnk_rd_Idx >= pck_chn_Count)) { // Debug dbg_Print("comm_Master::burst_read_Bulk:()", DBG_LVL_STEP); // Calculates the number of entire channel blocks available into this chunk if ((pck_dp_Count - pck_dp_Idx) <= (cnk_rd_Len - cnk_rd_Idx)) { // All blocks inside the chunk cnk_rd_Stop = cnk_rd_Idx + (pck_dp_Count - pck_dp_Idx); } else { // As much as possible blocks inside the chunk cnk_rd_Stop = cnk_rd_Idx + int((cnk_rd_Len - cnk_rd_Idx) / pck_chn_Count) * pck_chn_Count; } // Reads the data by channel block for (UInt_t rd_Idx = cnk_rd_Idx; rd_Idx < cnk_rd_Stop; rd_Idx += pck_chn_Count) { // Reads a single channel block. It reads one dp for each channel of the block. for (pck_chn_Idx = 0; pck_chn_Idx < pck_chn_Count; pck_chn_Idx++) { // The actual data transfer AND geometry mapping is performed here brs_lyr_Front[pck_Idx * brs_lyr_Count + brs_gmt_Layer[pck_chn_Idx]] [brs_gmt_Front[pck_chn_Idx][lyr_dp_Idx]] = cnk_rd_Buf[rd_Idx + pck_chn_Idx]; } // Increases layer dp index lyr_dp_Idx++; } // Increases chunk and packet indexes pck_dp_Idx += (cnk_rd_Stop - cnk_rd_Idx); cnk_rd_Idx = cnk_rd_Stop; } // Not enough words to bulk read a complete channel block, do it step by step else { if (pck_chn_Idx == pck_chn_Count) pck_chn_Idx = 0; // Triggers the transition mode } } //______________________________________________________________________________ void comm_Master::burst_read_Transition() { // Reads the data, chunk transition steps if (burst_read_Flag != comm_Const::brs_err_No) return; // Runs through the chunk step by step while (pck_chn_Idx < pck_chn_Count && cnk_rd_Idx < cnk_rd_Len) { // Debug dbg_Print("comm_Master::burst_read_Transition:()", DBG_LVL_STEP); // Transfer data throug channel connection brs_lyr_Front[pck_Idx * brs_lyr_Count + brs_gmt_Layer[pck_chn_Idx]] [brs_gmt_Front[pck_chn_Idx][lyr_dp_Idx]] = cnk_rd_Buf[cnk_rd_Idx]; // Increases counters cnk_rd_Idx++; pck_chn_Idx++; pck_dp_Idx++; // Increases the channel dp index in case we are completing a layers block if (pck_chn_Idx == pck_chn_Count) lyr_dp_Idx++; } } //______________________________________________________________________________ void comm_Master::burst_read_Footer() { // Checks for a packet/frame end if (burst_read_Flag != comm_Const::brs_err_No) return; // Debug dbg_Print("comm_Master::burst_read_Footer:()", DBG_LVL_STEP); // Reads the footer if all data are ok! if (pck_dp_Idx == pck_dp_Count) { // Transition proof mode word by word reading while (pck_foot_Idx < pck_foot_Count && cnk_rd_Idx < cnk_rd_Len) { // Footer values switch switch (pck_foot_Idx) { // Word #0: error flag case 0: pck_chk_Flag = (UInt_t)(cnk_rd_Buf[cnk_rd_Idx]); break; // Word #1: packet data word MSB 16 bits case 1: pck_chk_Count = ((UInt_t)cnk_rd_Buf[cnk_rd_Idx]) << 16; break; // Word #2: packet data word LSB 16 bits case 2: pck_chk_Count += (UInt_t)(cnk_rd_Buf[cnk_rd_Idx]); break; } // Increases the packet head and chunk buffer reading positions pck_foot_Idx++; cnk_rd_Idx++; } } // If the footer has been read, check for the next packet! if (pck_foot_Idx == pck_foot_Count) { // *** DEBUG // dbg_Print("comm_Master::burst_read_Footer:() end of packet found!", DBG_LVL_STEP); // std::cout << " --> pck_chk_Flag: " << pck_chk_Flag << "\n"; // std::cout << " --> pck_chk_Count: " << pck_chk_Count << "\n"; // *** END DEBUG // Check footer matching if (pck_chk_Flag) burst_read_Flag = comm_Const::brs_err_Mismatch; if (pck_chk_Count != pck_dp_Count) burst_read_Flag = comm_Const::brs_err_Mismatch; // Increases the pck_Count preemptively pck_Idx++; // Triggers the reading of next packet header pck_head_Idx = 0; } } //______________________________________________________________________________ void comm_Master::burst_read_Exit() { // Completes the reading and fixes the errors dbg_Print("comm_Master::burst_read_Exit:()", DBG_LVL_STEP); // Informs about error condition if (burst_read_Flag > 1) { // Something wrong whit packet integrity ccheck switch (burst_read_Flag) { case comm_Const::brs_err_Timeout: cool::Out->Print("COMM: reading timeout\n", LRED); break; case comm_Const::brs_err_Mismatch: cool::Out->Print("COMM: packet mismatch\n", LRED); break; case comm_Const::brs_err_Ask: cool::Out->Print("COMM: failed to send read command\n", LRED); break; } // Show error info cool::Out->Print(" pck_Count: "); cool::Out->Print(pck_Count, WHITE, true); cool::Out->Print(" pck_Idx: "); cool::Out->Print(pck_Idx, WHITE, true); cool::Out->Print(" pck_chn_Count: "); cool::Out->Print(pck_chn_Count, WHITE, true); cool::Out->Print(" pck_chn_dp_Count: "); cool::Out->Print(pck_chn_dp_Count, WHITE, true); cool::Out->Print(" pck_dp_Count: "); cool::Out->Print(pck_dp_Count, WHITE, true); } // Sends a soft reset in case of errors //if (burst_read_Flag > 1) Reset(); //delete cnk_Buf[0]; //delete cnk_Buf[1]; // Reset the flah to allow a new call burst_read_Flag = 0; } //______________________________________________________________________________ int comm_Master::reg_Read(UShort_t reg_ID, UShort_t* buffer, UShort_t len) { // Reads a buffer from the DARK register dbg_Print("comm_Master::reg_Read:(buffer)", DBG_LVL_FLOW); // Define pivot variables int rd_Len = 256; // 256 is the smallest buffer available int rd_tOut = 1000; // Read timeout, ms int wr_Len = 16; // Write buffer can be arbitrary unsigned short wr_Buff[16]; unsigned short rd_Buff[256]; // Sets the write register the read command wr_Buff[0] = comm_Const::frw_mdl_reg_ID; // Firmware const wr_Buff[1] = 0x0000 + reg_ID; // Read bit + Reg ID wr_Buff[15] = 0xFFFF; // Stop Word // Clean read buffer for (int i = 0; i < 256; i++) rd_Buff[i] = 0; // Writes the read command if (comm_USB::Write(dev_ID, wr_Buff, &wr_Len)) { dbg_Print("comm_Master::reg_Read(buffer): error writing read command", DBG_LVL_WARN); return -1; } // Reads the answer if (comm_USB::Read(dev_ID, rd_Buff, &rd_Len, true)) { dbg_Print("comm_Master::reg_Read(buffer): error reading back data", DBG_LVL_WARN); return -1; } // Get the data from the buffer block for (UShort_t i = 0; i < len; i++) buffer[i] = rd_Buff[i]; // Everything ok return 0; } //______________________________________________________________________________ int comm_Master::reg_Read(UShort_t reg_ID, UShort_t word_ID, UShort_t* word_Val) { // Reads a word from the DARK register dbg_Print("comm_Master::reg_Read:(word)", DBG_LVL_FLOW); // Define pivot variables UShort_t reg_Buff[8]; // Check for word ID if ((word_ID < 0) | (word_ID > 7)) return -1; // Reads the answer if (reg_Read(reg_ID, reg_Buff, 8)) { dbg_Print("comm_Master::reg_Read(word): error reading data", DBG_LVL_WARN); return -1; } // Get the word from the buffer block *word_Val = reg_Buff[word_ID]; // Everything ok return 0; } //______________________________________________________________________________ int comm_Master::reg_Write(UShort_t reg_ID, UShort_t* buffer, UShort_t len) { // Writes a buffer (8 words) to the DARK register dbg_Print("comm_Master::reg_Write:(buffer)", DBG_LVL_FLOW); // Define pivot variables unsigned short wr_Buff[16]; int wr_Len = 16; // Assigns register module ID and register address wr_Buff[0] = comm_Const::frw_mdl_reg_ID; // Firmware const wr_Buff[1] = 0x8000 + reg_ID; // Reg ID + Write bit wr_Buff[15] = 0XFFFF; // Stop Word // Fills the write buffer (8 words) for (UShort_t i = 0; i < 8; i++) { if (i <= len) wr_Buff[i + 2] = buffer[i]; else wr_Buff[i + 2] = 0; } // Writes if (comm_USB::Write(dev_ID, wr_Buff, &wr_Len)) { dbg_Print("comm_Master::reg_Write(buffer): error writing data", DBG_LVL_WARN); return -1; } // Everything ok return 0; } //______________________________________________________________________________ int comm_Master::reg_Write(UShort_t reg_ID, UShort_t word_ID, UShort_t* word_Val) { // Writes a single word to the DARK register dbg_Print("comm_Master::reg_Write:(word)", DBG_LVL_FLOW); // Define pivot variables UShort_t reg_Buff[8]; // Check for word ID if ((word_ID < 0) | (word_ID > 7)) return -1; // Read the present register configuration if (reg_Read(reg_ID, reg_Buff, 8)) { dbg_Print("comm_Master::reg_Write(word): error retriving current value", DBG_LVL_WARN); return -1; } // Change the word reg_Buff[word_ID] = *word_Val; // Writes the new buffer if (reg_Write(reg_ID, reg_Buff, 8)) { dbg_Print("comm_Master::reg_Write(word): error writing new value", DBG_LVL_WARN); return -1; } // Everything ok return 0; }