Proposed Trigger Configuration file changes

for 2002-2003 run

Version 10.0

Jeff Landgraf: <jml@bnl.gov>

We need to support two major changes to the trigger for the 2002-2003
run. The first is that we need to support the "New TCU", which from the
configuration perspective is different mainly because the Trigger Word
lookup table is split in two: the Physics Word LUT and the Trigger Word
LUT. Ideally, we keep a similar user interface, in which the user
defines bit patterns that correspond directly to a trigger condition.
This is not entirely trivial to do. The second change is to support the
"Trigger accounting". The main point here is that the higher level
triggers are linked to the lower level triggers. Each L1 trigger will
have a list of Trigger Words, and will only accept events if the trigger
word is on that list. L1 & L2 triggers will only accept events accepted
by a corresponding algorithm at the lower level.

Also, purely for my convenience, I want to clean up a few aspects of the
configuration. First, L3 algorithms should go into the trigger setup
structures. Second, algorithm structures should be modified so that L1,
L2, & L3 algorithm are setup the same way. Finally, I want to expand the
configurable DSM registers, to be more general and to reduce wasted
space a bit.

# Preliminaries

The general run control scheme stays exactly the same. All commands are
unchanged. The run control writes the configuration structure into
binary files that are available to all nodes over NFS. Run control sends
the message RTS_SEND_CONFIG to all nodes. Each node reads the
configuration file as required and once finished returns RTS_SEND_CONFIG
to the run control. The configuration file is not changed between the
two messages, but after the RTS_SEND_CONFIG message has been sent back
to Run Control, the file should not be read by any node.

There are a lot of changes needed the Trigger & some for the L3
algorithm setup. The rest of the configuration remains unchanged.

The most current version of the configuration structures will be in

/RTS/include/RC_Config.10.0.h

When we make this the production version, I will link RC_Config.h to
point to this file.

There are many points throughout this document where I have arrays of
quantities that can have a variable number of arguments. I set the
maximum index of these arrays to MAX, for now as I haven't specified all
of them yet. I assume that I know the number of elements in the arrays.
In the final configuration file I will know this by delimiting the last
entry with zeros if possible. If zero is a valid parameter I will add a
separate length field to the appropriate structures.

# I. TCU LUT Configuration

In the end to configure the TCU we need to create 4 LUTs for the TCU:
the Physics Word LUT, Trigger Word LUT, Action Word LUT and Prescale
table. Last year, run control sent a set of bit masks to L1CTL which
completely specified the TW LUT and the AW LUT. We will do the same
thing this year, although the rules for building the LUT's from the bit
masks will be more complicated. In addition, the GUI itself will be
building these lookup tables locally according to the same rules as part
of the process for generating the structures and verifying that they are
consistent.

On the user side, the trigger setup will include one or more Triggers.
Each Trigger

Is a collection of trigger conditions, where a trigger condition is the
physics part of the TW_DEF tables bit masks from last year. It also
includes the detector LIVE requirement, the desired action word,
pre-post counters, and detector request. Finally, it will include fields
that the GUI will use to calculate desired pre-scales. (It will also
include the info for the L1, L2 & L3 triggers to be discussed later.)
The structure will look like this:

struct Trigger

{

UINT32 offlineBit;

PwCondition L0conditions\[MAX\];

UINT32 detectorLiveOnBits; // required to be alive

UINT32 detectorLiveOffBits;

UINT32 detectorRequest; // these fire!

UINT32 desiredAW;

UINT32 desiredPre;

UINT32 desiredPost;

> float ZDC_rate;
>
> float expected_L0_fraction;
>
> float desired_L0_rate;
>
> //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--
>
> // The following are generated by the GUI from
>
> // the above parameters\...
>
> UINT32 desiredL0PS; // User doesn't enter

UINT32 PW_used\[MAX\]; // List of PW's contributing

UINT32 TW_used\[MAX\]; // List of TW's contributing

//\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--

\....more stuff not related to TCU configuration\....

};

struct PwCondition

{

UINT32 onbits;

UINT32 offbits;

};

This is roughly the same as TW_DEF last year. There are several
important differences:

