Arianna autonomous DAQ firmware

Dependencies:   mbed SDFileSystemFilinfo AriSnProtocol NetServicesMin AriSnComm MODSERIAL PowerControlClkPatch DS1820OW

main.cpp

Committer:
uci1
Date:
2012-06-30
Revision:
0:664899e0b988
Child:
1:e392595b4b76

File content as of revision 0:664899e0b988:

#include "mbed.h"

#include <stdint.h>
#include "SDFileSystem.h"
#include "MODSERIAL.h"
#include "Watchdog.h"
#include "SnConstants.h"
#include "SnBitUtils.h"
#include "SnSDUtils.h"
#include "SnConfigFrame.h"
#include "SnEventFrame.h"
#include "SnStatusFrame.h"
#include "SnCommWin.h"
#include "SnCommAfar.h"
#include "SnCommUsb.h"

//
// MBED PINS (ordered by number)
//
// leds (for debugging)
DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);
// Set up power pins - Note that it's Zero for "on"
DigitalOut PIN_turn_on_system(p17);
DigitalOut PIN_turn_on_amps(p25);
// Activate/select chip by falling edge
DigitalOut PIN_ADC_CS( p9 );
// clock signal to activate PLA setting
DigitalOut PIN_PLA_cs(p10);
// To force a trigger
DigitalOut PIN_forceTrigger(p11);  //modification
// To suppress thermal triggers
DigitalOut PIN_enableThermTrig(p12);
// Restart clock on all FPGAs.
DigitalOut PIN_DoNotRestartAllClocks( p13 );
// This tells the DFPGAs to store the data on motherboard FPGA and
// read it out.
DigitalIn PIN_a_sf_clk( p14 );
DigitalIn PIN_rst_a_sf(p15);
// Lock daughter card registeres (during data readout).
DigitalOut PIN_lockRegisters( p20 );
// Majority logic pins
DigitalOut PIN_MajLogHiBit(p22);
DigitalOut PIN_MajLogLoBit(p23);
// Tell FPGA to be ready to accept DAC values
DigitalOut PIN_start_fpga(p26); 
// Two bits to the select the daughter card for readout
DigitalOut PIN_selCardHiBit( p29 );
DigitalOut PIN_selCardLoBit( p30 );
// To launch a heartbeat pulse
DigitalOut PIN_heartbeat(p24);
// Setup SPI pins
SPI PIN_spi( p5, p6, p7 );
// The SD card
SDFileSystem sd(p5, p6, p7, p8, SnSDUtils::kSDsubDir+1);
LocalFileSystem local("local");


//
// fwd declare fcns
//
void                      ReadAllRegisters();
void                      ReadRegister(const uint8_t chan, int16_t* dev);
void                      SaveEvent(const int32_t etms);
void                      WaitTrigAndSendClock();
void                      SetConfig();
SnCommWin::ECommWinResult OpenCommWin();

//
// globals
//
// readout objs
static Ticker         gForceTicker;
static Ticker         gIOticker;
static Timer          gEvtTimer;
static SnConfigFrame  gConf;
static SnEventFrame   gEvent;
// parameters
static bool           gFirstEvt         = true;
static bool           gReadingOut       = false;
static bool           gCommWinOpen      = false; // if it's open
static bool           gOpenCommWin      = false; // if it should be opened
static int32_t        gEvtNum           = 0;   // num of evt written
static int32_t        gTrgNum[kNumTrgs] = {0}; // num of this type of trg received
// i/o
static Timer          gIOtimer;
static MODSERIAL      gCpu( USBTX, USBRX ); // defined here so it might be used for debugging output
static SnCommWin*     gComms[kNcomms]   = { new SnCommAfar, new SnCommUsb(&gCpu) }; // order => priority
static FILE*          gCurFile          = 0;
//static char           gEvtBuf[SnEventFrame::kMaxSizeOf];
//static char           gConfBuf[SnConfigFrame::kMaxSizeOf];
//static char           gStatBuf[SnStatusFrame::kMaxSizeOf];
static char           gGenBuf[SnStatusFrame::kMaxSizeOf]; // must be big enough for event or status or config!

void procForceTrigger() {
    led1=!led1;
    if (gReadingOut==false && gCommWinOpen==false) {
        gEvent.SetTrgBit(kFrcTrg);
        gEvent.SetTrgNum((gTrgNum[kFrcTrg])++);
        PIN_forceTrigger = 1;     // force a trigger
    }
}

void procCommWin() {
    if (gReadingOut==false && gCommWinOpen==false) {
        gOpenCommWin = true;
    }
}


// TODO: add block-id's to output file? (config block, event block, file header block, etc.)

