damaris-backends/drivers/SpinCore-PulseBlasterDDSIII/SpinCore-PulseBlasterDDSIII.cpp

511 lines
18 KiB
C++
Raw Permalink Normal View History

2014-06-26 11:10:51 +00:00
/* **************************************************************************
Author: Achim Gaedke
Created: June 2004
****************************************************************************/
#include "SpinCore-PulseBlasterDDSIII.h"
#include <cmath>
#include <typeinfo>
#include <iterator>
#include "core/core.h"
#include "core/states.h"
/*********************************************************************
PulseBlasterDDSIIICommand
*********************************************************************/
PulseBlasterDDSIIICommand::PulseBlasterDDSIIICommand() {
instruction=SpinCorePulseBlaster::CONTINUE;
length=0;
rx_phase_reg=0;
rx_enable=SpinCorePulseBlasterDDSIII::ANALOG_OFF;
tx_phase_reg=0;
tx_enable=SpinCorePulseBlasterDDSIII::ANALOG_OFF;
freq_reg=0;
ttls=0;
program=NULL;
}
PulseBlasterDDSIIICommand::PulseBlasterDDSIIICommand(const PulseBlasterDDSIIICommand& orig): PulseBlasterCommand(orig) {
rx_phase_reg=orig.rx_phase_reg;
rx_enable=orig.rx_enable;
tx_phase_reg=orig.tx_phase_reg;
tx_enable=orig.tx_enable;
freq_reg=orig.freq_reg;
}
PulseBlasterDDSIIICommand::PulseBlasterDDSIIICommand(PulseBlasterDDSIIIProgram& p,
double _frequency,
double _rx_phase, SpinCorePulseBlasterDDSIII::analog_state _rx_enable,
double _tx_phase, SpinCorePulseBlasterDDSIII::analog_state _tx_enable,
int _ttls, double _length)
:PulseBlasterCommand(p,_ttls,_length) {
rx_phase_reg=0;
tx_phase_reg=0;
freq_reg=0;
// allocate frequency and phases only if necessary
if (_rx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON || _tx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON) {
freq_reg=p.get_frequency_regno(_frequency);
// todo: be sure, whether tx_phase is really needed for DAC_OUT_0
// and so on...
//if (_rx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON)
rx_phase_reg=p.get_rx_phase_regno(_rx_phase);
//if (_tx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON)
tx_phase_reg=p.get_tx_phase_regno(_tx_phase);
}
rx_enable=_rx_enable;
tx_enable=_tx_enable;
}
int PulseBlasterDDSIIICommand::write_to_file(FILE* out, size_t indent) const {
if (program==NULL) throw pulse_exception("PulseBlasterDDSIII: Command not associated with Program");
fprintf(out,"%s<instruction frequency=\"%d\" rxphase=\"%d\" rxenable=\"%d\" txphase=\"%d\" txenable=\"%d\" ttls=\"%d\" ",
std::string(indent,' ').c_str(),freq_reg,rx_phase_reg,rx_enable,tx_phase_reg,tx_enable,ttls);
switch(instruction) {
case SpinCorePulseBlaster::LOOP:
fprintf(out,"inst=\"LOOP\" instdata=\"%d\"",loop_count);
break;
case SpinCorePulseBlaster::LONG_DELAY:
fprintf(out,"inst=\"LONG_DELAY\" instdata=\"%d\"",loop_count);
break;
case SpinCorePulseBlaster::BRANCH:
fprintf(out,"inst=\"BRANCH\" instdata=\"%" SIZETPRINTFLETTER "\"",std::distance(program->begin(),jump));
break;
case SpinCorePulseBlaster::END_LOOP:
fprintf(out,"inst=\"END_LOOP\" instdata=\"%" SIZETPRINTFLETTER "\"",std::distance(program->begin(),jump));
break;
case SpinCorePulseBlaster::JSR:
fprintf(out,"inst=\"JSR\" instdata=\"%" SIZETPRINTFLETTER "\"",std::distance(program->begin(),jump));
break;
case SpinCorePulseBlaster::CONTINUE:
fprintf(out,"inst=\"CONTINUE\"");
break;
case SpinCorePulseBlaster::STOP:
fprintf(out,"inst=\"STOP\"");
break;
case SpinCorePulseBlaster::RTS:
fprintf(out,"inst=\"RTS\"");
break;
case SpinCorePulseBlaster::WAIT:
fprintf(out,"inst=\"WAIT\"");
break;
default:
fprintf(out,"inst=\"%d\" instdata=\"UNKNOWN\"",instruction);
}
fprintf(out," length=\"%g\"/>\n",1.0/program->internal_clock_freq*length);
return 1;
}
/*********************************************************************
PulseBlasterDDSIIIProgram
*********************************************************************/
PulseBlasterDDSIIIProgram::PulseBlasterDDSIIIProgram() {
internal_clock_freq=100.0e6; // Hz
phase_accuracy=360.0/pow(2,12); // degree, manual page 8
freq_accuracy=internal_clock_freq/pow(2,28); // Hz, manual page 8
minimum_interval=9; // in clock cycles
}
PulseBlasterDDSIIIProgram::PulseBlasterDDSIIIProgram(const SpinCorePulseBlasterDDSIII& pbddsiii) {
internal_clock_freq=pbddsiii.clock; // Hz
minimum_interval=pbddsiii.shortest_pulse; // in clock cycles
phase_accuracy=360.0/pow(2,12); // degree, manual page 8
freq_accuracy=internal_clock_freq/pow(2,28); // Hz, manual page 8
}
PulseBlasterDDSIIIProgram::PulseBlasterDDSIIIProgram(const PulseBlasterDDSIIIProgram& orig): PulseBlasterProgram(orig) {
freq_accuracy=orig.freq_accuracy;
phase_accuracy=orig.phase_accuracy;
frequency_registers=orig.frequency_registers;
rx_phase_registers=orig.rx_phase_registers;
clear();
const_iterator orig_i;
for (orig_i=orig.begin(); orig_i!=orig.end(); ++orig_i) {
const PulseBlasterDDSIIICommand* the_command=dynamic_cast<const PulseBlasterDDSIIICommand*>(*orig_i);
if (the_command==NULL) {
throw SpinCorePulseBlaster_error("wrong command class or NULL pointer found in program");
}
PulseBlasterDDSIIICommand* new_one=new PulseBlasterDDSIIICommand(*the_command);
new_one->program=this;
push_back(new_one);
}
// set correct references...
orig_i=orig.begin();
for(iterator i=begin();i!=end();++i) {
(**i).program=this;
if ((**i).instruction==SpinCorePulseBlaster::END_LOOP || (**i).instruction==SpinCorePulseBlaster::BRANCH || (**i).instruction==SpinCorePulseBlaster::JSR) {
(**i).jump=i;
advance((**i).jump,distance(orig.begin(),(**orig_i).jump)-distance(orig.begin(),orig_i));
}
++orig_i;
}
}
int PulseBlasterDDSIIIProgram::write_to_file(FILE* out, size_t indent) const {
std::string indent_string(indent,' ');
fprintf(out,"%s<PulseBlasterDDSIIIProgram>\n",indent_string.c_str());
if (!frequency_registers.empty()) {
fprintf(out,"%s <frequencies>",indent_string.c_str());
for (size_t i=0; i<frequency_registers.size(); i++)
fprintf(out," %g",frequency_registers[i]);
fprintf(out," </frequencies>\n");
}
if (!rx_phase_registers.empty()) {
fprintf(out,"%s <rxphases>",indent_string.c_str());
for (size_t i=0;i<rx_phase_registers.size();i++)
fprintf(out," %g",rx_phase_registers[i]);
fprintf(out," </rxphases>\n");
}
if (!tx_phase_registers.empty()) {
fprintf(out,"%s <txphases>",indent_string.c_str());
for (size_t i=0;i<tx_phase_registers.size();i++)
fprintf(out," %g",tx_phase_registers[i]);
fprintf(out," </txphases>\n");
}
for(const_iterator i=begin();i!=end();++i) {
(**i).write_to_file(out,indent+2);
}
fprintf(out,"%s</PulseBlasterDDSIIIProgram>\n",indent_string.c_str());
return 1;
}
int PulseBlasterDDSIIIProgram::get_frequency_regno(double f) {
size_t i=0;
while (i<frequency_registers.size() && fabs(frequency_registers[i]-f)>freq_accuracy) ++i;
if (i==frequency_registers.size())
frequency_registers.push_back(f);
return i;
}
int PulseBlasterDDSIIIProgram::get_rx_phase_regno(double p) {
size_t i=0;
while (i<rx_phase_registers.size() && fabs(rx_phase_registers[i]-p)>phase_accuracy) ++i;
if (i==rx_phase_registers.size())
rx_phase_registers.push_back(p);
return i;
}
int PulseBlasterDDSIIIProgram::get_tx_phase_regno(double p) {
size_t i=0;
while (i<tx_phase_registers.size() && fabs(tx_phase_registers[i]-p)>phase_accuracy) ++i;
if (i==tx_phase_registers.size())
tx_phase_registers.push_back(p);
return i;
}
PulseBlasterCommand* PulseBlasterDDSIIIProgram::create_command(const state& the_state) {
if (typeid(the_state)!=typeid(state))
throw pulse_exception("tried to make a state from a sequence");
// todo: state's defaults
/*
There are three analog outputs on this card, but only two phase registers and two gates:
id=0, DAC_OUT0: influenced by rx1_specified, tx_phase, rx_enable
id=1, DAC_OUT1: influenced by rx2_specified, rx_phase, rx_enable
id=2, DAC_OUT2: influenced by tx_specified, tx_phase, tx_enable
all channels have the same frequency
*/
double length=the_state.length;
double state_frequency=0;
unsigned long ttls=0;
double rx_phase=0;
SpinCorePulseBlasterDDSIII::analog_state rx_enable=SpinCorePulseBlasterDDSIII::ANALOG_OFF;
double tx_phase=0;
SpinCorePulseBlasterDDSIII::analog_state tx_enable=SpinCorePulseBlasterDDSIII::ANALOG_OFF;
int id0_specified=0;
int id1_specified=0;
int id2_specified=0;
for (state::const_iterator i=the_state.begin(); i!=the_state.end(); ++i) {
// collect states information
const ttlout* to=dynamic_cast<const ttlout*>(*i);
if (to!=NULL && to->id==0) {
ttls|=to->ttls.to_ulong();
continue;
}
// add frequency information
const analogout* ao=dynamic_cast<const analogout*>(*i);
if (ao!=NULL && ao->id>=0 && ao->id<=2) {
if ((rx_enable!=SpinCorePulseBlasterDDSIII::ANALOG_OFF ||
tx_enable!=SpinCorePulseBlasterDDSIII::ANALOG_OFF) &&
fabs(state_frequency-ao->frequency)>freq_accuracy)
throw pulse_exception("only one frequency for analog outputs possible");
state_frequency=ao->frequency;
double phase=ao->phase;
if (phase < 0 || phase >= 360.0) {
phase=fmod(phase, 360.0);
if (phase<0) {phase+=360.0;}
}
assert(phase>=0 && phase<360.0);
switch(ao->id) {
case 0:
if (id0_specified) throw pulse_exception("rx channel (DAC_OUT_0) channel already set");
//if (rx_enable!=SpinCorePulseBlasterDDSIII::ANALOG_OFF) throw pulse_exception("rx channel already set");
// rx is identified with channel 0
if (id2_specified && fabs(phase-tx_phase)>phase_accuracy) fprintf(stderr, "WARNING from PulseBlaster DDSIII: redefining phase of TX (DAC_OUT_2) channel\n");
tx_phase=phase;
rx_enable=SpinCorePulseBlasterDDSIII::ANALOG_ON;
id0_specified=1;
break;
case 1:
if (id1_specified) throw pulse_exception("rx channel (DAC_OUT_1) channel already set");
// tx is identified with channel 1
rx_phase=phase;
rx_enable=SpinCorePulseBlasterDDSIII::ANALOG_ON;
id1_specified=1;
break;
case 2:
if (id2_specified || tx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON) throw pulse_exception("tx channel (DAC_OUT_2) already set");
// tx is identified with channel 1
if (id0_specified && fabs(phase-tx_phase)>phase_accuracy) fprintf(stderr, "WARNING from PulseBlaster DDSIII: redefining phase of RX (DAC_OUT_0) channel\n");
tx_phase=phase;
tx_enable=SpinCorePulseBlasterDDSIII::ANALOG_ON;
id2_specified=1;
break;
}
continue;
}
}
#if SP_DEBUG
fprintf(stderr, "rx phase=%f, tx phase=%f\n",rx_phase, tx_phase);
#endif
if (!id0_specified && rx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON)
fprintf(stderr, "WARNING from PulseBlaster DDSIII: RF Output enabled on DAC_OUT_0\n");
if (!id1_specified && rx_enable==SpinCorePulseBlasterDDSIII::ANALOG_ON)
fprintf(stderr, "WARNING from PulseBlaster DDSIII: RF Output enabled on DAC_OUT_1\n");
return new PulseBlasterDDSIIICommand(*this,state_frequency,rx_phase,rx_enable,tx_phase,tx_enable,ttls,length);
}
PulseBlasterCommand* PulseBlasterDDSIIIProgram::create_command(const PulseBlasterCommand* orig) {
PulseBlasterDDSIIICommand* new_command=NULL;
if (orig==NULL) {
new_command=new PulseBlasterDDSIIICommand();
new_command->program=this;
new_command->length=minimum_interval;
}
else {
const PulseBlasterDDSIIICommand* commandDDSIII=dynamic_cast<const PulseBlasterDDSIIICommand*>(orig);
if (commandDDSIII==NULL) throw pulse_exception("wrong PulseBlasterCommand class in PulseBlasterDDSIIIProgram method");
new_command=new PulseBlasterDDSIIICommand(*commandDDSIII);
}
return new_command;
}
/*********************************************************************
SpinCorePulseBlasterDDSIII
*********************************************************************/
SpinCorePulseBlasterDDSIII::SpinCorePulseBlasterDDSIII(int the_id, double the_clock, unsigned int _sync_mask): SpinCorePulseBlaster(10,the_clock) {
sync_mask=_sync_mask;
ttl_device_id=the_id;
freq_regno=16;
phase_regno=16;
}
void SpinCorePulseBlasterDDSIII::set_registers(int device, unsigned int register_size, double multiplier, const std::vector<double>& values) {
if (values.size()>register_size)
throw SpinCorePulseBlaster_error("to many data for registers");
unsigned char* data=(unsigned char*)malloc(4*register_size);
if (data==NULL) {
throw SpinCorePulseBlaster_error("could not allocate memory for register data");
}
for (unsigned int reg=0; reg<register_size; ++reg) {
if (values.size()>reg) {
double temp=values[reg]*multiplier;
if (temp<0 || temp>pow(2,32))
throw SpinCorePulseBlaster_error("invalid data value");
unsigned int val=(unsigned int)temp;
data[reg*4]=(val&0xff000000)>>24;
data[reg*4+1]=(val&0xff0000)>>16;
data[reg*4+2]=(val&0xff00)>>8;
data[reg*4+3]=val&0xff;
}
else {
data[reg*4]=0;
data[reg*4+1]=0;
data[reg*4+2]=0;
data[reg*4+3]=0;
}
}
write_register(0,0); // dev reset
write_register(2,4); // bytes per word
write_register(3,device); // dev to program
write_register(4,0); //reset address counter
write_data(data,4*register_size);
free(data);
}
void SpinCorePulseBlasterDDSIII::set_phase_registers(std::vector<double> rx_phases, std::vector<double> tx_phases) {
if (tx_phases.empty())
set_registers(2,phase_regno,1.0,std::vector<double>(1,0.0));
else
set_registers(2,phase_regno,pow(2,32)/360.0,tx_phases);
if (rx_phases.empty())
set_registers(3,phase_regno,1.0,std::vector<double>(1,0.0));
else
set_registers(3,phase_regno,pow(2,32)/360.0,rx_phases);
}
void SpinCorePulseBlasterDDSIII::set_frequency_registers(const std::vector<double>& values) {
if (values.empty())
set_registers(1,freq_regno,pow(2,32)/clock,std::vector<double>(1,1e6));
else
set_registers(1,freq_regno,pow(2,32)/clock,values);
}
void SpinCorePulseBlasterDDSIII::write_command(unsigned char* data, const PulseBlasterCommand& command) {
//void PulseBlasterDDSIII::append_command(std::string& data, int freqreg, int phasereg_tx, int output_tx, int phasereg_rx, int output_rx, int flags, opcode inst, int inst_data, double length)
const PulseBlasterDDSIIICommand* commandDDSIII=dynamic_cast<const PulseBlasterDDSIIICommand*>(&command);
if (commandDDSIII==NULL)
throw SpinCorePulseBlaster_error("found wrong command class in PulseBlasterProgram");
if (command.program==NULL) throw SpinCorePulseBlaster_error("Command not associated with Program");
int inst_data=0;
switch(command.instruction) {
case SpinCorePulseBlaster::CONTINUE:
case SpinCorePulseBlaster::STOP:
case SpinCorePulseBlaster::WAIT:
case SpinCorePulseBlaster::RTS:
// no parameter
break;
case SpinCorePulseBlaster::LOOP:
inst_data=command.loop_count-1;
break;
case SpinCorePulseBlaster::LONG_DELAY:
inst_data=command.loop_count-2;
break;
case SpinCorePulseBlaster::BRANCH:
case SpinCorePulseBlaster::END_LOOP:
case SpinCorePulseBlaster::JSR:
inst_data=std::distance(command.program->begin(),command.jump);
break;
default:
throw SpinCorePulseBlaster_error("instruction code not known");
}
unsigned int delay=(unsigned int)command.length-3;
// Output, Control Word 1st Byte
data[0]=(commandDDSIII->freq_reg&0x0f)<<4|(commandDDSIII->tx_phase_reg&0x0f);
// Output, Control Word 2nd Byte
data[1]=((commandDDSIII->rx_phase_reg&0x0f)<<4)|((command.ttls&0x300)>>8);
if (commandDDSIII->rx_enable==ANALOG_OFF) data[1]|=0x04;
if (commandDDSIII->tx_enable==ANALOG_OFF) data[1]|=0x08;
// Output, Control Word 3rd Byte
data[2]=command.ttls&0xff;
// Data Field 1st Byte
data[3]=(inst_data&0x0ff000)>>12;
// Data Field 2nd Byte
data[4]=(inst_data&0xff0)>>4;
// Data Field 3rd Byte and opcode
data[5]=(inst_data&0xf)<<4|(command.instruction&0xf);
// Delay Count 1st Byte
data[6]=(delay&0xff000000)>>24;
// Delay Count 2nd Byte
data[7]=(delay&0xff0000)>>16;
// Delay Count 3rd Byte
data[8]=(delay&0xff00)>>8;
// Delay Count 4th Byte
data[9]=(delay&0xff);
/* *** BUG-FIX (ToDo: clean solution) ***
there is a nasty error in pulseblaster, affecting all states with 4th byte
equal 0xff and delay >255. In this case reduce state for 10ns.
*/
if (data[9]==0xff && delay>0xff)
data[9]=0xfe;
}
int SpinCorePulseBlasterDDSIII::write_to_device(const PulseBlasterDDSIIIProgram& p) {
set_frequency_registers(p.frequency_registers);
set_phase_registers(p.rx_phase_registers,p.tx_phase_registers);
std::string program;
//Begin pulse program
for (PulseBlasterDDSIIIProgram::const_iterator c=p.begin(); c!=p.end();++c) {
char command[10];
write_command((unsigned char*)command,**c);
program.append(command, (size_t)10);
}
set_program(program);
// End of programming registers and pulse program
set_initialized();
return 1;
}
PulseBlasterProgram* SpinCorePulseBlasterDDSIII::create_program(state& exp) {
PulseBlasterDDSIIIProgram* prog=new PulseBlasterDDSIIIProgram();
// some initialisiation...
prog->append_sequence(exp);
// some reset code ...
return prog;
}
void SpinCorePulseBlasterDDSIII::run_pulse_program(const PulseBlasterProgram& p) {
const PulseBlasterDDSIIIProgram* prog=dynamic_cast<const PulseBlasterDDSIIIProgram*>(&p);
if (prog==NULL)
throw SpinCorePulseBlaster_error("found wrong program class in SpinCorePulseBlasterDDSIII method");
write_to_device(*prog);
start();
}
void SpinCorePulseBlasterDDSIII::wait_till_end() {
double waittime=duration-time_running.elapsed();
double timeout=(waittime>10)?(waittime*0.01):0.05;
#if SP_DEBUG
fprintf(stderr,"waiting while DDSIII pulseprogram running (%f s of %f s)...", waittime, duration);
#endif
while (waittime>-timeout && core::term_signal==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();
}
if (core::term_signal!=0) {
//reset pulseblaster
stop();
reset_flags(0);
}
#if SP_DEBUG
fprintf(stderr,"done\n");
#endif
}