AlcapDAQ  1
caenV767.cpp
Go to the documentation of this file.
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 
5 #include <sys/time.h>
6 #include <time.h>
7 
8 #include "midas.h"
9 
10 #include "crate.h"
11 #include "vme.h"
12 #include "odb_wrapper.h"
13 #include "diag.h"
14 
15 INT caenV767_bor();
16 INT caenV767_eor();
17 INT caenV767_poll_live();
18 INT caenV767_read(char *pevent);
19 
21  NULL, // init
22  NULL, // exit
23  NULL, // pre_bor
24  caenV767_bor, // bor
25  caenV767_eor, // eor
26  caenV767_poll_live, // poll_live
27  NULL, // poll_dead
28  NULL, // start_cycle
29  NULL, // stop_cycle
30  caenV767_read, // read
31 };
32 
33 #define CAENV767_MAX_DMA_SIZE 256
34 #define CAENV767_MAX_SIZE (32*1024*4)
35 #define ACTIVE_READOUT_MAX_PER_POLL (2*1024)
36 #define CAENV767_NUM_CHANNELS 128
37 
38 struct caenV767 {
39  bool enabled;
40  bool do_active_readout;
42  struct vme_handle *vme_handle;
43  unsigned long vme_base;
44  char odb_name[20];
45  char bank_name[20];
47  unsigned char *active_readout_buffer;
49  int half_full_level;
50  double bytes_per_second;
51 };
52 
53 struct timeval block_start_time;
54 
55 #define MAX_CAENV767 4
57 
58 // Register offsets
59 #define CAENV767_OUTBUF 0x00
60 #define CAENV767_CTR1 0x10
61 #define CAENV767_STR2 0x48
62 #define CAENV767_EVT_COUNT 0x4C
63 #define CAENV767_OPHAND 0x50
64 #define CAENV767_OPCODE 0x52
65 #define CAENV767_CLEAR 0x54
66 
67 // Reserved value returned in output buffer when the FIFO is empty
68 #define CAENV767_NOT_VALID 0x00600000
69 
70 // Bits in status register 2
71 #define CAENV767_GLOBAL_TDC_ERROR 0x0008
72 
73 /*
74  Complete function listing
75 
76  void caenV767_opcode_write(struct caenV767 *caen, short code);
77  short caenV767_opcode_read(struct caenV767 *caen);
78  INT caenV767_bor1(struct caenV767 *caen)
79  INT caenV767_bor()
80  INT caenV767_eor1(struct caenV767 *caen)
81  INT caenV767_eor()
82  INT caenV767_fifo_read(struct caenV767 *caen, unsigned char *buffer,
83  int max_size, bool in_active_readout)
84  INT caenV767_poll1(struct caenV767 *caen)
85  INT caenV767_poll_live()
86  INT caenV767_read1(struct caenV767 *caen, char *pevent)
87  INT caenV767_read(char *pevent)
88 */
89 
90 /* ********************************************************************* */
91 void caenV767_opcode_write(struct caenV767 *caen, short code)
92 {
93  // Wait for the microcontroller to become ready
94  while(!(vme_read_d16(caen->vme_handle, caen->vme_base | CAENV767_OPHAND) &
95  0x0002)) {
96  ss_sleep(10);
97  }
98 
99  // Wait a little longer
100  ss_sleep(10);
101 
102  // Write the opcode value
103  vme_write_d16(caen->vme_handle, caen->vme_base | CAENV767_OPCODE, code);
104 }
105 
106 /* ********************************************************************* */
108 {
109  // Wait for the microcontroller to become ready
110  while(!(vme_read_d16(caen->vme_handle, caen->vme_base | CAENV767_OPHAND) &
111  0x1)) {
112  ss_sleep(10);
113  }
114 
115  // Wait a little longer
116  ss_sleep(10);
117 
118  // Read the opcode value
119  return vme_read_d16(caen->vme_handle, caen->vme_base | CAENV767_OPCODE);
120 }
121 
122 /* ********************************************************************* */
123 /*
124  * caenV767_bor1
125  *
126  * Initialize one CAENV767 module.
127  */
129 {
130  // Get the VME base address of the module
131  caen->vme_base = odb_get_dword("/Equipment/Crate %d/Settings/%s/vme address",
132  crate_number, caen->odb_name);
133 
134  // Open a VME handle for memory-mapped access
135  struct vme_mapping_ctrl mapping = {
140  };
141 
142  struct vme_handle *handle =
143  vme_open(caen->vme_base, mapping, 0x200, CAENV767_MAX_DMA_SIZE);
144  caen->vme_handle = handle;
145 
146  // Determine whether we're supposed to do active readout
147  caen->do_active_readout =
148  odb_get_bool("/Equipment/Crate %d/Settings/%s/Active Readout mode",
149  crate_number, caen->odb_name);
150 
151  // Determine whether we should record trailing edges, or only leading edges
152  caen->record_trailing_edges =
153  odb_get_bool("/Equipment/Crate %d/Settings/%s/Record trailing edges",
154  crate_number, caen->odb_name);
155 
156  // Request a bus error when DMA transfer reaches end of data
157  vme_write_d16(caen->vme_handle, caen->vme_base | CAENV767_CTR1, 0x0020);
158 
159  // Read the half-full mark and the active readout buffer size
160  caen->half_full_level =
161  odb_get_int("/Equipment/Crate %d/Settings/%s/Half-full level",
162  crate_number, caen->odb_name);
164  odb_get_int("/Equipment/Crate %d/Settings/%s/Active readout buffer size",
165  crate_number, caen->odb_name);
166 
167  // Allocate the active readout buffer
168  caen->active_readout_buffer = new unsigned char[caen->active_readout_buffer_size];
169 
170  // Set various TDC options
171  caenV767_opcode_write(caen, 0x1200); // set start gating mode
172  caenV767_opcode_write(caen, 0x7100); // set DRDY = almost full
173  caenV767_opcode_write(caen, 0x7400); // prepare to set almost full level...
174  caenV767_opcode_write(caen, caen->half_full_level); // ...set almost full level
175  caenV767_opcode_write(caen, 0x4000); // enable readout of start time
176  caenV767_opcode_write(caen, 0x4300); // enable subtraction of start time
177  // from hit times
178 
179  if(caen->record_trailing_edges) {
180  caenV767_opcode_write(caen, 0x6600); // enable both edges on all channels
181  // and start
182  } else {
183  caenV767_opcode_write(caen, 0x6000); // enable only leading edge on all
184  // channels and start
185  }
186 
187  // Enable the CAEN channels according to the ODB settings
188  diag_print(1, "Enabling all channels on %s.\n", caen->odb_name);
189  caenV767_opcode_write(caen, 0x2300); // enable all channels first
190  BOOL channel_enable_array[CAENV767_NUM_CHANNELS], channelstatus;
191  odb_get_boolarray(&channel_enable_array[0], CAENV767_NUM_CHANNELS,
192  "/Equipment/Crate %d/Settings/%s/channels/channel enabled",
193  crate_number, caen->odb_name);
194  // disable each masked channel
195  for (int channel=0; channel<=(CAENV767_NUM_CHANNELS-1); channel++) {
196  channelstatus = channel_enable_array[channel];
197  if (channelstatus == 0) {
198  diag_print(1, "Disabling %s channel %d.\n", caen->odb_name, channel);
199  caenV767_opcode_write(caen, 0x2100 | channel);
200  }
201  }
202 
203  // Reset the TDC
204  vme_write_d16(caen->vme_handle, caen->vme_base | CAENV767_CLEAR, 1);
205 
206  // Clear for the next block
207  caen->active_readout_size = 0;
208 
209  return SUCCESS;
210 }
211 
212 /* ********************************************************************* */
213 /*
214  * caenV767_bor
215  *
216  * Called at the beginning of the run to discover CAENV767 modules
217  * and initialize them.
218  */
220 {
221  // Use the ODB to find any CAENV767 modules
222  for(int j = 0; j < MAX_CAENV767; j++) {
223 
224  bool enabled = false;
225 
226  if (odb_find_key("/Equipment/Crate %d/Settings/CAEN %d", crate_number, j)) {
227  diag_print(1, "ODB says CAEN %d is present, ", j);
228  enabled =
229  odb_get_bool("/Equipment/Crate %d/Settings/CAEN %d/enabled status",
230  crate_number, j);
231  if (enabled) {
232  diag_print(1, "and is enabled. Initializing...\n");
233  } else {
234  diag_print(1, "but is disabled.\n");
235  }
236  }
237 
238  caenV767[j].enabled = enabled;
239 
240  // Set up the name of the MIDAS bank associated with the module
241  sprintf(caenV767[j].bank_name, "CAE%d", j);
242  sprintf(caenV767[j].odb_name, "CAEN %d", j);
243 
244  if(enabled) {
245  caenV767_bor1(&caenV767[j]);
246  }
247  }
248 
249  return SUCCESS;
250 }
251 
252 /* ********************************************************************* */
254 {
255  vme_close(caen->vme_handle);
256  delete[] caen->active_readout_buffer;
257  return SUCCESS;
258 }
259 
260 /* ********************************************************************* */
261 /*
262  * caenV767_eor
263  *
264  * Called at the end of the run to release any resources that may have
265  * been allocated.
266  */
267 INT caenV767_eor()
268 {
269 
270  for(int i = 0; i < MAX_CAENV767; i++) {
271  if(caenV767[i].enabled) {
272  int status = caenV767_eor1(&caenV767[i]);
273  if(status != SUCCESS) {
274  return status;
275  }
276  }
277  }
278 
279  return SUCCESS;
280 }
281 
282 /* ********************************************************************* */
283 INT caenV767_fifo_read(struct caenV767 *caen, unsigned char *buffer,
284  int max_size, bool in_active_readout)
285 {
286  if(!in_active_readout) {
287  int status = vme_dma_read(caen->vme_handle,
288  caen->vme_base | CAENV767_OUTBUF,
289  buffer,
290  max_size);
291  return status;
292  } else {
293  int size = 0;
294 
295  for(int i = 0; i < max_size / sizeof(DWORD); i++) {
296  DWORD word =
298 
299  if((word & CAENV767_NOT_VALID) == CAENV767_NOT_VALID) {
300  break;
301  }
302 
303  *((DWORD *) buffer) = word;
304  buffer += sizeof(DWORD);
305  size += sizeof(DWORD);
306  }
307 
308  return size;
309  }
310 }
311 
312 /* ********************************************************************* */
313 /*
314  * caenV767_poll1
315  *
316  * Performs active readout for a single CAENV767 FIFO.
317  */
319 {
320  // If active readout is not enabled, skip it.
321  if(!caen->do_active_readout) {
322  return SUCCESS;
323  }
324 
325  // Check whether there's anything at all in the buffer.
326 
327  // How many words do we expect?
328  struct timeval now;
329  gettimeofday(&now, NULL);
330  double block_seconds = (now.tv_sec - block_start_time.tv_sec) +
331  1e-6*(now.tv_usec - block_start_time.tv_usec);
332  int expected_words =
333  (int) (caen->bytes_per_second / block_seconds / sizeof(DWORD));
334 
335  // First check how much space we have available in the active
336  // readout buffer.
337  int size_left = caen->active_readout_buffer_size - caen->active_readout_size;
338  int size = MIN(ACTIVE_READOUT_MAX_PER_POLL, size_left);
339  size = MIN(size, expected_words);
340 
341  // Now try to read up to that amount, without using block transfers,
342  // which can potentially drop the last data word.
343  int status =
344  caenV767_fifo_read(caen,
346  size,
347  TRUE);
348 
349  // Check the status
350  if(status >= 0) {
351  caen->active_readout_size += status;
352  } else {
353  diag_print(0, "Status from caenV767_fifo_read is %d for %s\n", status,
354  caen->odb_name);
355  return FE_ERR_HW;
356  }
357 
359  return FE_NEED_STOP;
360  } else {
361  return SUCCESS;
362  }
363 }
364 
365 /* ********************************************************************* */
366 /*
367  * caenV767_poll_live
368  *
369  * Called periodically while a block is active; performs active readout.
370  *
371  * Returns:
372  * - ordinarily 0,
373  * - a request for a "soft stop" end-of-block, or
374  * - an error code
375  */
376 INT caenV767_poll_live()
377 {
378  // If this is our first active readout pass, just remember the time.
379  if(block_start_time.tv_sec == 0 && block_start_time.tv_usec == 0) {
380  return SUCCESS;
381  }
382 
383  for(int i = 0; i < MAX_CAENV767; i++) {
384  if(caenV767[i].enabled) {
385  int status = caenV767_poll1(&caenV767[i]);
386  if(status != SUCCESS) {
387  return status;
388  }
389  }
390  }
391 
392  return SUCCESS;
393 }
394 
395 /* ********************************************************************* */
396 /*
397  * caenV767_read1
398  *
399  * Constructs the MIDAS bank for a single CAENV767.
400  */
401 INT caenV767_read1(struct caenV767 *caen, char *pevent)
402 {
403  // Create the MIDAS bank
404  DWORD *pdata;
405  bk_create(pevent, caen->bank_name, TID_DWORD, &pdata);
406 
407  // Copy data that was read during the active readout
408  int active_size = caen->active_readout_size;
409  memcpy(pdata, caen->active_readout_buffer, active_size);
410 
411  // Read any data remaining in the module
412  int status =
413  caenV767_fifo_read(caen, ((unsigned char *) pdata) + active_size,
415 
416  // Check the status
417  int final_size = 0;
418  if(status >= 0) {
419  final_size = status;
420  } else {
421  // handle the error
422  }
423 
424  // Check the TDC status for error codes (typically "DLL unlocked" problems)
425  // and add an error flag word if a problem is found.
426  short str2 = vme_read_d16(caen->vme_handle, caen->vme_base | CAENV767_STR2);
427  if(str2 & CAENV767_GLOBAL_TDC_ERROR) {
428  DWORD flag_word = CAENV767_NOT_VALID;
429  for(int tdc = 0; tdc < 4; tdc++) {
430  short ind_tdc_error_mask = (1 << (12+tdc));
431  if(str2 & (1 << (12+tdc))) {
432  flag_word |= (1 << tdc);
433  }
434  }
435 
436  pdata[(active_size + final_size)/sizeof(DWORD)] = flag_word;
437  final_size += sizeof(DWORD);
438 
439  diag_print(0, "TDC error in %s: 0x%08x\n", caen->odb_name, flag_word);
440  }
441 
442  // Close the bank
443  bk_close(pevent, pdata + (active_size + final_size)/sizeof(DWORD));
444 
445  // Clear for the next block
446  caen->active_readout_size = 0;
447 
448  // Reset the TDC
449  vme_write_d16(caen->vme_handle, caen->vme_base | CAENV767_CLEAR, 1);
450 
451  // Update the expected rate.
452  if(block_start_time.tv_sec != 0) {
453  struct timeval now;
454  gettimeofday(&now, NULL);
455  double block_seconds = (now.tv_sec - block_start_time.tv_sec) +
456  1e-6*(now.tv_usec - block_start_time.tv_usec);
457  caen->bytes_per_second = (active_size + final_size)/block_seconds;
458  }
459  block_start_time.tv_sec = block_start_time.tv_usec = 0;
460 
461  return SUCCESS;
462 }
463 
464 /* ********************************************************************* */
465 /*
466  * caenV767_read
467  *
468  * Called at the end of a block to assemble data from that block into a
469  * MIDAS event.
470  */
471 INT caenV767_read(char *pevent)
472 {
473  for(int i = 0; i < MAX_CAENV767; i++) {
474  if(caenV767[i].enabled) {
475  int status = caenV767_read1(&caenV767[i], pevent);
476  if(status != SUCCESS) {
477  return status;
478  }
479  }
480  }
481 
482  return SUCCESS;
483 }
484 
485 /* ********************************************************************* */