int main() {
    led2=1;
    //wait_ms(100);

    // a failsafe
    Watchdog::kick(kWDFailsafe);

    // Turn on all power
    // Note that zero means "on"
    PIN_turn_on_system=0;

    // block (thermal) triggers during configuration
    PIN_enableThermTrig       = 0;
    PIN_ADC_CS                = 1;
    PIN_DoNotRestartAllClocks = 1;
    PIN_forceTrigger          = 0;
    wait_ms(20);
    
    // setup SPI
    PIN_spi.frequency( 10000000 );  // Max is 12.5 MHz
    
    gForceTicker.detach();
    gFirstEvt = true;
    
    gConf.Reset();
    
    //
    // get config
    //
    
    // TODO: communication window
    // TODO: call usb port setup
    //OpenCommWin();
    // TODO: get config
    SetConfig();
    
    // get ready to trigger
    PIN_spi.format( 16, 1 ); // change to data readout format

    // force a trigger every dForceTrigPeriod seconds
    gForceTicker.attach(&procForceTrigger, gConf.GetForceTrigPeriod());
        
    led2=0;
    
    // the main event loop. wait for triggers in SendClock
    while( true )
    {
        // in here, we wait for triggers from the MB-FPGA
        led4 = !led4;
        Watchdog::kick(); // don't reset!
        gEvtTimer.reset();
        gEvtTimer.start();
        PIN_lockRegisters = 0; // allow data to come from DFPGA
        WaitTrigAndSendClock();
        PIN_lockRegisters = 1; // block registers during readout
        gEvtTimer.stop();
        Watchdog::kick(); // don't reset!
        
        if (gOpenCommWin) {
            OpenCommWin();
            gOpenCommWin=false;
            continue;
        }
        
        //
        // got trigger. read registers to mbed and build the event
        //

        // read data & calc CRC
        gEvent.ReadWaveforms(PIN_spi, PIN_selCardHiBit, PIN_selCardLoBit);
        gEvent.SetCurMbedTime();
        // TODO: no way to check for external trigger?
        if (gEvent.IsForcedTrg()==false) {
            gEvent.SetTrgBit(kThmTrg);
            gEvent.SetTrgNum((gTrgNum[kThmTrg])++);
        } // else already set by procForceTrigger
        // (no need to calc if we throw this event away)
        
        Watchdog::kick(); // don't reset!

        const int32_t etms = gEvtTimer.read_ms();
        if ( gEvent.IsForcedTrg() || 
            (etms>gConf.GetEvtThrtlPeriodMs()) ) {
            
            PIN_lockRegisters = 0; // done reading, unlock so we can talk to SD card.
            SaveEvent(etms);

        }
    }

}

//
// save the event
//
void SaveEvent(const int32_t etms) {
    // write the event
        
    // set the event number & dt
    gEvent.SetEvtNum(gEvtNum++);
    gEvent.SetDTms(etms);
    
    // save to SD
    SnSDUtils::WriteEventTo(gCurFile, gGenBuf, gEvent, gConf);
    
    // reset
    gEvent.ClearEvent();
    
    if (gEvtNum==5) {
        fclose(gCurFile);
        while (1) {
            led3 = 1;
            wait(0.5);
            led3 = 0;
            wait(0.5);
        }
    }
    
}

//
// set configuration
//
void SetConfig() {
    // restart watchdog
    Watchdog::kick(gConf.GetWatchdogPeriod());
    
    // reset event, timers, trigger counters
    gEvent.ClearEvent();
    gEvtNum = gConf.GetFirstEvt();
    gEvtTimer.reset();
    memset(gTrgNum, 0, sizeof(int32_t)*kNumTrgs);
    
    // make new output file
    SnSDUtils::CloseOutputFile(gCurFile);
    
    gCurFile = SnSDUtils::OpenNewOutputFile(gConf.GetMacAddress(),
                                            gConf.GetRun());
    SnSDUtils::WriteFileHeader(gCurFile, gConf.GetMacAddress());
    SnSDUtils::WriteConfig(gCurFile, gConf);
    
    // TODO: turn on amps individually, when that's possible
    if (gConf.IsEachAmpOn()) {
        PIN_turn_on_amps=0;
    }
    
    // Set PLA value(s)
    // TODO: send multiple values when FPGA code is ready for it
    // TODO: uncomment when using new version of FPGA code
    PIN_spi.format( 16, 0 ); // change mode for DAC & PLA value setting
    PIN_MajLogHiBit=1;
    PIN_MajLogLoBit=1;
    PIN_enableThermTrig=0;

    uint16_t hi, lo;
    PIN_PLA_cs=1;
    wait(3);
    for (uint8_t pi=0; pi<kNplas; pi++) {
        if (pi < gConf.GetNumPlas()) {
            SnConfigFrame::GetHiLoPlas(gConf.GetPla(pi), hi, lo);
            PIN_spi.write(hi);
            PIN_spi.write(lo);
        } else {
            PIN_spi.write(kNoTrigPla); // hi
            PIN_spi.write(kNoTrigPla); // lo
        }
    }
    wait(3);
    PIN_PLA_cs=0;
    wait(3);
    
    // DAC values
    //
    // first 12 bits = DAC value
    // next 2 bits = DAC ID
    // last 2 bits = dFPGA ID
    //
    // But FPGA uses "gray encoding" which means only 1 bit
    // can change at a time (of the last 4 bits). So even tho
    // the card/dac# is encoded, the order is also important
    // 0000 (dac0,card0), 0001 (dac0,card1), 0011 (dac0,card3), 0010 (dac0,card2),
    // 0110 (dac1,card2), 0111 (dac1,card3), 0101 (dac1,card1), etc.
    int dv=0;
    for (uint8_t i=0, gri=0; i<kTotDacs; i++) {
        // get the gray-codes for this iteration
        gri = SnBitUtils::binToGray(i);
        
        // build bit word
        dv   = static_cast<int>(gConf.GetDac(gri & 0x0003u, gri >> 2u));
        dv <<= 4u;
        dv  |= gri;
        
        // send to FPGA
        PIN_start_fpga=1;
        PIN_spi.write(dv);
        PIN_start_fpga=0;
    }
        
    wait_ms(20);
    
    // Majority Logic Trigger selection (# of cards)
    SnBitUtils::SetChanNumBits(gConf.GetNumCardsMajLog() - 1u,
                   PIN_MajLogHiBit, PIN_MajLogLoBit);
    
    // Enable thermal trigger?
    PIN_enableThermTrig = gConf.IsThermTrigEnabled();

    PIN_spi.format( 16, 1 ); // back to trigger mode
    
    Watchdog::kick(); // don't reset!

}

