Chapter 54. The SBS Readout framework

This chapter describes a framework for reading out events via the SBS PCI/VME bus bridge interface. This chapter describes

Complete reference information is available in the 3sbsreadout part of this manual.

54.1. SBS Readout concepts

The SBS readout is an application framework. Application frameworks are nice because they already supply the main flow of control. Your job as an application programmer is to fill in the experiment specific pieces of the framework.

The disadvantage of an application framework is that it can be hard to figure out how to get started without a good orientation. This section aims to be that orientation. We will describe the flow of the application and the concepts you'll have to deal with.

Normally you will only need to be concerned with how the framework operates when data taking is active. When data taking is active, An independent thread loops checking a pair of triggers. These triggers are called the event trigger and tthe scaler triger. Each trigger has associated with it a set of code to execute. This code is organized hierarchically as event segments in the case of the event trigger and scaler banks in the case of the scaler trigger.

Triggers and the code that responds to them must be registered with the framework if it is to know about it. The specific triggers themselves must also be registered.

This section therefore will discuss the following objects:

Event Segments. An event segment is a logical unit of data acquisitition. There are two useful base classes: CEventSegment provides a primitive segment. You extend CEventSegment to build a class that actually initializes, reads and clears some digitizer modules. CCompoundEventSegment provides a container for other event segments (including other CCompoundEventSegment objects). CCompoundEventSegment provides the glue that allows you to build an experiment readout from logical chunks.

To use the CEventSegment class, you create a derived class and implement the pure virtual methods of CEventSegment. You can optionally override the virtual methods supplied by CEvenSegment (which are implemented to do nothing) if your electronics requires this.

The virtual methods you can implement are:

void initialize()

This method is called once as data taking transitions to the active state (resume or begin). It is expected to perform any one-time initialization of the hardware managed by this event segment. This includes returning the module to a known state, reading setup files and applying them to the module, enabling data taking and so on.

void clear()

Clears any pending data from the module. This is called after initialize during startup. It is also called after each event has been read. You should implement this if the hardware this segment is reading requires any post readout attention to be made ready for the next event.

void disable()

Called as data taking is being transitioned to an inactive state (paused or ended). This is expected to do any work needed to disable dat taking in the modules managed by this segment.

size_t read( void* pBuffer, size_t maxwords)

Called in response to a trigger. This function is expected to read the hardware directly into pBuffer which has maxwords of 16 bit words available. On return, the actual number of 16 bit words read should be returned.

For reference information on this class see: CEventSegment(3sbsreadout). Closely allied with this is the concept of packets. Packets are supported via the CDocumentedPacket(3sbsreadout). The CEventPacket(3sbsreadout) class extends CEventSegment to provide a base class for event segments that are wrapped in a packet.

Event segments are organized by placing them in CCompoundEventSegment objects. CCompoundEventSegment is itself an CEventSegment and therefore can contain other compounds, which supports a hierarchical organization of the experimental readout.

Ordinarily you will use a CCompoundEventSegment by creating an instance of it and inserting other segments into it. Here are the methods that allow you to manipulate the event segments in a compound event segment.

void AddEventSegment( CEventSegment* pSegment )

Adds an event segment to the end of the ordered collection of event segments in the list. Whenever a method like initialize orread is invoked on a compound event segment it will invoke the corresponding method in the elements it contains in the order in which they were added.

void DeleteEventSegment ( CEventSegment* pSegment )

If pSegment is contained by the compound, it will be removed from the container. If not this method does nothing.

CCompoundEventSegment::EventSegmentIterator begin()

Returns an iterator which 'points' to the beginning of the collection. Refer to the description below for information about iterators.

CCompoundEventSegment::EventSegmentIterator end()

Returns an iterator that points just off the end of the collection. A typical usage pattern is shown below


CCompoundEventSegment seg;
...
CCompoundEventSegment::EventIterator p = seg.begin();
while (p != seg.end()) {
   // Do something with the event segment pointed to by *p
   
   p++;
}
                                

The CCompoundEventSegment::EventSegmentIterator data typeis a pointer like object within the collection of event segments. Dereferencing it gives you a pointer to the event segmet at the current location. Increment advances the iterator to the next element of the collection.

For reference information about the CCompoundEventSegment class see CCompoundEventSegment(3sbsreadout).

The readout software has a single object from the CExperiment(3sbsreadout) class. This object contains a top level CCompoundEventSegment. It also provides a member function AddEventSegment that allows you to add an event segment (or compound event segment) to that top level segment.

See the section: Modifying the skeleton application to meet your needs for information about where to add event segments.

Scaler banks. The Readout program supports a second trigger that reads out scaler banks. Scaler banks usually read out a set of counters that describe the rates in various detectors subsystems and other items of interest. Scaler banks operate in a manner analagous to the event segments we have just described.