-   Trigger can completely ignore this table. This table is only used
    for the user to set up triggers on the screen. The GUI then uses
    this table to populate additional structures that trigger will
    actually use to set up the TCU.

-   There are multiple conditions. If any of the conditions in the list
    are satisfied than the trigger is satisfied.

-   The prescale isn't entered directly. Instead, one enters the
    expected L0 fraction (events satisfying conditions / zdc
    cooincidences) and desired L0 rate (evts / sec) for this Trigger.
    The GUI will calculate the desired prescale from these two values
    and the ZDC rate (at the beginning of the run). This is a first
    order attempt to make the prescales easier to use. Once we (the
    experts) have determined reasonable fractions for all of the
    triggers in use, then we can automatically pick up the zdc rates at
    the beginning of each run and have at least some shot at getting the
    dead-times & relative trigger rates to what we want (while only
    fiddling with one number per run\...)

-   The values that are to be put in the Action Word LUT are all marked
    as "desired". This is because multiple triggers might have
    conflicting requests if they "overlap". If the requests are not
    consistent, the GUI will not allow the run to start, so trigger need
    not worry about inconsistent requests. I will discuss how I will
    determine consistency in detail below.

The overall trigger setup is a collection of Triggers:

struct TrgSetup

{

Trigger triggers\[MAX\]; // GUI only

PwCondition contaminationDef;
//\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--

PwCondition pwc\[MAX\]; //

PwLink pwl\[MAX\]; // Define the TCU LUTs

TwCondition twc\[MAX\]; //

TwLink twl\[MAX\]; //

AwCondition awc\[MAX\]; //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--

\...stuff unrelated to TCU\...

};

So we start with a TrgSetup structure, containing n valid trigger
entries that were entered by the user. The contaminationDef condition is
also set by the user. All other entries in the TrgSetup structure start
out zeroed.

### PW LUT construction

The first thing I do is loop through each trigger and copy each
L0Condition into the pwc\[\] array. It is possible that two triggers
have conditions that are exactly the same. In that case, I only enter
the condition into pwc\[\] once. I call the index in this array a
condition's pwcIdx.

Secondly I build a table that is closely analogous to the PW LUT, which
I'll call the PwDef table. This is constructed as follows:

UINT32 PwDef\[2\^16\]; // index is input bits for PW LUT

for(int input = 0; input\<2\^16; input++)

{

for(int pwcIdx=0;pwcIdx\<nConditions;pwcIdx++)

{

if(ConditionSatisfied(pwc\[pwcIdx\], input))

PwDef \|= 1\<\<pwcIdx;

}

}

PwDef is logically just like a PW although it has nConditions bits
rather than 6 bits. In fact, it is even better than an arbitrary PW
because it explicitly contains the conditions that lead to the PW.
Regions where two conditions overlap are obvious because the pwcIdx bit
is set for both conditions. The regions defined by a given PwDef are
mutually exclusive by construction, and the confusing order dependence
of conditions is completely removed.

To get a 6 bit action word, we need to associate the PwDef's to the
PW's. For this we use the pwl\[\] array

PwLink

{

UINT32 pwDef;

UINT32 PW;

};

I fill this table by looping through the PwDef\[\] array I constructed
above. For each unique value for PwDef I add an entry to PwLink. The PW
value starts at 0 (for PwDef==0) and increments on each new PwDef.

Potentially, There could be as many as 2\^nConditions PW created in this
way, but in fact most conditions only overlap with one or two other
conditions and the number of PW's really turns out to be on the order of
2 \* nConditions which easily fits into the 6-8 bits allocated.

To construct the actual PW LUT, one uses the pwc\[\] and pwl\[\] in a
similar loop:

UINT32 PWLUT\[2\^16\]; // index is input bits for PW LUT

for(int input = 0; input\<2\^16; input++)

