White Rabbit based Timing
Steps to create a White Rabbit triggered real-time event
Generally
- New operational front-ends first need to be registered by the Infrastructure- and by the Timing-Group:
Class Design
The design needs:
- RT Action
- Logical Event of type "timing"
- Scheduling unit referencing the logical-event and the RT-action
Deployment Unit
The DU needs a concurrency layer containing a reference to the scheduling unit in the design.
The executable needs to be of type 'mixed'.
Instantiation File
The instantiation file needs:
Timing configuration at: classes->[class name]->device-instance->configuration
- TimingDomain: The id of the ! TimingDomain has to mach the GroupID of the incoming event. Events of all other domains will be ignored
- accelerator:
- accelerator-zone: required for the settings database
- Check this link in order to see all GroupID's which are played on a FEC
- mainMuxCriterion 'SEQUENCE' to multiplex all fields per sequence-id or 'PROCESS' to multiplex them per process-id. NONE, if you dont want to multiplex your fields.
Event configuration at: classes->[class name]->events-mapping->[event name]->event-configuration->Timing->hardware-event
Set the event configuration name and event code (e.g.EVT_START_RF#1)
Event mapping at: classes->[class name]->device-instance->event-mapping->[event name]->event-configuration-ref
Select the event configuration defined above.
Event Behaviour
The FESA framework will register with the timing network to receive events that match the accelerator zone and event numbers given. The framework will always receive Process Start and Sequence Start events to track timestamps, but will not raise an RT-Action unless specified in the instantiation file.
Multiplexing Context
The timing event triggers the creation of a multiplexing context that contains the event timing details that are passed to the RT-Action.
uint32_t formatId;
uint32_t groupId;
uint32_t eventNumber;
uint32_t sequenceIndex;
uint32_t sequenceCounter;
uint32_t processIndex;
uint32_t beamProductionChainIndex;
uint64_t eventExecutionTimestamp;
uint64_t sequenceTimestamp;
uint64_t processTimestamp;
uint64_t beamProductionChainTimestamp;
uint64_t acquisitionTimestamp;
Use of the Multiplexing Context in a Realtime Action
const fesaGSI::TimingContextWR* contextWR = dynamic_cast<const fesaGSI::TimingContextWR*>(pEvt->getMultiplexingContext());
Error Flags
The White Rabbit Context contains error flags from the timing receiver:
isEarly(): the Timing Event was triggered before its deadline (to prevent timing receiver from filling)
isLate(): the Timing Event was triggered after its deadline
isConflict(): two Events were triggered simultaneously and their order is undefined
These three errors will typically indicate serious Timing Master or clock synchronization problems
isDelayed(): the timing receiver and software stack was unable to process events fast enough
Multiplexing Depth
Per default, all multiplexed setting-fields of a multiplexed device will be duplicated:
- times 1024 when the device is multiplexed per Process Index
- times 64 when the device is multiplexed per Sequence Index
This default multiplexing-depth should be used whenever possible. If for some reason (E.g. insufficient memory / system resources) the default cannot be used, please inform the LSA and FESA team about that! Otherwise it may happen that your device fails to handle an incoming out-of-bound Process/Sequence index during operation, which was sent by LSA.
The multiplexing-depth can be adjusted manually by passing the application-argument "-muxDepth
":
myDUBinary ...... -usrArgs "-muxDepth 123"
TimingUtility Helper Functions
#include <fesa-core-gsi/RealTime/TimingUtility.h>
//For work with White Rabbit EventIDs.
generate_event_id(TimingGroupID, EventNumber)
generate_event_id_mask(TimingGroupID, EventNumber)
The bit positions in an EventID are defined in fesa-core-gsi/RealTime/TimingEventSourceWR.h (EVENTID_EVENTNO_OFFSET etc.)
Working With Devices in different Timing Groups
Devices in a deploy unit may be configured to be in different TimingDomains and therefore respond to events in different timing groups.
In the instance file events-mapping it is determined which devices will respond to which event, and which timing group each device belongs to.
In an RTAction, the device collection is determined by which devices are listening to an event. If two devices in two different timing groups are listening for the same event number and that event number is sent to both groups by the timing master, two RT Actions will be triggered with the device collection containing both devices.
If a device is only interested in events for its own timing group the device collection can be filtered:
#include <fesa-core-gsi/RealTime/TimingUtility.h>
...
void MyAction::execute(fesa::RTEvent* pEvt)
{
const std::vector<Device*>& alldevices = MyClassServiceLocator_->getDeviceCollection();
const std::vector<Device*>& devices = fesaGSI::FilterDeviceCollectionToTimingGroupID(alldevices, pEvt);
Access to SAFTlib functions
White Rabbit functions are available in the TimingEventSourceWR class
Static Access
#include <fesa-core-gsi/RealTime/TimingEventSourceWR.h>
...
Device* device = WRTimingTestServiceLocator_->getDeviceCollection()[0];
...
fesaGSI::TimingEventSourceWR::registerTrigger(device, eventNo, offset, tag);
Instance Access
#include <fesa-core-gsi/RealTime/TimingEventSourceWR.h>
#include <fesa-core/RealTime/AbstractEventSourceFactory.h>
...
std::string className = this->WRTimingTestServiceLocator_->getClassName(); // substitute your FESAClassName for WRTimingTest
fesaGSI::TimingEventSourceWR* evtSource = dynamic_cast<fesaGSI::TimingEventSourceWR*>(fesa::AbstractEventSourceFactory::getEventSource(className, "Timing"));
...
evtSource->registerTrigger( device, eventNo, offset, tag );
To generate tags on the SCU bus in response to a timing event:
void TimingEventSourceWR::registerTrigger( AbstractDevice* device, int eventNo, uint64_t offset, uint32_t tag );
The groupID is retrieved from the instantiation file via the device pointer.
Where offset is the time delay in ns after event before tag is written
To stop an event responding:
void TimingEventSourceWR::deregisterTrigger( AbstractDevice* device, int eventNo );
To manually generate a tag on the SCU bus:
void TimingEventSourceWR::injectTag( uint32_t tag);
SCU Environment
The SCU must be running the dbus and saftlib daemons. Example ps output:
1611 dbus 0:34 /bin/dbus-daemon --system
1623 root 2:47 /sbin/saftd baseboard:dev/wbm0
If one of the daemons does not run, check that the right fpga-firmware-version is installed. Currently there is no proper way to do so. Here a rudimental solution:
- If the command "eb-info" is available:
-
# eb-info dev/wbm0
- The firmware of the build-date "Wed Jun 10 11:03:34 CEST 2015" runs, however it has several time synchronized bug, a upgrade is recommended.
- If your firmware is even older, you need to upgrade it!
- If the command"eb-info" is not available, but the command "eb-ls" is available:
- The chances are good that your firmware is up to date.
- If none of both is available.
- Something is terribly wrong on this FEC, please contact the Timing Team
In order to get a up to date fpga-firmware, please contact the Timing Team
https://www-acc.gsi.de/wiki/bin/view/Timing/TimingSystemHowBuildingDeployment
Use the saft-ctl tool to check the Status of the SAFTlib daemon:
saft-ctl baseboard -s
Use saft-ctl to check that White Rabbit Events are reaching the SCU:
saft-ctl baseboard snoop 0 0 0
On the asl-cluster, there is a tool to decode the received event-payload (ID). It can be found here:
/common/usr/fesa/tools/decodeEventPayload
[dm-snoop will be removed in later releases] Use the timing tool dm-snoop to check White Rabbit events are reaching the SCU.
To run the dbus and saftlib daemons automatically on an SCU after reboot create a link to the saftlib initialization script in the nfsinit folder for the SCU:
matthies@asl733:scuxl0052>pwd
/common/export/nfsinit/scuxl0052
matthies@asl733:scuxl0052>ll
lrwxrwxrwx 1 matthies bel 19 Jul 9 13:45 20_timing-rte -> ../global/timing-rte-R8-balloon_0
lrwxrwxrwx 1 root root 20 Jul 12 11:24 50_fesa -> ../global/fesa_64bit
The scripts will be run in ascending order indicated by the numbers at the beginning.
For more information on the timing runtime environment see https://www-acc.gsi.de/wiki/FESA/FESA3_TimingRTE_400
Multiple instances per SCU
The SAFTlib daemon controls access to the timing hardware. FESA classes create proxy objects to interact with the SAFTlib daemon. This allows multiple FESA classes to execute on the same SCU and register for the same timing event. The number of events that can be listened to is limited by the condition table of the ECA unit.
Accessing SAFTlib directly
Using the Saftlib Directory a FESA class can connect to the Saftlib Daemon without going through the FESA framework. Saftlib tracks ownership of events and actions so events registered with the FESA framework may be seen but not modified. A class may have its own DBUS event loop and set of events instead of - or in addition to - a timing event source registered by the framework.
As an example, a skeleton custom event source for a SoftwareAction.
Create a Custom Event Source, link to RTAction, Set EventMapping.
Required Headers:
#include <giomm.h>
#include <SAFTd.h>
#include <TimingReceiver.h>
#include <SoftwareActionSink.h>
#include <SoftwareCondition.h>
Class members:
Glib::RefPtr<saftlib::TimingReceiver_Proxy> timingReceiver_;
Glib::RefPtr<saftlib::SoftwareActionSink_Proxy> timingActionSink_;
Glib::RefPtr<Glib::MainLoop> timingLoop_;
std::vector<Glib::RefPtr<SoftwareCondition_Proxy> > conditions_;
Constructor / Init
timingLoop_ = Glib::MainLoop::create();
std::map<Glib::ustring, Glib::ustring> timingDevices = saftlib::SAFTd_Proxy::create()->getDevices();
timingReceiver_ = saftlib::TimingReceiver_Proxy::create(timingDevices["baseboard"]);
timingActionSink_ = saftlib::SoftwareActionSink_Proxy::create(timingReceiver_->NewSoftwareActionSink(""));
void CustomTiming::connect(const boost::shared_ptr<fesa::EventElement>& eventElement)
{
guint64 eventID = 0x0fa0001000000000;
guint64 eventMask = 0xfffffff000000000;
gint64 offset = 0;
try
{
Glib::RefPtr<SoftwareCondition_Proxy> condition = SoftwareCondition_Proxy::create(timingActionSink_->NewCondition(true, eventID, eventMask, offset));
condition->Action.connect(sigc::mem_fun(this, &CustomTiming::on_action)); // register object method as callback function
conditions.push_back(condition); // the smart pointer must stay alive for actions to occur
}
catch (const Glib::Error& error)
{
}
}
void CustomTiming::wait(boost::shared_ptr<fesa::RTEvent>& eventToFire)
{
timingLoop_->run(); // this blocks until an action occurs
createEvent(eventToFire, CustomTiming::defaultEvent);
}
// callback function
void CustomTiming::on_action(guint64 id, guint64 param, guint64 time, guint64 execution, guint16 flags)
{
// unpack error flags
const bool late = (flags & 1) != 0;
const bool early = (flags & 2) != 0;
const bool conflict = (flags & 4) != 0;
const bool delayed = (flags & 8) != 0;
// do something with timing information
timingLoop_->quit();
}
https://www-acc.gsi.de/wiki/pub/Timing/TimingSystemDocumentsSaftlib/saftlib.pdf
https://www-acc.gsi.de/wiki/Timing/TimingSystemEventNumbers
https://www-acc.gsi.de/wiki/Timing/TimingSystemGroupsAndMachines
SAFTlib Version History
- 12.2016, Saftlib Version 1.0.8
Released as part of Timing Run-Time Environment Release 8
- 11.08.2016, Saftlib Version 1.0.8
ECA2 Support
Fixes:
Event triggers for SCUbus Tags with adjacent EventIDs are accepted
Action offsets greater than 2^32ns work correctly.
Negative offsets for actions work correctly.
No Late Errors for events that have not been subscribed to
High 32-bit of payload is correct, no longer copied from eventid.
Changes:
saft-ctl tool replaces the functionality of dm-snoop and eca-ctl
Interface:
Fixed inconsistent signedness of guard and offset parameters.
Usage of local timing
-- due to a bug local timing is not usable in FESa v4.0.0 --
While adding a FEC ( press "add FEC" in the deployment unit ) in the folder of this new FEC a file called Timing.xml will be generated.
By default this file looks like that:
<?xml version="1.0" encoding="UTF-8"?><fec name="zui">
<local-timing>
<event code="4711" name="MY_TIMING_EVENT_1"/>
<event code="4712" name="MY_TIMING_EVENT_2"/>
</local-timing>
</fec>
So when picking a hw-event in the instance-file, at the very end of the list you as well should see "MY_TIMING_EVENT_1#4711" and "MY_TIMING_EVENT_2#4712".
You are free to modify this xml file to add any number of self-defined events. When you finished your modifications, you need to trigger "Synchronize Source Code" in the deployment unit, in order to update the schema of the instance-file and to make use of your local timing configuration.
Simulation of Timing Events
For easy simulation, best use the following tool: /common/usr/fesa/tools/SimulateEvents/SimulateEvents
Copy it to your FEC and use it from there. Use -h to see its documentation and all possible parameters.
The source-code can be found here: https://www-acc.gsi.de/svn/fesa/framework/branches/SimulateEvents