From: <ro...@us...> - 2011-10-21 14:44:34
|
Revision: 2562 http://nscldaq.svn.sourceforge.net/nscldaq/?rev=2562&view=rev Author: ron-fox Date: 2011-10-21 14:44:26 +0000 (Fri, 21 Oct 2011) Log Message: ----------- Make the packet/vpacket macros work without the existence of a daq_isJumboBuffer() Modified Paths: -------------- trunk/nextgen/ChangeLog trunk/nextgen/configure.ac trunk/nextgen/docbuild/tutorial.xml trunk/nextgen/sbs/vmemodules/camac.h Modified: trunk/nextgen/ChangeLog =================================================================== --- trunk/nextgen/ChangeLog 2011-10-18 17:58:18 UTC (rev 2561) +++ trunk/nextgen/ChangeLog 2011-10-21 14:44:26 UTC (rev 2562) @@ -91,5 +91,8 @@ October 18, 2011 10.0-015 - Pull in up to date scaler display program from 8.2..which diverged after this forked off. +October 21, 2011 10.0-016 + - Fix Packet/Vpacket macros so that they generate compatibility + (word size-field) packets not checking for jumbo-bufferness. Modified: trunk/nextgen/configure.ac =================================================================== --- trunk/nextgen/configure.ac 2011-10-18 17:58:18 UTC (rev 2561) +++ trunk/nextgen/configure.ac 2011-10-21 14:44:26 UTC (rev 2562) @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. AC_PREREQ(2.61) -AC_INIT(nscldaq, 10.0-015, fo...@ns...) +AC_INIT(nscldaq, 10.0-016, fo...@ns...) AC_CONFIG_SRCDIR([/utilities/StringsToIntegers.h]) AC_CONFIG_HEADER([config.h]) AC_CONFIG_MACRO_DIR([m4]) Modified: trunk/nextgen/docbuild/tutorial.xml =================================================================== --- trunk/nextgen/docbuild/tutorial.xml 2011-10-18 17:58:18 UTC (rev 2561) +++ trunk/nextgen/docbuild/tutorial.xml 2011-10-21 14:44:26 UTC (rev 2562) @@ -349,12 +349,6 @@ class CAENcard; <co id="CAENcardForwardClass" /> -/*! - This is a minimal support class that read a CAEN 792 module in the - simplest way possible as a test data source for nextgen NSCLDAQ - - -*/ class CCAENEventSegment : public CEventSegment <co id="CEventSegmentDerivied" /> { private: @@ -417,7 +411,7 @@ object will be used to manipulate the CAEN digitizer. </para> </callout> - <calllout arearefs="EVSConstructor"> + <callout arearefs="EVSConstructor"> <para> Normally your event segments will want to implement a constructor. In this case we provide the @@ -425,7 +419,7 @@ base address, virtual slot number (<parameter>id</parameter>). and VME crate number. </para> - </calllout> + </callout> </calloutlist> <para> Lets look at the implementation (.cpp) file of the event @@ -447,34 +441,2982 @@ </programlisting> </example> - + <calloutlist> + <callout arearefs="config.hInclude"> + <para> + All implementation code you write in RingDaq should + include <filename>config.h</filename> as the first + header. This file provides definitions that other + RingDaq headers may need. + </para> + </callout> + <callout arearefs="config.hInclude"> + <para> + Since this file will implement the + <classname>CCAENEventSegment</classname> class it + needs access to the header so that method prototypes + and member data definitions are available to + method implementations. + </para> + </callout> + <callout arearefs="CAENCard.hInclude"> + <para> + This satisfies the forward reference to the + <classname>CAENcard </classname> class + we made in the header. Since <classname>CAENEventSegment</classname> + is going to call <classname>CAENcard</classname> methods, + we'll the compiler will need the actual class + definition. + </para> + </callout> + <callout arearefs="EventSegment_otherIncludes"> + <para> + The headers below are standard C/C++ headeres + that define funtions and classes we will + use in the implementation of this class. + </para> + </callout> + </calloutlist> + <para> + The next code section we will look at contains the + constructor and destructor of the event segment. The constructor + is invoked when the event segment is created (usually in the + <filename>Skeleton.cpp</filename> just prior to registration). + The destructor is usually never invoked. However if you have + some overarching event segment that, at initialization time, + creates other event segments it contains, destructors may be + called. In order to allow your code to be embedded in environments + you don't initially anticipate, you should write correct + constructors for all event segments. + </para> + <example> + <title>Event segment constructor and destructor</title> + <programlisting> +CCAENEventSegment::CCAENEventSegment(uint32_t base, uint8_t id, + int packet, int crate) : + m_pCard(new CAENcard(id, 0, false, base)) <co id="newCAENcard" /> +{ + +} + +CCAENEventSegment::~CCAENEventSegment() +{ + delete m_pCard; <co id="deleteCAENcard" /> +} + + </programlisting> + </example> + <calloutlist> + <callout arearefs="newCAENcard"> + <para> + Initializes the <varname>m_pCare</varname> member data + with a pointer to a <classname>CAENcard</classname> + object that will manage the CAEN tdc we are + operating with. + </para> + <para> + At this time, no operations are perfomed on the + device itself as we've not yet been asked to + initialize it. + </para> + </callout> + <callout arearefs="deleteCAENcard"> + <para> + If we are ever destroyed we must delete the + <classname>CAENcard</classname> object the constructor + created or memory and SBS mapping resources will + be leaked for each construction/destruction cycle. + </para> + </callout> + </calloutlist> + <para> + Next lets look at the initialization code. In a production + environment, this code might open a file and read some configuration + data, using that data to figure out how to initialize the + device. In keeping with showing the simplest code possible, + we are going to hard code all configuration information. + </para> + <example> + <title>Event segment <methodname>initialize</methodname> implementation</title> + <programlisting> +void +CCAENEventSegment::initialize() +{ + m_pCard->reset(); <co id="CEVSReset" /> + sleep(2); + for(int i =0; i < 32; i++) { <co id="CEVSThresholds" /> + m_pCard->setThreshold(i, 0); + } + m_pCard->commonStart(); <co id="CEVSTDCConfig" /> + m_pCard->keepOverflowData(); + m_pCard->keepUnderThresholdData(); + m_pCard->setRange(0x1e); + + +} + </programlisting> + </example> + <calloutlist> + <callout arearefs="CEVSReset"> + <para> + Prior to doing anything to the TDC it is reaset. + after being reset it is necessary to wait a bit for the + TDC to become ready for programming. The + <function>sleep</function> is probably somewhat longer + than required. A call to <function>usleep</function> + for a few milliseconds is probably more appropriate. + </para> + </callout> + <callout arearefs="CEVSThresholds"> + <para> + Since we are going to accept overflow and underthreshold + data, the thresholds are just set to zero. Normally you + would process some configuration file at this point to + determine actual threshold values to program. + </para> + </callout> + <callout arearefs="CEVSTDCConfig"> + <para> + This section of code programs the remainder of the + TDC configuration. + </para> + </callout> + </calloutlist> + <para> + The <methodname>clear</methodname> function is trivial: + </para> + <example> + <title>Event segment <methodname>clear</methodname> implementation</title> + <programlisting> +CCAENEventSegment::clear() +{ + m_pCard->clearData(); +} + </programlisting> + </example> + <para> + The heart of the event segment is, of course, the code + that reads out the module: + </para> + <example> + <title>Event segment <methodname>read</methodname> method implementation</title> + <programlisting> +size_t +CCAENEventSegment::read(void* pBuffer, size_t maxwords) +{ + // Maximum number of words is 34*2: + + if (maxwords < 34*2 ) { <co id="checkMaxWordsOk" /> + throw + std::string( + "CCAENEventSegment - insufficient buffers space for worst case event"); + } + + for (int i =0; i < 30; i++) { + if(haveEvent()) break; <co id="waitForData" /> + } + int n = (m_pCard->readEvent(p))/sizeof(uint16_t); <co id="readData" /> + + + return n; <co id="CEVSnReturn" /> +} +bool +CCAENEventSegment::haveEvent() <co id="haveEventImpl" /> +{ + return m_pCard->dataPresent(); +} + + </programlisting> + </example> + <calloutlist> + <callout arearefs="checkMaxWordsOk"> + <para> + When <methodname>read</methodname> is called it is + given a pointer that describes where to put data + that has been read; <parameter>pBuffer</parameter> + and a <type>size_t</type> <parameter>maxwords</parameter> + that describes the amount of space remaining in the + buffer in words (<type>uint16_t</type> sized units). + This code ensures that the maximum TDC event size + will fit in the remaining buffer space, throwing + an exception if it won't. + </para> + <para> + The computation of the largest TDC event size comes + from the fact that the TDC has 32 channels, that each event + will have a header and trailer, and that each item will + be a <type>uint32_t</type>, which uses two + <type>uint16_t</type> units of storage. + </para> + </callout> + <callout arearefs="waitForData"> + <para> + It is possible the trigger latency will be shorter than + the conversion time of the TDC. In this loop + we wait for the module to have an event's worth of data. + If it never does after 30 tests (each test will be about + 2 μsecs), the loop exits anyway. + </para> + </callout> + <callout arearefs="readData"> + <para> + Reads an event worth of data from the module. + </para> + </callout> + <callout arearefs="CEVSnReturn"> + <para> + The read function returns the number of bytes of data + read from the module. Therefore, + <varname>n</varname> is the number of + <type>uint16_t</type> words read. That value is returned. + </para> + </callout> + <callout arearefs="haveEventImpl"> + <para> + This is a convenience function to determine if the + module has an event. + </para> + </callout> + </calloutlist> + <para> + Before leaving the subject of event segments for the scaler + readout, I want to touch on two other important classes + you can use to help you organize your code: + </para> + <variablelist> + <varlistentry> + <term><classname>CCompoundEventSegment</classname></term> + <listitem> + <para> + The <classname>CCompoundEventSegment</classname> + class is an event segment that consists of + an ordered list of event segments (including + other compound event segments). + </para> + <para> + This can be used to organize a detector system + that consists of many detector elements + into a single event segment you can hand to your + users + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><classname>CEventPacket</classname></term> + <listitem> + <para> + NSCL events are often broken into packets. + A packet consists of a header that has a + size (note in RingDaq the size is a + <type>uint32_t</type> while in SPDAQ, + it is a <type>uint16_t</type>), a tag, and following + the header, the payload of the packet. + </para> + <para> + <classname>CEventPacket</classname> + allows you to wrap an existing event + segment (including a compound event segment), + in a packet without the event segment knowing it's + being wrapped. + </para> + </listitem> + </varlistentry> + </variablelist> + <para> + Let's look at a simple example of a compound event segment. + Earlier in this section, we've built a class; + <classname>CCAENEventSegment</classname> that encapsulated + a CAEN 32 channel TDC. Suppose we had an experiment + that consisted of several of these TDCs (I know it's a bad + example but it prevents me from having to build additional + event segments and all I really want to show is the mechanics + of using compound event segments.) + </para> + <para> + We might have code the builds a Compound event segment as + follows: + </para> + <example> + <title>Using a compound event segment</title> + <programlisting> +... + // Create a few CCAENEventSegment objects: + CCAENEventSegment tdc1(0x10000000, 1); + CCAENEventSegment tdc2(0x10010000, 2); + CCAENEventSegment tdc3(0x10020000, 3); + + // Create a compound event segment that reads out tdc1, tdc2, tdc3 in order: + + CCompoundEventSegment tdcs; + tdcs.AddEventSegment(&tdc1); + tdcs.AddEventSegment(&tdc2); + tdcs.AddEventSegment(&tdc3); +... + </programlisting> + </example> + <para> + The key to this example is that a <classname>CCompoundEventSegment</classname> + has a method named <methodname>AddEventSegment</methodname> whose + parameter is a pointer to an existing event segment. This + creates an ordered list of event segments. + </para> + <para> + Since a <classname>CCompoundEventSegment</classname> is itself + an event segment it can be added to another + <classname>CCompoundEventSegment</classname> as well. + Continuing the example above: + </para> + <informalexample> + <programlisting> +... + // Assume we've made more event segments named adcs and qdcs: + + CCompoundEventSegment myDetector; + myDetector.AddEventSegment(&tdcs); + myDetector.AddEventSegment(&adcs); + myDetector.AddEventSegment(&qdcs); +... + </programlisting> + </informalexample> + <para> + The event segment hierarchy you build up can be as deep as you + need to capture the needs of your detector system. + </para> + <para> + Suppose now that <varname>myDetector</varname> in the previous + example is a know NSCL detector that has been assigned a packet + id of 0x1234. We can now create an event segment that + wraps our detector in that packet by using a + <classname>CEventPacket</classname> as follows: + </para> + <example> + <title>Using <classname>CEventPacket</classname></title> + <programlisting> +... + CEventPacket myDetectorPacket(myDetector, 0x1234, + "My Detector", + "Packet for the My Detector device" + "V1.0"); + </programlisting> + </example> + <para> + It's pretty easy to understand what the first two parameters + are, a reference to an event packet and the tag. + The remaining information are bundled into a documentation + event emitted at the start of run that describe the set of + event packets that you can expect to see in the run. + Each documenation event contains a set of strings, one for + each packet you created. Each string is a colon separated + set of fields consisting of the short name of the packet + (<literal>My Detector</literal> above), + The stringified packet id (<literal>0x1234</literal>) as a + hexadecimal, + a long name of the + packet (<literal>Packet for the My Detector device</literal> + above), and a packet version which should be changed whenever + the format of the packet changes (<literal>V1.0</literal> + in the example above), and the date and time the packet object + was constructed (e.g. <literal>Tue Oct 18 15:32:20 2011</literal>). + </para> + <para> + If you want to see what the description string looks like, you can + call the <methodname>Format</methodname> method of the + <classname>CEventPacket</classname> object. + </para> </section> <section> <title>Creating scaler banks and scaler modules</title> - <para></para> + <para> + The readout framework supports a hierachical organization of + scalers using two classes <classname>CScaler</classname> which + is intended to represent a single scaler module (but need not), + and <classname>CScalerBank</classname> a <classname>CScaler</classname> + into which <classname>CScaler</classname> objects including + <classname>CScalerBank</classname> objects can be put. + </para> + <para> + Thus a <classname>CScalerBank</classname> is to <classname>CScaler</classname> + as a <classname>CCompoundEventSegment</classname> is to + <classname>CEventSegment</classname> objects. + </para> + <para> + The interface of a <classname>CScaler</classname> is similar to that + of a <classname>CScaler</classname>: + </para> + <variablelist> + <methodsynopsis> + <type>void</type> <methodname>initialize</methodname> <void /> + </methodsynopsis> + <methodsynopsis> + <type>void></type> <methodname>clear</methodname><void /> + </methodsynopsis> + <methodsynopsis> + <type>void</type> <methodname>disable</methodname> <void /> + </methodsynopsis> + <methodsynopsis> + <type>std::vector<uint32_t></type> <methodname>read</methodname> <void /> + </methodsynopsis> + <varlistentry> + <term><methodname>read</methodname></term> + <listitem> + <para> + All other methods are completely analagous + to their counterparts in <methodname>CEventSegment</methodname>. + <methodname>read</methodname> is as well, however + it is supposed to return a vector of <type>uint32_t</type> + values that are the scalers it reads. + </para> + <para> + The framework will append this set of values to + the final set of scalers it will report to the + RingDaq. + </para> + </listitem> + </varlistentry> + </variablelist> + <para> + So lets look at a simple <classname>CScaler</classname>. In + this case we will be building a <classname>CScaler</classname> + class that manages a single SIS 3820 scaler module. + </para> + <para> + First the header for the class: + </para> + <example> + <title><classname>CScaler</classname> header</title> + <programlisting> + +#include <CScaler.h> + + +class CSIS3820; + +class CSIS3820Scaler : public CScaler <co id="DerivedFromCScaler" /> +private: + CSIS3820* m_pScaler; <co id="CSIS3820-memberpointer" /> + +public: + CSIS3820Scaler(unsigned long base, int crate = 0); <co id="CIS3820Scaler-ConstructorDecl" /> + ~CSIS3820Scaler(); + + + // The interface required by the CScaler class: + +public: <co id="CSIS3820Scaler-override-decls" /> + virtual void initialize(); + virtual void clear(); + virtual std::vector<uint32_t> read(); + + +}; + + </programlisting> + </example> + <calloutlist> + <callout arearefs="DerivedFromCScaler"> + <para> + Much of this code should look familiar from the' + <classname>CEventSegment</classname> discussion. + The <classname>CSIS3820Scaler</classname> class must + be derived from the <classname>CScaler</classname> class + in order to be registered to read scalers with the + readout framework. + </para> + </callout> + <callout arearefs="CSIS3820-memberpointer"> + <para> + This member will be a pointer to a + <classname>CSIS3820</classname> object that will + be used to talk to the scaler module we are reading. + </para> + </callout> + <callout arearefs="CIS3820Scaler-ConstructorDecl"> + <para> + The constructor declaration provides the ability for + us to specify the base address an the VME crate in which + the scaler is installed. Note that as for the + <classname>CEventSegment</classname> since we are going + to dynamically create the scaler module class, we need + to declare a destructor to allow us to destroy it when + our object is destroyed. + </para> + </callout> + <callout arearefs="CSIS3820Scaler-override-decls"> + <para> + We must declare the functions from the + <classname>CScaler</classname> base class that will + be implemented by <classname>CSIS3820Scaler</classname>. + This must be at least the <methodname>read</methodname> + method as that is abstract in <classname>CScaler</classname>. + </para> + </callout> + </calloutlist> + <para> + As before, let's look at the implementation in logical pieces. + The front matterr is a few <literal>#include</literal> directives + to pull in the headers we need: + </para> + <example> + <title><classname>CSIS3820Scaler</classname> implementation includes section</title> + <programlisting> +#include <config.h> +#include "CSIS3820Scaler.h" +#include <CSIS3820.h> +#include <iostream> +#include <vector> + </programlisting> + </example> + <para> + The constructor and destructor are pretty simple as well: + </para> + <example> + <title><classname>CSIS3820Scaler</classname> constructor/destructor</title> + <programlisting> +CSIS3820Scaler::CSIS3820Scaler(unsigned long base, int crate) : + m_pScaler(new CSIS3820(base, crate)) <co id="newCSIS3820" /> +{ + + +} + + +CSIS3820Scaler::~CSIS3820Scaler() +{ + delete m_pScaler; <co id="deleteCSIS3820" /> + +} + + </programlisting> + </example> + <calloutlist> + <callout arearefs="newCSIS3820"> + <para> + Initializes the <varname>m_pScaler</varname> + attribute with a pointer to a dynamically created + <classname>CSIS3820</classname> object. + </para> + </callout> + <callout arearefs="deleteCSIS3820"> + <para> + Since the constructor created an object dynamically, + the destructor is responsible for <literal>delete</literal>ing + it. + </para> + </callout> + </calloutlist> + <para> + Next lets look at the <methodname>initialize</methodname> and + <methodname>clear</methodname> methods as they are relatively + short. + </para> + <example> + <title><classname>CSIS3820Scaler</classname> initialize/clear methods</title> + <programlisting> +void CSIS3820Scaler::initialize() +{ + m_pScaler->setOperatingMode(CSIS3820::LatchingScaler); <co id="CSIS3820Scaler-latchmode" /> + m_pScaler->setLatchSource(CSIS3820::LatchVMEOnly); + m_pScaler->EnableClearOnLatch(); <co id="CSIS3820Scaler-rdclear" /> + m_pScaler->Enable(); <co id="CSIS3820Scaler-start" /> + m_pScaler->Arm(); + +} + +void CSIS3820Scaler::clear() +{ + m_pScaler->ClearChannels(); <co id="CSIS3820Scaler-clear" /> +} + + </programlisting> + </example> + <calloutlist> + <callout arearefs="CSIS3820Scaler-latchmode"> + <para> + Sets the scaler to latch mode with the + VME being the only source of the latch signal. For latching + scalers this is normally the appropriate way to read them. + Latch mode allows all scaler values to be "simultaneously" + recorded for readout at a single snapshot in time. + </para> + </callout> + <callout arearefs="CSIS3820Scaler-rdclear"> + <para> + This function sets the scaler to clear its external counters + when they are latched to the internal module memory. See, + however the discussion about clearing below. + </para> + </callout> + <callout arearefs="CSIS3820Scaler-start"> + <para> + Enabling and arming the scalers is what starts them + counting input pulses. + </para> + </callout> + <callout arearefs="CSIS3820Scaler-clear"> + <para> + This clears all of the external counters of the module. + </para> + </callout> + </calloutlist> + <note> + <title>Clears</title> + <para> + The way in which this module is being cleared is not techincally + correct. The module supports a clear on latch. As coded, + the scalers will be cleared a read time and then a bit later + when <methodname>clear</methodname> is called. This + can cause a few counts to be lost over the duration of the + run. + </para> + <para> + The correct way to handl this module is to clear it + at initialization time and then not to provide a + <methodname>clear</methodname> function. The code + was written this way in order to illustrate the use + of a <methodname>clear</methodname> method rather than + to be strictly speaking correct. + </para> + </note> + <para> + The <classname>std::vector</classname> class makes it pretty + easy to write the <methodname>read</methodname> method as well. + </para> + <example> + <title><classname>CSIS3820Scaler</classname> read</title> + <programlisting> +std::vector<uint32_t> +CSIS3820Scaler::read() +{ + std::vector<uint32_t> result; <co id='CSIS3820Scaler_readResultVector' /> + uint32_t channels[32]; <co id="CSIS3820Scaler_readResultArray" /> + + m_pScaler->LatchAndRead(reinterpret_cast<unsigned long*>(channels)); <co id="CSIS3820_readout" /> + + result.insert(result.begin(), channels, channels + 32); <co id="CSIS3820_readToVector" /> + + return result; + + +} + + </programlisting> + </example> + <calloutlist> + <callout arearefs='CSIS3820Scaler_readResultVector'> + <para> + The <methodname>read</methodname> method must return an + <classname>std::vector</classname>. This declares + storage for that vector. + </para> + </callout> + <callout arearefs="CSIS3820Scaler_readResultArray"> + <para> + The <methodname>LatchAndRead</methodname> function + we use below will read the 32 channels of the module into + an ordinary storage array. Therefore wwe need to declare + one here to hold that output. + </para> + </callout> + <callout arearefs="CSIS3820_readout"> + <para> + Reads the data in to <varname>channels</varname> + </para> + </callout> + <callout arearefs="CSIS3820_readToVector"> + <para> + The <methodname>std::vector::insert</methodname> + method is then used to put the scaler channel values + into the result vector which is returned. + </para> + </callout> + </calloutlist> + <para> + Before leaving this discussion of scalers lets take a short + tour of the <classname>CScalerBank</classname> class. + This class allows us to group several <classname>CScaler</classname> objects + into a single scaler-like object. + </para> + <para> + Suppose we have several SIS 3820 scaler modules. In a detector + system. The example below organizes them into a single + scaler bank which we can hand out as our detector system's scaler. + </para> + <example> + <title>Creating a scaler bank</title> + <programlisting> +... + CSIS3820Scaler sc1(0x80000000); + CSIS3820Scaler sc2(0x80010000); + CSIS3820Scaler sc3(0x80020000); + + CScalerBank myDetectorScalers; + myDetectorScalers.AddScalerModule(&sc1); + myDetectorScalers.AddScalerModule(&sc2); + myDetectorScalers.AddScalerModule(&sc3); +... + </programlisting> + </example> + <para> + Naturally since, <classname>CScalerBank</classname> objects + are themselves derived from <classname>CScaler</classname> they + can be added, in turn, to other scaler banks. + </para> </section> <section> <title>Tying the pieces together</title> <para> + Now that we have an event segment and a scaler module, + we must tie this all together into a readout program by + registering the appropriate objects with the readout framework. + In this section we will: </para> + <itemizedlist> + <listitem> + <para> + Show how to select and specify the event trigger. + </para> + </listitem> + <listitem> + <para> + Show how to create and register a documented packet + from a few <classname>CCAENEventSegment</classname> objects + to respond to the event trigger. + </para> + </listitem> + <listitem> + <para> + Show how to create and register several + <classname>CSIS3820Scaler</classname> objects, organize + them as a scaler bank and register them to be read when + the scaler trigger fires. In the process we will also + point out how to modify the scaler trigger. + </para> + </listitem> + </itemizedlist> + <para> + All of these operations involve editing the + <filename>Skeleton.cpp</filename> file that was distributed + as part of the skeleton. + </para> + <section> + <title>Specifying the event trigger.</title> + <para> + In this section we'll look at how to specify the + event trigger for the readout framework. + Really we need to specify two things. How to + know when an event should be read, and how to indicate + to the electronics when the event readout is complete. + </para> + <para> + Each readout must specify an object that is a + <classname>CEventTrigger</classname> as the trigger object. + This object is repeatedly asked if an event is ready to be read. + Each readout may optionally specify an object that is a + <classname>CBusy</classname> object. The readout framework + interacts with that object (if specified) to determine + how to indicate to the electronics that additional triggers + can be responded to. + </para> + <para> + While you can write your own event trigger and busy classes, + the framework comes with support for the + CAEN V262 and CAEN V977 as trigger/busy electronics in the form + of Busy and trigger classes. See the reference information + for more about those. For now, we will set up our readout + to trigger and report busy-ness via the CAEN V262 module. + </para> + <example> + <title>Sepcifying the trigger/busy</title> + <programlisting> +#include <config.h> +#include "Skeleton.h" +#include <CExperiment.h> +#include <TCLInterpreter.h> +#include <CTimedTrigger.h> + +#include "CCAENEventSegment.h" + + +#include "CSIS3820Scaler.h" + +#include <CCAENV262Trigger.h> <co id="CV262_Includes" /> +#include <CCAENV262Busy.h> + +... +void +Skeleton::SetupReadout(CExperiment* pExperiment) +{ +... + pExperiment->EstablishTrigger(new CCAENV262Trigger(0x444400, 0) ); <co id="CV262_trigger" /> + pExperiment->EstablishBusy(new CCAENV262Busy(0x444400, 0)); <co id="CV262_busy" /> + +... +} + + + </programlisting> + </example> + <calloutlist> + <callout arearefs="CV262_Includes"> + <para> + This header and the next define the + <classname>CV262Trigger</classname> and + <classname>CV262Busy</classname> classes which we will + be using as trigger and busy classes respectively. + </para> + </callout> + <callout arearefs="CV262_trigger"> + <para> + This line of code creates a new + <classname>CV262Trigger</classname> object + for a module with base address of <literal>0x444400</literal> + in VME crate 0. This is the traditional location of this + module in the NSCL DAQ. The all to the + <methodname>EstablishTrigger</methodname> method + of the <classname>CExperiment</classname> object + makes this trigger module the experiment event + trigger. + </para> + </callout> + <callout arearefs="CV262_busy"> + <para> + Similarly, this line creates a + <classname>CV262Busy</classname> object at the same + VME base address and establishes it as the module + that will handle and maintain the program's + busy state. + </para> + </callout> + </calloutlist> + </section> + <section> + <title>Specifying what is read out by an event trigger</title> + <para> + The <methodname>Skeleton::SetupReadout</methodname> method of + the skeleton is also where event segment should be registered. + You can imagine the <classname>CExperiment</classname> as a + <classname>CCompoundEventSegment</classname> in the sense that + it implements the <methodname>AddEventSegment</methodname> method. + This method allows you to specify the set of event segments + you want to respond to the event trigger. + </para> + <para> + <classname>CExperiment</classname> and + <classname>CCompoundEventSegment</classname> invoke corresopnding + methods of the event segments added to them in the order + in which they were registered. This allows you to control + the exact sequence in which event segments put their data + into the output event. + </para> + <para> + The sample code fragments below: + <orderedlist> + <listitem> + <para> + Build a compound event segment from several + <classname>CCAENEventSegment</classname> objects + </para> + </listitem> + <listitem> + <para> + Wraps the compound event segment into a + <classname>CEventPacket</classname> and + adds it to the readout. + </para> + </listitem> + <listitem> + <para> + Wraps a single <classname>CCAENEventSegment</classname> + object in a <classname>CEventPacket</classname> + and adds that to the readout as well. + </para> + </listitem> + </orderedlist> + </para> + <example> + <title>Adding event segments to the experiment</title> + <programlisting> +... +#include "CCAENEventSegment.h" +#include <CCompoundEventSegment.h> <co id="EVSRegsiter_headers" /> +#include <CEventPacket.h> +... +void +Skeleton::SetupReadout(CExperiment* pExperiment) +{ +... + CCAENEventSegment* pTdc0 = new CCAENEventSegment(0x10000000, 0); + CCAENEventSegment* pTdc1 = new CCAENEventSegment(0x11000000, 1); + CCAENEventSegment* pTdc2 = new CCAENEventSegment(0x12000000, 2); <co id="EVSRegister_simplesegs" /> + CCAENEventSegment* pTdc3 = new CCAENEventSegment(0x13000000, 3); + CCAENEventSegment* pTdc4 = new CCAENEventSegment(0x14000000, 4); + CCAENEventSegment* pTdc5 = new CCAENEventSegment(0x15000000, 5); + + CCompountEventSegment* pCompound = new CCompoundEventSegment; <co id="EVSRegister_MakeCompound" /> + pCompound->AddEventSegment(pTdc0); + pCompound->.AddEventSegment(pTdc1); + pCompound->AddEventSegment(pTdc2); + pCompound->AddEventSegment(pTdc3); + pCompound->AddEventSegment(pTdc4); + + pExperiment->AddEventSegment(new CEventPacket(*pCompound, <co id="EVSRegister_Registercompound" /> + 0xff01, "Compound", + "Sample compound event segment", + "V1.0")); + pExperiment->AddEventSegment(new CEventPacket(*pTdc5, <co id="EVSRegister_RegisterSimple" /> + 0xff02, "Simple", + "Sample simple event segment", + "V1.0")); +.. +} +... + </programlisting> + </example> + <calloutlist> + <callout arearefs="EVSRegsiter_headers"> + <para> + These headers must be included to get the class + definitions we need for our readout definitions. + The <filename>CCAENEventSegment.h</filename> header + is assumed to hold the class definitions for the + <classname>CCAENEventSegment</classname> we developed + earlier in the chapter. + </para> + </callout> + <callout arearefs="EVSRegister_simplesegs"> + <para> + Creates the event segments that manage the individual + TDC modules. Each module is given a unique + virtual slot number and base address. + </para> + </callout> + <callout arearefs="EVSRegister_MakeCompound"> + <para> + Creates a compound event segment that contains + the first 5 TDC events egments (<varname>pTdc0</varname> + through <varname>pTdc4</varname>). + </para> + </callout> + <callout arearefs="EVSRegister_Registercompound"> + <para> + Wraps the compound event segment in a packet whose + id will be 0xff01 and whose short name is + <literal>Compound</literal>. The event segment is + added as the first segment to be read by the + readout framework in response to a trigger. + </para> + </callout> + <callout arearefs="EVSRegister_RegisterSimple"> + <para> + Wraps <varname>pTdc5</varname> in a packet + whose id will be <literal>0xff02</literal> and + whose short name will be <literal>Simple</literal>. + This event packet is added as the second segment to + be read in response to a trigger. + </para> + </callout> + </calloutlist> + <para> + To conclude this section, let's look at how the software + responds to an event trigger. + </para> + <para> + When the trigger fires, + The Readout framework will first invoke the <methodname>read</methodname> + method of <literal>Compound</literal> event packet. + The event packet will save space for the packet size and + insert the header. It will then call the + <methodname>read</methodname> + method of its event segment, <varname>pCompound</varname>. + <varname>pCompound</varname> in turn will invoke the + <methodname>read</methodname> method of each of the event + segments it wraps in the order in which they were added: + <varname>pTdc0</varname>, <varname>pTdc1</varname>,... + <varname>pTdc4</varname>. + Once all events egments are read, the compound event segment + will compute and fill in the size field of the header. + </para> + <para> + The readout framework will next invoke the + <methodname>read</methodname> method of the + <literal>Simple</literal> event segment. + This will perform in the same way as <literal>Compound</literal> + except that it will directly call the <methodname>read</methodname> + method of <varname>pTdc</varname>. + </para> + <para> + Once the event has been read out, the same algorithm will be + applied, however the <methodname>clear</methodname> method + will be invoked. + </para> + </section> + <section> + <title>Specifying the scaler readout</title> + <para> + Specifying the scaler readout is very similar to specifying + the event trigger response. This is done in the + method <methodname>Skeleton::SetupScalers</methodname>. + </para> + <para> + At present, this method sets up a timed trigger as the + scaler readout trigger. The default code sets the + scaler readout trigger period to <literal>2</literal> seconds. + </para> + <para> + You must add code to this method to define the response + to the scaler trigger. If you don't want a timed trigger, + you can substitite some other trigger object if you have + some special application need. + </para> + <para> + In the example below, several SIS3820 scalers are + combined to form a scaler bank. That scaler bank is registered, + and then a single SIS3820 module is registered. + </para> + <example> + <title>Setting up scaler readout</title> + <programlisting> +... +#include "CSIS3820Scaler.h>" <co id="ScalerRegister_headers" /> +#include <CScalerBank>" +... +void +Skeleton::SetupScalers(CExperiment* pExperiment) +{ + CReadoutMain::SetupScalers(pExperiment); + + <co id="ScalerRegister_trigger" /> + + timespec t; + t.tv_sec = 2; + t.tv_nsec = 0; + CTimedTrigger* pTrigger = new CTimedTrigger(t); + pExperiment->setScalerTrigger(pTrigger); + + + <co id="SclerRegister_individualModules" /> + + CSIS3820Scaler* pScaler0 = new CSIS3820Scaler(0x80000000); + CSIS3820Scaler* pScaler1 = new CSIS3820Scaler(0x80010000); + CSIS3820Scaler* pScaler2 = new CSIS3820Scaler(0x80020000); + CSIS3820Scaler* pScaler3 = new CSIS3820Scaler(0x80030000); + CSIS3820Scaler* pScaler4 = new CSIS3820Scaler(0x80040000); + CSIS3820Scaler* pScaler5 = new CSIS3820Scaler(0x80050000); + + <co id="ScalerRegister_createbank" /> + CScalerBank* pBank = new CScalerBank; + pBank->AddScalerModule(pScaler0); + pBank->AddScalerModule(pScaler1); + pBank->AddScalerModule(pScaler2); + pBank->AddScalerModule(pScaler3); + pBank->AddScalerModule(pScaler4); + + + <co id="ScalerRegister_Register" /> + + pExperiment->AddScalerModule(pBank); + pExperiment->AddScalerModule(pScaler5); + + +} +... + + </programlisting> + </example> + <calloutlist> + <callout arearefs="ScalerRegister_headers"> + <para> + Includes the headers we will need in the code + that follows. The assumption is that the + <classname>CSIS3820Scaler</classname> class header + is named <filename>CSIS3820Scaler.h</filename>. + </para> + </callout> + <callout arearefs="ScalerRegister_trigger"> + <para> + This code sets up a timed trigger for scalers. + The line that reads <literal>t.tv_sec = 2;</literal> + sets the readout period to <literal>2</literal> seconds. + Since the scaler timestamp resolution is whole seconds, + you shoule leave the <varname>tv_nsec</varname> + field set to zero. + </para> + </callout> + <callout arearefs="SclerRegister_individualModules"> + <para> + This code creates 6 <classname>CSIS3820Scaler</classname> + objects and assigns their addresses to + <varname>pScaler0</varname> ... <varname>pScaler5</varname>. + </para> + </callout> + <callout arearefs="ScalerRegister_createbank"> + <para> + Adds the scalers <varname>pScaler0</varname> through + <varname>pScaler4</varname> to the scaler bank. The + order in which they are added determines the order + in which they will be read. + </para> + </callout> + <callout arearefs="ScalerRegister_Register"> + <para> + The scaler bank and remaining scaler module + are added to the set of scalers the readout will + read/clear when the scaler trigger fires. + Once more these are read in the order in which they + were registered. + </para> + </callout> + </calloutlist> + </section> </section> <section> <title>Editing and using the Makefile</title> <para> + Once you have completed your modifications, and written your code, + you need to be able to build it. The <filename>Makefile</filename> + distributed with the skeleton is heavily commented. Most of the + time you will simply have to tell the Makefile to build your + objects and incorporate them into the <filename>Readout</filename> + executable it builds. </para> + <para> + If we assume that we've written two <filename>.cpp</filename> files + and corresponding headers named: <filename>CCAENEventSegment.cpp</filename> + and <filename>CSIS3820Scaler.cpp</filename>, + We need to modify the <literal>OBJECTS</literal> definition to + read as follows: + </para> + <example> + <title>Makefile modifications</title> + <programlisting> + ... +OBJECTS=Skeleton.o CCAENEventSegment.o CSIS3820Scaler.o + ... + </programlisting> + </example> </section> </chapter> <chapter> <title>Creating a Readout program from a spectrodaq production readout program</title> - <para></para> + <para> + This chapter describes how to create a RingDaq readout program given + a spectrodaq production readout program as a starting point. + The RingDaq readout program closely mimics the SPDAQ production readout + framework. Therefore the concepts should seem quite familiar. + </para> + <para> + In this chapter we'll see how to: + </para> + <itemizedlist> + <listitem> + <para>Obtain a copy of the RingDaq readout framework skeleton</para> + </listitem> + <listitem> + <para>Port an existing event segment to RingDaq</para> + </listitem> + <listitem> + <para>Modify the <filename>Skeleton.cpp</filename> to register + our event segments.</para> + </listitem> + <listitem> + <para> + Port existing scaler objects to RingDaq + and instantiate/register them with <filename>skeleton.cpp</filename> + </para> + </listitem> + + <listitem> + <para>Modify the <filename>Makefile</filename> to add our + software to the final <filename>Readout</filename> program + produced by it. + </para> + </listitem> + </itemizedlist> + <section> + <title>Obtaining a copy of the RingDaq readout skeleton</title> + <para> + In this section we are going to operate as if an environment + variable named <literal>DAQROOT</literal> is defined and + points to the top level of the RingDaq distribution. + At the time this is being written, at the NSCL this would give + <literal>DAQROOT</literal> the value + <filename>/usr/opt/daq/10.0</filename>. As time goes on, + this directory name may change as version numbers change. + If you are not at the NSCL you will need to contact your + system administrators about where they installed this software. + </para> + <para> + The commands below show how to obtain a copy of the readout + skeleton for RingDaq: + </para> + <example> + <title>Getting the skeleton</title> + <programlisting> +mkdir myreadout +cd myreadout +cp $DAQHOME/skeletons/dbs/* . + </programlisting> + </example> + <para> + This sequence of unix shell commands creates a new directory + named <filename>myreadout</filename>, makes that the current + default directory and copies the readout skeleton into that + directory. + </para> + <para> + The readout skeleton constists of the following files: + </para> + <variablelist> + <varlistentry> + <term><filename>Makefile</file... [truncated message content] |