//
// readout functions
//
void WaitTrigAndSendClock() {

    if (gFirstEvt==false) {
        PIN_DoNotRestartAllClocks    = 0;
        wait_us(1);
        PIN_DoNotRestartAllClocks    = 1;
        //led3 = !led3; // toggle send clock led
    } else {
        gFirstEvt = false;
    }
    
    //
    // wait for a trigger here.
    //
    gReadingOut = false;  // this will allow forced triggers (see procForceTrigger())
    while ( PIN_a_sf_clk == 1 ) {
        if (gOpenCommWin) {
            // break out to open comms
            return;
        }
    }
    PIN_forceTrigger=0;   // necessary for forced triggers, harmless for other triggers
    gReadingOut = true;   // disallow new forced triggers
    
    //
    // collect data from daughter cards
    //
    
    // TODO: what if some card (set of channels) doesn't respond?
    // currently, will wait forever?
    // also, if ch1 is dead, will wait forever (due to FPGA code)
    
    for( uint8_t i = 0; i < kNsamps; i++ ) {
        if( PIN_a_sf_clk == 1 ) {
            if( i == 0 )
                wait_us( 1 );

            PIN_ADC_CS = 0;
            PIN_spi.write( 0x00 );
            PIN_ADC_CS = 1;
        } else {
            i--;
        }
    }
    
}

SnCommWin::ECommWinResult OpenCommWin() {
    // loop through each comm mode:
    //  a) try to connect
    //  b) if connected, listen for config
    //  c) if config requests data, send it
    
    gCommWinOpen = true;
    Watchdog::kick(); // don't reset!
    
    // TODO: power up comm systems
    
    gIOtimer.reset();
    gIOtimer.start();
    
    const uint32_t conto = (gConf.GetCommWinDuration() < kConnectTimeout) ?
        gConf.GetCommWinDuration() : kConnectTimeout;
    const uint32_t listo = (gConf.GetCommWinDuration() < kListenTimeout) ?
        gConf.GetCommWinDuration() : kListenTimeout;
    
    SnCommWin::ECommWinResult res = SnCommWin::kUndefFail;
    
    SnCommWin** cw = gComms;
    for (uint8_t i=0; (i<kNcomms) && 
                     (gIOtimer.read()<gConf.GetCommWinDuration()); i++, cw++) {
        
        // open window and (mabye) send status update
        const SnCommWin::ECommWinResult conres = (*cw)->OpenWindow(
            gIOtimer, conto, true, gConf, gEvent, gGenBuf, gGenBuf);
        if (conres>=SnCommWin::kConnected) {
            // connected. listen for config
            const SnCommWin::ECommWinResult cfgres = (*cw)->GetConfig(
                gConf, gIOtimer, listo, gGenBuf);
            if (cfgres>=SnCommWin::kOkWithMsg) {
                // got config. set it up.
                SetConfig();
                // send data if need be (files, some events, etc)
                if (gConf.GetCommSendData()!=0) {
                    res = (*cw)->SendData(gConf, gEvent, gGenBuf, gGenBuf);
                } else {
                    // don't send anything
                    res = cfgres;
                }
                break;
            }
        }
        
        Watchdog::kick(); // don't reset!

    }
    
    gCommWinOpen = false;
    return res;
}