/*
Copyright (C) 2006 Scott Dattalo
This file is part of gpsim.
gpsim is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
gpsim is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with gpsim; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/*
stimuli.cc
This module provides extended stimuli for gpsim.
pulsegen - This a stimulus designed to synthesize square waves.
It has the following attributes:
.set
.clear
.delete
.period
The set and clear attributes generate the edge states.
For example if the module is instantiated as PG, then
gpsim> PG.set = 0x1000
gpsim> PG.clear = 0x2000
generate a rising edge at cycle 0x1000 and a falling edge at
cycle 0x2000. Any number of edges can be specified and they
may be in any order. The delete attribute removes an edge:
gpsim> PG.delete = 0x2000 # remove the edge at cycle 0x2000
The period attribute tells how many cycles there are in a
rollover. If period is 0, then the pulsegen is not periodic.
If there are edges beyond the period time, then those will be
ignored.
To be added:
.start - specify a cycle to start
.vhi - voltage for high drive
.vlo - voltage for low drive
.rth - output resitance
.cth - output capacitance.
pwlgen - piecewise linear generator.
<not implemented>
filegen - time and values are specified in a file.
<not implemented>
*/
/* IN_MODULE should be defined for modules */
#define IN_MODULE
#include "../src/gpsim_time.h"
#include "stimuli.h"
#include "../config.h"
#include "../src/pic-ioports.h"
#include "../src/symbol.h"
#include "../src/trace.h"
#include "../src/processor.h"
namespace ExtendedStimuli {
//----------------------------------------------------------------------
// Attributes
//
class PulseAttribute : public Integer
{
public:
PulseAttribute(PulseGen *pParent, const char *_name, const char * desc, double voltage);
virtual void set(gint64);
private:
PulseGen *m_pParent;
double m_voltage;
};
class PulsePeriodAttribute : public Integer
{
public:
PulsePeriodAttribute(PulseGen *pParent, const char *_name, const char * desc);
virtual void set(gint64);
private:
PulseGen *m_pParent;
};
//----------------------------------------------------------------------
//----------------------------------------------------------------------
PulseAttribute::PulseAttribute(PulseGen *pParent,
const char *_name, const char * desc,
double voltage)
: Integer(_name,0,desc), m_pParent(pParent), m_voltage(voltage)
{
}
void PulseAttribute::set(gint64 i)
{
Integer::set(i);
ValueStimulusData vsd;
vsd.time = i;
vsd.v = new Float(m_voltage);
m_pParent->put_data(vsd);
}
//----------------------------------------------------------------------
PulsePeriodAttribute::PulsePeriodAttribute(PulseGen *pParent,
const char *_name, const char * desc)
: Integer(_name,0,desc), m_pParent(pParent)
{
}
void PulsePeriodAttribute::set(gint64 i)
{
Integer::set(i);
m_pParent->update_period();
}
//----------------------------------------------------------------------
// StimulusBase
//----------------------------------------------------------------------
StimulusBase::StimulusBase(const char *_name, const char *_desc)
: Module(_name,_desc)
{
// Default module attributes.
initializeAttributes();
// The I/O pin
m_pin = new IO_bi_directional((name() + ".pin").c_str());
m_pin->update_direction(IOPIN::DIR_OUTPUT,true);
}
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void StimulusBase::callback_print()
{
printf("ExtendedStimulus:%s CallBack ID %d\n",name().c_str(),CallBackID);
}
void StimulusBase::create_iopin_map()
{
create_pkg(1);
assign_pin(1, m_pin);
}
//----------------------------------------------------------------------
// FileGen Module
//----------------------------------------------------------------------
//FileGen::FileGen
//----------------------------------------------------------------------
// PulseGen Module
//----------------------------------------------------------------------
Module *PulseGen::construct(const char *new_name)
{
PulseGen *pPulseGen = new PulseGen(new_name);
pPulseGen->create_iopin_map();
return pPulseGen;
}
//----------------------------------------------------------------------
//----------------------------------------------------------------------
PulseGen::PulseGen(const char *_name=0)
: StimulusBase(_name, "\
Pulse Generator\n\
Attributes:\n\
.set - time when the pulse will drive high\n\
.clear - time when the pulse will drive low\n\
.period - time the pulse stream is repeated\n\
"), m_future_cycle(0), m_start_cycle(0)
{
// Attributes for the pulse generator.
m_set = new PulseAttribute(this, "set","r/w cycle time when ouput will be driven high", 5.0);
m_clear = new PulseAttribute(this, "clear","r/w cycle time when ouput will be driven low",0.0);
m_period = new PulsePeriodAttribute(this,
"period","r/w cycle time to specify pulse stream repeat rate");
add_attribute(m_set);
add_attribute(m_clear);
add_attribute(m_period);
sample_iterator = samples.end();
}
PulseGen::~PulseGen()
{
delete m_pin;
delete m_set;
delete m_clear;
delete m_period;
}
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void PulseGen::callback()
{
//guint64 currCycle = get_cycles().get();
if (sample_iterator != samples.end()) {
double d;
(*sample_iterator).v->get(d);
m_pin->putState(d > 2.5);
++sample_iterator;
// If we reached the end of a non-periodic waveform
// then we're done.
if (sample_iterator == samples.end() &&
m_period->getVal() == 0)
return;
// If this is a periodic pulse stream and either
// a) we reached the end of the sequence
// b) we have more data but it exceeds the period
// then
// start the stream over.
if (m_period->getVal() && ((sample_iterator == samples.end() ||
(*sample_iterator).time > m_period->getVal()))) {
sample_iterator = samples.begin();
m_start_cycle += m_period->getVal();
}
m_future_cycle = m_start_cycle + (*sample_iterator).time;
get_cycles().set_break(m_future_cycle, this);
}
}
//------------------------------------------------------------
// Set a callback break point at the cycle specified
// Point the sample_iterator to the appropiate sample
void PulseGen::setBreak(guint64 next_cycle,list<ValueStimulusData>::iterator si)
{
if (m_future_cycle) {
get_cycles().clear_break(this);
m_future_cycle = 0;
sample_iterator = samples.end();
}
if (next_cycle > get_cycles().get()) {
get_cycles().set_break(next_cycle, this);
m_future_cycle = next_cycle;
sample_iterator = si;
}
}
//------------------------------------------------------------
// cycleIsInFuture - find_if helper predicate.
//
// The stl find_if() algorithm takes a pointer to a function whose
// job is to compare a list element to a reference value. Stroustrop
// calls this function a predicate.
static guint64 current_cycle=0;
static bool cycleIsInFuture(ValueStimulusData &data_point)
{
return data_point.time > current_cycle;
}
void PulseGen::update_period()
{
// If the period is 0 then force the start to 0.
if (m_period->getVal() == 0)
m_start_cycle = 0;
// Find the next sample that will generate an edge.
list<ValueStimulusData>::iterator si;
current_cycle = get_cycles().get() - m_start_cycle;
si = find_if(samples.begin(), samples.end(), cycleIsInFuture);
if (si == samples.end() && m_period->getVal())
setBreak(m_start_cycle + m_period->getVal(), samples.begin());
}
void PulseGen::update()
{
if (samples.empty())
return; // There are no samples
current_cycle = get_cycles().get();
list<ValueStimulusData>::iterator si;
if (current_cycle == 0) {
// The simulation hasn't started yet.
// Point to the second sample
si = samples.begin();
++si;
// If the next sample *is* the second one then we've been here before
// (and that means we've already handled the first sample)
if (sample_iterator == si)
return;
if (si == samples.end()) {
si = samples.begin();
sample_iterator = si;
double d;
(*si).v->get(d);
m_pin->putState(d > 2.5);
}
sample_iterator = si;
--si;
double d;
(*si).v->get(d);
m_pin->putState(d > 2.5);
setBreak((*sample_iterator).time, sample_iterator);
return;
}
current_cycle -= m_start_cycle;
si = find_if(samples.begin(), samples.end(), cycleIsInFuture);
if (si == sample_iterator)
return;
setBreak(m_start_cycle + (*si).time, si);
}
void PulseGen::put_data(ValueStimulusData &data_point)
{
list<ValueStimulusData>::iterator si;
si = find(samples.begin(), samples.end(), data_point);
if (si == samples.end()) {
samples.push_back(data_point);
samples.sort();
} else {
delete (*si).v;
(*si).v = data_point.v;
}
update();
}
string PulseGen::toString()
{
ostringstream sOut;
sOut << "pulsegen toString method" << hex;
list<ValueStimulusData>::iterator si;
if (m_period->getVal())
sOut << endl <<"period 0x" << m_period->getVal();
if (m_start_cycle)
sOut << endl << "start 0x" << m_start_cycle;
si = samples.begin();
while (si != samples.end()) {
sOut << endl;
double d;
(*si).v->get(d);
sOut << " {0x" << (*si).time << ',' << d << '}';
if (si == sample_iterator)
sOut << " <-- Next at cycle 0x" << (m_start_cycle + (*si).time);
++si;
}
sOut << ends;
return sOut.str();
}
//----------------------------------------------------------------------
// File Stimulus
//----------------------------------------------------------------------
class FileNameAttribute : public String
{
public:
FileNameAttribute(FileStimulus *, const char *_name, const char * desc);
virtual void set(Value *);
const char *getLine();
const FILE *fp() { return m_pFile; }
private:
FileStimulus *m_Parent;
FILE *m_pFile;
enum {
eBuffSize = 1024
};
char m_buff[eBuffSize];
};
//=1024;
FileNameAttribute::FileNameAttribute(FileStimulus *pParent,
const char *_name,
const char * _desc)
: String(_name,"",_desc), m_Parent(pParent), m_pFile(0)
{
}
void FileNameAttribute::set(Value *pV)
{
if (m_pFile)
return;
String::set(pV);
m_pFile = fopen( getVal(), "r");
m_Parent->newFile();
}
const char *FileNameAttribute::getLine()
{
m_buff[0]=0;
if (m_pFile)
fgets(m_buff, eBuffSize, m_pFile);
return m_buff;
}
//----------------------------------------------------------------------
//----------------------------------------------------------------------
FileStimulus::FileStimulus(const char *_name)
: StimulusBase(_name, "\
File Stimulus\n\
Attributes:\n\
.file - file name\n\
"), m_future_cycle(0)
{
// Attributes for the pulse generator.
m_file = new FileNameAttribute(this, "file","name of file or pipe supplying data");
add_attribute(m_file);
}
FileStimulus::~FileStimulus()
{
}
void FileStimulus::newFile()
{
if (m_future_cycle) {
get_cycles().clear_break(this);
m_future_cycle = 0;
}
parse(m_file->getLine());
}
void FileStimulus::parse(const char *cP)
{
if (!cP)
return;
guint64 t;
float v;
//fscanf(m_file,"%" PRINTF_INT64_MODIFIER "i %g",&t, &v);
sscanf(cP,"%" PRINTF_INT64_MODIFIER "i %g",&t, &v);
cout << " read 0x" << hex << t << "," << v << endl;
}
void FileStimulus::callback()
{
}
string FileStimulus::toString()
{
ostringstream sOut;
sOut << "fileStimulus toString method" << endl;
sOut << ends;
return sOut.str();
}
//----------------------------------------------------------------------
//----------------------------------------------------------------------
class RegisterAddressAttribute : public Integer
{
public:
RegisterAddressAttribute(Register *pReg, const char *_name, const char * desc);
virtual void set(gint64);
private:
Register *m_replaced;
const int InvalidAddress;
};
RegisterAddressAttribute::RegisterAddressAttribute(Register *pReg,const char *_name, const char * desc)
: Integer(_name,0xffffffff,desc),
m_replaced(pReg),
InvalidAddress(0xffffffff)
{
m_replaced->address = InvalidAddress;
}
void RegisterAddressAttribute::set(gint64 i)
{
Processor *pcpu = get_active_cpu();
if (pcpu && m_replaced) {
if (m_replaced->address != InvalidAddress)
pcpu->rma.removeRegister(m_replaced->address,m_replaced);
m_replaced->set_cpu(pcpu);
m_replaced->address = i & 0xffffffff;
if (!pcpu->rma.insertRegister(m_replaced->address,m_replaced))
m_replaced->address = InvalidAddress;
gint64 insertAddress = m_replaced->address;
Integer::set(insertAddress);
}
}
//----------------------------------------------------------------------
//----------------------------------------------------------------------
static void buildTraceType(Register *pReg, unsigned int baseType)
{
RegisterValue rv;
rv = RegisterValue(baseType + (0<<8), baseType + (1<<8));
pReg->set_write_trace(rv);
rv = RegisterValue(baseType + (2<<8), baseType + (3<<8));
pReg->set_read_trace(rv);
}
//----------------------------------------------------------------------
//----------------------------------------------------------------------
Module *PortStimulus::construct(const char *new_name)
{
PortStimulus *pPortStimulus = new PortStimulus(new_name);
pPortStimulus->create_iopin_map();
return pPortStimulus;
}
//----------------------------------------------------------------------
//----------------------------------------------------------------------
PortStimulus::PortStimulus(const char *_name)
: Module(_name, "\
Port Stimulus\n\
Attributes:\n\
.port - port name\n\
.tris - tris name\n\
.lat - latch name\n\
")
{
mPort = new PicPortRegister((name()+".port").c_str(),8,0xff);
mTris = new PicTrisRegister((name()+".tris").c_str(),mPort);
mLatch = new PicLatchRegister((name()+".lat").c_str(),mPort);
mLatch->setEnableMask(0xff);
mPortAddress = new RegisterAddressAttribute(mPort, "portAdr","Port register address");
mTrisAddress = new RegisterAddressAttribute(mTris, "trisAdr","Tris register address");
mLatchAddress = new RegisterAddressAttribute(mLatch, "latAdr","Latch register address");
get_symbol_table().add_register(mPort);
get_symbol_table().add_register(mTris);
get_symbol_table().add_register(mLatch);
add_attribute(mPortAddress);
add_attribute(mTrisAddress);
add_attribute(mLatchAddress);
// FIXME - probably want something better than the generic module trace
ModuleTraceType *mMTT = new ModuleTraceType(this,1," Port Stimulus");
get_trace().allocateTraceType(mMTT);
buildTraceType(mPort, mMTT->type());
buildTraceType(mTris, mMTT->type() + (4<<8));
buildTraceType(mLatch, mMTT->type() + (8<<8));
}
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void PortStimulus::callback_print()
{
printf("PortStimulus:%s CallBack ID %d\n",name().c_str(),CallBackID);
}
void PortStimulus::create_iopin_map()
{
create_pkg(8);
for (int i=0; i<8; i++) {
char pinNumber = '1'+i;
IO_bi_directional *ppin;
ppin = new IO_bi_directional((name() + ".p" + pinNumber).c_str());
ppin->update_direction(IOPIN::DIR_OUTPUT,true);
assign_pin(i+1, mPort->addPin(ppin,i));
}
}
} // end of namespace ExtendedStimuli
syntax highlighted by Code2HTML, v. 0.9.1