{

UINT PW;

UINT PwDef

for(int pwcIdx=0;pwcIdx\<nConditions;pwcIdx++)

{

if(ConditionSatisfied(pwc\[pwcIdx\], input))

{

PwDef \|= 1\<\<pwcIdx;

}

}

for(int i=0;i\<pwlEntries;i++)

{

if(pwl\[i\].PwDef == PwDef)

{

PW = pwl\[i\].PW;

break;

}

}

PWLUT\[input\] = PW;

if(ConditionsSatisfied(contaminationDef, input)

{

PWLUT\[input\] \|= 1 \<\< 13; // Assuming contamination

// bit is 13!

}

}

Only two hitch's: (1) you need to find the PW corresponding to the
PwDef, (2) the contamination bit is effectively the 13^th^ bit of the PW
from which it gets sent to the detector busy FPGA.

### TW LUT construction

The construction for the TW LUT is highly analogous to that of the PW
LUT. The first difference is that the twConditions are not defined by
onbits and offbits. Instead the TW condition is defined by:

struct TwConditions

{

UINT32 PW;

UINT32 detectorLiveOnBits;

UINT32 detectorLiveOffBits;

};

Each PW can require different detector bits. In addition, it is possible
that a single PW comes from more than one trigger, resulting in more
than one set of detector bits to be associated with a given PW.

For each trigger, I will maintain a list of PWs that satisfy the
conditions for that trigger. I can produce this list at any time by
looping through the pwl\[\] array to find conditions contributing to
each PW. Then I loop through each trigger to see which triggers contain
that condition.

To build TwConditions, I loop through every trigger. For each PW
involved I add a entry into twc\[\] containing PW and that triggers
detector masks. Again, as for the PwConditions, I will suppress repeated
conditions.

Now I follow the exact procedure as for the PW LUT to build the twl\[\]
structure.

struct TwLink

{

UINT32 twDef_hi;

UINT32 twDef_lo;

UINT32 TW;

};

I am assuming here, that there may be more than 32 TwConditions, so I
expand the number of bits for TwDef.

In the same way as I obtain the list of PW for each Trigger, I will
construct a list of TW for each Trigger.

There is an important implication here, which is that the TW and PW are
generated by the computer without any information about the intended
use. Therefore, TW and PW become featureless sequential integers. We
drop any 0x1100 \--\> central. There definition is not guaranteed to be
the same for runs with different configurations. They become internal
parameters and will only be used by us for debugging.

### AW LUT & Prescale table Construction

I treat the AW LUT & Prescale table interchangeably because they are
both indexed by the TW. At this point, we have a set of Triggers. Each
Trigger has a corresponding set of PW's and a set of TW's associated
with it.

The GUI will provide the following structure to aid in the AW & Prescale
table construction:

struct AwConditions

{

UINT32 TW;

UINT32 PS;

UINT32 AW;

UINT32 detectorRequest;

UINT32 pre;

UINT32 post;

};

To build this structure, for each TW I will examine all Triggers that
contain TW. I will set the values according to the following table:

desiredPS take the smallest value

desiredAW require exact match

desiredDetectorRequest require exact match

desiredPre take the biggest value

desiredPost take the biggest value

If the value requires an exact match, but two or more triggers have
different values then the trigger setup is impossible and the GUI will
not allow the run to start.

# II. L1, L2 & L3 Trigger Configuration

It is important to make the handling of High-Level triggers consistent.
It is also important to be able to integrate the different levels
together if we wish to analyze the data produced.

Here is the paradigm I propose. (Most elements should be familiar
because this is not a radical departure from the current scheme in L3.):

-   At each level there are a list of algorithms. These algorithms have
    a name and an ID. The ID starts at 0 and increases. If an algorithm
    is retired, its number is not re-used.

-   At run time, each running algorithm will be provided with a bit
    number at run time. This bit will represent the algorithm in status
    flags.

-   Each algorithm has a set of algorithm specific input variables. This
    will consist of \~5 Integer variables and \~5 floating point
    variables. The code should access them as userInt\[x\], but if names
    are supplied to me, they will be displayed to the user of the GUI
    and available for display by name in the run-log and other clients
    of the databases.

-   All levels will reject every event for which no algorithm is
    satisfied.

-   All levels will provide an algorithm called "Always accept", which
    will accept every event that satisfies its prescale.

-   Each L2 & L3 algorithm should have a single prescale parameter. (We
    should get rid of the L3 postscale parameter unless someone can
    explain why it is needed to me. Otherwise, all levels should
    implement a postscale as well.)

-   Each algorithm will be provided with a descriptor that describes
    what events it should run on. If an event does not match the
    descriptor, then the event should be rejected by the algorithm \-\--
    even if the event would normally have satisfied the algorithm.

-   For every run, multiple algorithms run in parallel.

-   Every algorithm can have multiple instances running in the same run.
    The user parameters & PS & event descriptors may be different for
    different instances of the same algorithm. (If an algorithm requires
    a lot of processing time, because, for example it needs to loop over
    all tracks, it can be divided into a shared processing step and
    separate instances for the decision step. The point is that the PS &
    event descriptor must be maintained separately for each instance.)

-   Each algorithm should save counters of the events rejected by that
    event by TW.

The structure used to configure L1, L2 & L3 algorithms will look
something like this:

struct L1Algorithm

{

int id;

int userInt\[5\];

int userFloat\[5\];

int specialProcessing;

> //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--

int PS; // generated by GUI

int statusBit;

UINT32 evtTW\[MAX\];

FLOAT evtRS\[MAX\];

//\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--

};

struct L2Algorithm // L2 & L3

{

int id; // algorithm id

int userInt\[5\]; // user variables

float userFloat\[5\];

int specialProcessing;

> //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--

int PS; // generated by GUI

int statusBit;

UINT32 evtTrgBit;

//\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--

};

Here, the statusBit is the bit corresponding to this instance of the
algorithm in the trigger summary flags. For L1 algorithms, the evtTW\[\]
lists the TW's that the algorithm should examine. If an events TW is not
on this list, the algorithm must reject the event. Similiarly, for L2 &
L3 algorithms, the algorithm should examine the events trigger summary
for the previous level. If the evtTrgBit bit is not set, then the
algorithm must reject the event. The specialProcessing flag is for
future expansion. It's use would be to set special processing for this
event. (Write out raw data instead of clusters, etc\...)

In the same way as for the L0 configuration, some of these values are
entered by the user, and others are calculated by the GUI. The user will
enter values into the trigger structure.

struct Trigger

{

\.... L0 Configuration above \....

Algorithm l1;

Algorithm l2;

Algorithm l3;

float expected_L1_fraction;

float desired_L1_rate;

float expected_L2_fraction;

float desired_L2_rate;

float expected_L3_fraction;

float desired_L3_rate;

\.... unrelated to high level algorithms \....

};

As for the L0 configuration, the subsystems will not examine the data
from the Trigger structure. Instead, there will be corresponding entries
in TrgSetup

struct TrgSetup

{

\... L0 config \...

Algorithm l1_algorithms\[MAX\];

Algorithm l2_algorithms\[MAX\];

Algorithm l3_algorithms\[MAX\];

\... other stuff \...

};

The GUI will generate the structures in TrgSetup from the information
the user provided info in the Trigger structures.

### L1 Rescaling

To be useful for analysis, the high-level triggers should uniformly
sample events satisfying the lower-level conditions for that trigger. To
do this, we must handle the complication that the L0 TW divides the
event space into mutually exclusive sets, but the triggers we are
interested in overlap. I have suggested a bunch of ways to do this in
the past, at the trigger meeting I suggested doing this in a new system
after DAQ but before OFFLINE. Here I suggest yet another method.

The goal is just to ensure that the effective prescale of every TW being
fed into a L1 algorithm is the same. Each TW has a different PS at L0,
so L1 needs to correct for this by maintaining different PS for each TW.
The relationship we want is simply:

PS_0(TW) \* PS_1(TW) = Constant (or)

PS_0(TW) \* RS(TW) \* PS_1 = Constant

Where RS(TW) is the rescale. With this definition, PS_1 is the
additional scaling done purely at L1, independent of TW. PS_1 can never
be less than one, so assuming we don't want L1 to add any scaling, the
constant should be MAX(PS_0(TW)).

The upshot is that L1 must use a different prescale for each Trigger
Word. The prescale factor should be given by: RS(TW) \* PS.

This simple rescale combined with the requirements / restrictions placed
on the L1,L2&L3 algorithms guarantees that the sample of events
satisfying a given L3 algorithm defined in a Trigger is exactly the same
sample no matter how many other Triggers are defined at the same time.
The effective prescale of the trigger is:

PS_EFF = MAX(PS_0(TW)) \* PS_1 \* PS_2 \* PS_3

where TW is restricted to the trigger words contributing to this
trigger.

The scaler information is NOT required to ensure that the event sample
is unbiased.

The deadtime for the Trigger can be easily calculated from the scalers
because of the restriction that each TW contributing to the Trigger has
the same detectorRequest.

The total cross-section can be calculated by using detector deadtime
along with the effective prescale, and external information about the
beam characteristics.

# III. Special Triggers

Special Triggers would also be implemented within the scheme of the
Trigger structure. Generally the L1, L2 & L3 triggers would all be
"Always accept". The L0 setup is unchanged. The main addition is that
the TCD controller needs to be given instructions to fire the special
triggers with some frequency. These instructions are placed in the
Trigger structure.

struct Trigger

{

\... L0, L1, L2 & L3 stuff \...

TcdSetup tcd;

};

struct TcdSetup

{

int tcdId;

int seconds;

};

This is the same as last year, except I will pick up the trigger command
from the L0 part of the Trigger definition.

# IV. Offline Interface

The L3 summary is already available in DATAP. This because there is a
one-to-one correspondence between L3 algorithm instances and Trigger
entries, the L3 summary becomes a bitmask showing which Triggers were
satisfied by the event. (The difference from last year is that the L0,
L1 & L2 requirements and the requirement that the event be unbiased are
also now given by the L3 summary alone.) The only trick is that the bits
in the L3 summary are arbitrary. They just refer to the index of the
Trigger in the Trigger array. We need to map these bits to a larger
bitmask that contains static bits for every Trigger used by STAR for all
time. I do this by writing an entry to the database for every run.

struct TriggerInfo

{

UINT32 statusBits\[32\];

UINT32 PS\[32\];

UINT32 PsL0\[32\];

UINT32 PsL1\[32\];

UINT32 PsL2\[32\];

UINT32 PsL3\[32\];

float fractionalCrossSection\[32\];

float totalCrossSection\[32\];

float deadTime\[32\];

// etc\...

};

The statusBits flag is a lookup table linking the bit in the L3 summary
to the static Trigger bit. This bit is defined in the original Trigger
definition structure. PS is the effective total prescale by trigger. The
additional fields are ideas for quantities that could be added to the
database record when the run is stopped to make analysis easier. These
could each be calculated from a scaler analysis.

The final step is that StEvent needs a function

bool checkTriggerBit(int triggerBit)

{

int statusBit=0xffff;

for(int i=0;i\<32;i++)

{

if(TriggerInfo.statusBits\[i\] == triggerBit)

{

statusBit = i;

> break;
>
> }

}

> if (statusBit == 0xffff)
>
> return false; //trigger not in run

return (statusBit & L3_Summary);

}

To query bits in the 2\^32 bit virtual Trigger bitmask.

# V. DSM Register configuration

Last year we had a bunch of huge arrays for the DSM register values:

struct TRG_SETUP

{

\.....

int L1_DSM_Reg_Data_Values\[32\]\[32\];

int L2_DSM_Reg_Data_Values\[32\]\[32\];

\.....

int MWDC_DSM_Reg_Data_Values\[32\]\[32\];

\.....

};

This led to lots of unused space, as we only used a few registers per
DSM. Also, it meant that the display had trigger parameters organized by
DSM rather than by use. Also, the dictionary entries that allowed easy
naming of these parameters made them very useful for expansion
parameters for unrelated systems such as the FPD.

To make things a little more clear this year I want to switch to a
single array:

struct RegValue

{

> int object;
>
> int index;
>
> int register;
>
> int value;

};

struct TrgSetup

{

\... L0, L1, L2, L3, special trigs, etc\...

RegValue registers\[MAX\];

};

We will agree on object numbers for the different DSM types. Index &
register will map to the two indexes of the last years DSM's. We add new
object numbers for expansion.