64fb1ed1fe
WAIT opcode is ignored if preceeding state is shorter than 120ns.
275 lines
9.2 KiB
C++
275 lines
9.2 KiB
C++
#include "SpinCore-PulseBlaster.h"
|
|
#include "PulseBlasterProgram.h"
|
|
#include "core/core.h"
|
|
|
|
#ifndef SP_DEBUG
|
|
# define SP_DEBUG 0
|
|
#endif
|
|
|
|
#ifdef __linux__
|
|
# include <sys/types.h>
|
|
# include <sys/stat.h>
|
|
# include <unistd.h>
|
|
|
|
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<state_sequent*>(&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;
|
|
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->instruction=SpinCorePulseBlaster::CONTINUE;
|
|
//c->length=shortest_pulse+2; // markusro: why +2 ??
|
|
c->length=12; // markusro: why +2 ??
|
|
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
|
|
}
|