#include "SpinCore-PulseBlaster.h" #include "PulseBlasterProgram.h" #include "core/core.h" #ifndef SP_DEBUG # define SP_DEBUG 0 #endif #ifdef __linux__ # include # include # include SpinCorePulseBlasterLowlevel::SpinCorePulseBlasterLowlevel() { device_file_descriptor=open("/dev/" PULSEBLASTER_DEVICE_FILE_NAME,O_NONBLOCK|O_RDWR); if (device_file_descriptor<0) throw SpinCorePulseBlaster_error("could not open the device /dev/" PULSEBLASTER_DEVICE_FILE_NAME "\n"); } SpinCorePulseBlasterLowlevel::~SpinCorePulseBlasterLowlevel() { close(device_file_descriptor); } int SpinCorePulseBlasterLowlevel::write_data(const unsigned char* data, size_t size) { size_t orig_size=size; const unsigned int max_chunk_size=100*1<<10; // 100k commands #if SP_DEBUG stopwatch write_data_time; write_data_time.start(); #endif while (size>0) { int result=write(device_file_descriptor, data, ((size>max_chunk_size)?max_chunk_size:size) ); // error handling if (result==-1) throw SpinCorePulseBlaster_error(std::string("write_data: error \"")+strerror(errno)+"\""); if (result<0) { char errorno[256]; snprintf(errorno, 256, "%d",result); throw SpinCorePulseBlaster_error(std::string("write_register: ioctl returned negative value = ")+errorno); } // if (result==0) do some retry magic.... // success! data+=result; size-=result; } #if SP_DEBUG fprintf(stderr, "wrote %d bytes in %f s\n", orig_size, write_data_time.elapsed()); #endif return orig_size; } #endif #ifdef __CYGWIN__ SpinCorePulseBlasterLowlevel::SpinCorePulseBlasterLowlevel() { const char spincore_dll[]="spinapi"; const char sp_outp_func_name[]="pb_outp"; const char sp_inp_func_name[]="pb_inp"; const char sp_Close_func_name[]="pb_close"; const char sp_Init_func_name[]="pb_init"; PBP_DLL = LoadLibrary(spincore_dll); if (PBP_DLL==NULL) { throw SpinCorePulseBlaster_error(std::string("could not open ")+spincore_dll+" library\n"); } sp_outp = (__attribute__((stdcall))int(*)(unsigned short, int))GetProcAddress(PBP_DLL,sp_outp_func_name); if (sp_outp==NULL) { FreeLibrary(PBP_DLL); PBP_DLL=NULL; throw SpinCorePulseBlaster_error("could not access Pulseblaster PCI communication function sp_outp"); } sp_inp = (__attribute__((stdcall))int(*)(unsigned short))GetProcAddress(PBP_DLL, sp_inp_func_name); if (sp_inp==NULL) { FreeLibrary(PBP_DLL); PBP_DLL=NULL; throw SpinCorePulseBlaster_error("could not access Pulseblaster PCI communication function sp_inp"); } sp_Init = (__attribute__((stdcall))int(*)())GetProcAddress(PBP_DLL,sp_Init_func_name); if (sp_Init==NULL) { FreeLibrary(PBP_DLL); PBP_DLL=NULL; throw SpinCorePulseBlaster_error("could not access Pulseblaster Init function"); } sp_Close = (__attribute__((stdcall))int(*)())GetProcAddress(PBP_DLL, sp_Close_func_name); if (sp_Close==NULL) { FreeLibrary(PBP_DLL); PBP_DLL=NULL; throw SpinCorePulseBlaster_error("could not access Pulseblaster Close function"); } #if SP_DEBUG __attribute__((stdcall))void (*sp_set_debug)(int flag); sp_set_debug=(__attribute__((stdcall))void(*)(int))GetProcAddress(PBP_DLL,"pb_set_debug"); if (sp_set_debug!=NULL) sp_set_debug(1); else fprintf(stderr, "could not load debug function from %s DLL\n",spincore_dll); #endif int result=sp_Init(); if (result!=0) { fprintf(stderr, "sp_Init returned %d\n", result); FreeLibrary(PBP_DLL); PBP_DLL=NULL; throw SpinCorePulseBlaster_error("could not initialise Pulseblaster card"); } } SpinCorePulseBlasterLowlevel::~SpinCorePulseBlasterLowlevel() { if (sp_Close!=NULL) sp_Close(); sp_inp=NULL; sp_outp=NULL; sp_Close=NULL; sp_Init=NULL; if (PBP_DLL!=NULL) FreeLibrary(PBP_DLL); } #endif void SpinCorePulseBlaster::reset_flags(unsigned int flags) { unsigned char data[40]; data[0]=(flags&0xff000000)>>24; data[1]=(flags&0xff0000)>>16; data[2]=(flags&0xff00)>>8; data[3]=flags&0xff; write_register(0,0); // dev reset write_register(2,4); // bytes per word write_register(3,0xFF); // dev to program write_register(4,0); //reset address counter write_data(data,4); write_register(5,0); //strobe clock write_register(5,0); //strobe clock } void SpinCorePulseBlaster::set_program(const std::string& data) { if (command_length==0) throw SpinCorePulseBlaster_error("command length not set"); if (data.size()%command_length!=0) throw SpinCorePulseBlaster_error("program data length does not match command length"); if (data.size()/command_length>max_commands) { throw SpinCorePulseBlaster_error("program length exceeds maximum command number"); } write_register(0,0); // dev reset write_register(2,command_length); // bytes per word write_register(3,0); // dev to program write_register(4,0); //reset address counter write_data(data); } void SpinCorePulseBlaster::run_pulse_program_w_sync(state& exp, double sync_freq) { // set duration state_sequent* seq=dynamic_cast(&exp); if (seq==NULL) throw pulse_exception("pulse program should be a sequence"); state_iterator i(*seq); while (!i.is_last()) i.next_state(); duration=i.get_time(); #if SP_DEBUG fprintf(stderr, "caluclated time of pulse program is %g\n",duration); #endif PulseBlasterProgram* prog=create_program(exp); if (prog==NULL) throw pulse_exception("could not create PulseBlasterProgram"); if (sync_freq>0 && sync_mask!=0) { // synchronization with help of board P136 // P136 derives a single trigger slope from sampling clock of Spectrum MI4021 // using push_front(): we have to add the two commands in reverse order PulseBlasterCommand* c; // second command: clear sync mask c=prog->create_command(); c->ttls=0x0; c->instruction=SpinCorePulseBlaster::CONTINUE; c->length=shortest_pulse; prog->push_front(c); // first command: wait for monoflop on P136 up again // the monoflop will open if ADC sample clock goes through zero voltage // this synchronizes the start of the pulse program with the sampling clock c=prog->create_command(); c->ttls=sync_mask; c->instruction=SpinCorePulseBlaster::WAIT; c->length=shortest_pulse*2.0; prog->push_front(c); if (1) { // zeroth command: pulse before WAIT opcode needs to be at least 120ns // otherwise the WAIT is ignored and the program keeps repeating until pb_stop() // reported to SpinCore (Contact Request 2016-12-19 at 1482143561) // markusro: When is this necessary? (TODO) c=prog->create_command(); //c->ttls=sync_mask; c->ttls = 0x0; c->instruction=SpinCorePulseBlaster::CONTINUE; //c->length=shortest_pulse+2; // c->length=12; // markusro: why shortest_pulse+2=11 , tests show 120 ns minimum state time?? prog->push_front(c); } duration+=2.0*shortest_pulse/clock+1.0/sync_freq; } // workaround for another PulseBlaster Bug: // extra CONTINUE opcodes with little more duration to force proper initialization of all internal counters while ((prog->size()>1 && (*(++(prog->begin())))->instruction!=CONTINUE) || (prog->size()>0 && (*(prog->begin()))->instruction!=CONTINUE)) { PulseBlasterCommand* c; c=prog->create_command(); c->instruction=SpinCorePulseBlaster::CONTINUE; c->length=shortest_pulse+2; prog->push_front(c); duration+=(1.0+shortest_pulse)/clock; } // end: clear flags and stop pulseblaster prog->push_back(prog->create_command()); prog->push_back(prog->create_command()); prog->back()->instruction=STOP; #if SP_DEBUG prog->write_to_file(stderr); #endif run_pulse_program(*prog); time_running.start(); duration+=3.0*shortest_pulse/clock; delete prog; } void SpinCorePulseBlaster::wait_till_end() { double waittime=duration-time_running.elapsed(); double timeout=(waittime>10)?(waittime*0.01):0.1; #if SP_DEBUG fprintf(stderr,"waiting while pulseprogram running (%f)...",waittime); #endif // Bit zero is stopped; bit one is reset; bit two is running; bit three is waiting. int status=get_status(); #if SP_DEBUG fprintf(stderr,"status=0x%04x ",status); #endif // with synchronization, also waiting status can occur while (waittime>-timeout && core::term_signal==0 && (status&(RUNNING|WAITING))!=0) { if (waittime<1e-2) waittime=1e-2; else waittime*=0.9; #if SP_DEBUG fprintf(stderr,"sleeping for %g seconds...",waittime); fflush(stderr); #endif timespec nanosleep_time; nanosleep_time.tv_sec=(time_t)floor(waittime); nanosleep_time.tv_nsec=(long)ceil((waittime-nanosleep_time.tv_sec)*1e9); nanosleep(&nanosleep_time,NULL); waittime=duration-time_running.elapsed(); status=get_status(); #if SP_DEBUG fprintf(stderr,"status: 0x%04x\n",status); fflush(stderr); #endif } if (core::term_signal!=0) { //reset pulseblaster stop(); reset_flags(0); } if (waittime<=-timeout) { fprintf(stderr, "Pulseblaster: status=0x%04x, ran into timeout after %f s\nPulseblaster: aborting...", status, time_running.elapsed()); stop(); reset_flags(0); status=get_status(); fprintf(stderr,"now: status=0x%04x\n", status); } #if SP_DEBUG fprintf(stderr,"done\n"); #endif }