The CScaler class is intended to read a single scaler module. CScalerBank is a container for other CScaler objects including other CScalerBank objects. This pair of classes allows you to build a modular hierarchy of scalers to read in response to the scaler trigger. The scaler trigger is assumed to come at a much lower rate than the event trigger and therefore can tolerate alonger deadtime.

CScaler is an abstract base class. You use it by extending it by supplying code for the pure virtual methods, and overriding the other virtual methods you need to override. The virtual methods that are fair game to be overidden are:

void initialize()

Invoked once as the readout program begins taking data. This happens after a begin and after a resume. The code you supply here is supposed to prepare modules for data taking. The base class implementation does nothing

void clear()

Invoked once as data taking becomes active (begin or resume), and after the scaler is read. If something must be done to clear a scaler after it has been read write this method and put that code here. Note that many scalers have a destructive read method and that should be used by preference as it ensures that scaler counts will not be lost (those accepted between read and clear).

void disable()

Called as data taking is halted (due to an end or pause). If a module requires special handling to disable it implement this method.

std::vector<uint32_t> read

Reads the scaler(s) managed by this class. The scalers are returned as an STL vector of 32 bit unsigned values.

The CScalerBank class is a parallel to the CCompoundEventSegment. It is a CScaler that contains other CScaler objects, including CCompoundEventSegment objects.

The following methods allow you to manipulate the container in a CScalerBank object:

void AddScalerModule( CScaler* pScaler )

Adds a scaler module to the end of the ordered container of scaler modules that is managed by this object. The virtual functions that make this module look like a CScaler iterate through the set of scaler objects in the container in the order in which they were added and invoke the corresponding function in those objects.

void DeleteScaler( CScaler* pScaler )

If the scaler pointed to by pScaler is in the container (shallow inclusion), it is removed from the container. Note that the object is not destroyed. IF pScaler is not in the collection this function does nothing.

CScalerBank::ScalerIterator begin()

Returns an iterator that 'points' to the first item in the container. Dereferencing a CScalerBank::ScalerIterator will result in a CScaler*. Incrementing the iterator will make it 'point' to the next object in the container.

CScalerBank::ScalerIterator end()

Returns an iterator that points just off the end of the collection. A typical use for this is:


CScalerBank bank;
...
CScalerBank::ScalerIterator p = bank.begin();
while (p != bank.end()) {
    CScaler* pScaler = *p;
    
    // do something with the scaler.
    
    p++;            // Next scaler.
}
                                

For reference information see CScalerBank

As with event segments, the CExperiment encapsulate as ScalerBank that is the top level of the scaler readout hierarchy. CExperiment exports methods that allow you to add and remove scaler modules (including scaler banks) to this top level scaler bank. See Modifying the skeleton application to meet your needs for iformation about how to use these.

Triggers. We have seen how to build the list of stuff the readout program will read. Triggers determine when the top level event segment and scaler bank are read. In order to make Readout to do anything with these you must supply appropriate trigger objects and register them with the CExperiment object. This registration is described in "Modifying the skeleton application to meet your needs" below.

All triggers are subclasses of CEventTrigger. Several of the commonly used triggers have been defined for you. You can also build custom triggers by extending CEventTrigger or any of the prebuilt classes that are close to what you want.

You write a trigger class by overriding the virtual methods in CEventTrigger supplying code that is appropriate to your needs. You must define and implement operator() which is a pure virtual method of CEventTrigger.

The virtual member functions in the CEventTrigger class are:

void setup()

Called when data taking becomes active (begin or resume). This should perform any initialization of the trigger hardware or software.

void teardown()

Called as data taking halts (end or pause). Any code required to make the trigger hardware or software insenstive to additional triggers should be supplied here.

bool operator()()

This is periodically called and should return true if the trigger actions should be peformed.

The following trigger classes are in the Readout library and can be used simplly by instantiatig and registering them.

CCAENV262Trigger

Triggers using the CAEN V262 module.

CNullTrigger

Trigger that never fires.

CTimedTrigger

Trigger that fires periodically

CV977Trigger

Trigger that uses the CAEN V977 module.

Busys. During the time in which Readout is responding to a trigger it is unable to accept a new trigger. During this time it is often important to veto external electronics until the system is able to accept the next trigger. This is the function of Busy objects.

Busy objects are members of classes that are derived from CBusy which has the following pure virtual members:

void GoBusy

This is called when due to software reasons the computer will be unable to accept a trigger. For example, as the run ends. It is important to note that the actual event by event busy should be generated by hardware and then only cleared by software.

void GoClear

Called when the software is able to accept the next trigger.

The readout framework provides CCAENV262Busy, and CV977Busy as prebuilt busy classes. This, and any busy class you create must be registered to be used. The example below shows the creation and registration of a CV977Busy object to manage the busy. The physicsal module is in VME crate 0 and has a base address of 0x00444400.


#include <CV977Busy.h>
...
void
Skeleton::SetupReadout(CExperiment* pExperiment)
{
   ...
   pExperiment->EstablishBusy(new CV977Busy(0x444400));
   ...
}