diff options
Diffstat (limited to 'src/progs/gpsim/base')
-rw-r--r-- | src/progs/gpsim/base/Makefile.am | 6 | ||||
-rw-r--r-- | src/progs/gpsim/base/base.pro | 6 | ||||
-rw-r--r-- | src/progs/gpsim/base/gpsim.cpp | 155 | ||||
-rw-r--r-- | src/progs/gpsim/base/gpsim.h | 85 | ||||
-rw-r--r-- | src/progs/gpsim/base/gpsim_debug.cpp | 282 | ||||
-rw-r--r-- | src/progs/gpsim/base/gpsim_debug.h | 95 |
6 files changed, 629 insertions, 0 deletions
diff --git a/src/progs/gpsim/base/Makefile.am b/src/progs/gpsim/base/Makefile.am new file mode 100644 index 0000000..b008527 --- /dev/null +++ b/src/progs/gpsim/base/Makefile.am @@ -0,0 +1,6 @@ +INCLUDES = -I$(top_srcdir)/src $(all_includes) +METASOURCES = AUTO + +libgpsim_la_LDFLAGS = $(all_libraries) +noinst_LTLIBRARIES = libgpsim.la +libgpsim_la_SOURCES = gpsim.cpp gpsim_debug.cpp diff --git a/src/progs/gpsim/base/base.pro b/src/progs/gpsim/base/base.pro new file mode 100644 index 0000000..4b05ac5 --- /dev/null +++ b/src/progs/gpsim/base/base.pro @@ -0,0 +1,6 @@ +STOPDIR = ../../../.. +include($${STOPDIR}/lib.pro) + +TARGET = gpsim +HEADERS += gpsim.h gpsim_debug.h +SOURCES += gpsim.cpp gpsim_debug.cpp diff --git a/src/progs/gpsim/base/gpsim.cpp b/src/progs/gpsim/base/gpsim.cpp new file mode 100644 index 0000000..e6c17bd --- /dev/null +++ b/src/progs/gpsim/base/gpsim.cpp @@ -0,0 +1,155 @@ +/*************************************************************************** + * Copyright (C) 2006 Nicolas Hadacek <hadacek@kde.org> * + * * + * This program 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 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ +#include "gpsim.h" + +#include <qregexp.h> +#include "progs/base/generic_prog.h" +#include "progs/base/generic_debug.h" + +//----------------------------------------------------------------------------- +GPSim::Process::Process(Log::Base *base) + : ::Process::LineOutput(0, "gpsim_process"), Log::Base(base), _ready(false) +{ + connect(this, SIGNAL(stdoutDataReceived()), SLOT(stdoutDataReceivedSlot())); +} + +void GPSim::Process::stdoutDataReceivedSlot() +{ + if ( _stdout.startsWith("**gpsim> ") || _ready ) { + log(Log::DebugLevel::Extra, "received console prompt", Log::Delayed); + _ready = true; + emit requestSynchronousStop(); + } +} + +void GPSim::Process::addStdoutLine(const QString &line) +{ + log(Log::DebugLevel::Extra, " " + line, Log::Delayed); + if ( line.startsWith("***ERROR:") ) { + log(Log::LineType::Error, line); + return; + } + QString s = line; + QString prompt = "**gpsim> "; + while ( s.startsWith(prompt) ) { + _ready = true; + s = s.mid(prompt.length()); + } + s = s.stripWhiteSpace(); + _stdoutLines += s; +} + +//----------------------------------------------------------------------------- +GPSim::ProcessManager::ProcessManager(Log::Base *base) + : Log::Base(base), _process(base) +{} + +bool GPSim::ProcessManager::start() +{ + _process._ready = false; + _process.setup("gpsim", QStringList("-i"), false); + if ( !_process.start(0) ) { + log(Log::LineType::Error, i18n("Failed to start \"gpsim\".")); + return false; + } + return runSynchronously(); +} + +bool GPSim::ProcessManager::runSynchronously() +{ + ::Process::State state = ::Process::runSynchronously(_process, ::Process::NoRunAction, 5000); + if ( !_process.isRunning() ) { + log(Log::LineType::Error, i18n("\"gpsim\" unexpectedly exited.")); + return false; + } + if ( state==::Process::Timedout ) { + log(Log::LineType::Error, i18n("Timeout waiting for \"gpsim\".")); + return false; + } + return true; +} + +bool GPSim::ProcessManager::sendCommand(const QString &cmd, bool synchronous) +{ + _process._ready = false; + _process._stdoutLines.clear(); + _process._stdout = QString::null; + _process.writeToStdin(cmd + '\n'); + if (synchronous) return runSynchronously(); + return true; +} + +bool GPSim::ProcessManager::sendSignal(uint n, bool synchronous) +{ + _process._ready = false; + _process._stdoutLines.clear(); + _process._stdout = QString::null; + if ( !_process.signal(n) ) { + log(Log::LineType::Error, i18n("Error send a signal to the subprocess.")); + return false; + } + if (synchronous) return runSynchronously(); + return true; +} + +bool GPSim::ProcessManager::getVersion(VersionData &version) +{ + if ( !sendCommand("version", true) ) return false; + QRegExp reg("\\w*\\s*(\\d+\\.\\d+\\.\\d+).*"); + if ( _process.sout().count()==0 || !reg.exactMatch(_process.sout()[0]) ) { + version = VersionData(); + return true; + } + version = VersionData::fromString(reg.cap(1)); + return true; +} + +//----------------------------------------------------------------------------- +GPSim::Hardware::~Hardware() +{ + delete _manager; +} + +bool GPSim::Hardware::internalConnectHardware() +{ + delete _manager; + _manager = new ProcessManager(this); + _manager->process().setWorkingDirectory(_base.debugger()->directory()); + if ( !_manager->start() ) return false; + if ( !_manager->getVersion(_version) ) return false; + if ( !_version.isValid() ) { + log(Log::LineType::Error, i18n("Could not recognize gpsim version.")); + return false; + } + return true; +} + +void GPSim::Hardware::internalDisconnectHardware() +{ + delete _manager; + _manager = 0; +} + +bool GPSim::Hardware::execute(const QString &command, bool synchronous, QStringList *output) +{ + log(Log::DebugLevel::Normal, QString("command: %1").arg(command)); + if (output) output->clear(); + if ( !_manager->sendCommand(command, synchronous) ) return false; + if (output) *output = _manager->process().sout(); + return true; +} + +bool GPSim::Hardware::signal(uint n, bool synchronous, QStringList *output) +{ + log(Log::DebugLevel::Normal, QString("signal: %1").arg(n)); + if (output) output->clear(); + if ( !_manager->sendSignal(n, synchronous) ) return false; + if (output) *output = _manager->process().sout(); + return true; +} diff --git a/src/progs/gpsim/base/gpsim.h b/src/progs/gpsim/base/gpsim.h new file mode 100644 index 0000000..ab004a3 --- /dev/null +++ b/src/progs/gpsim/base/gpsim.h @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright (C) 2006 Nicolas Hadacek <hadacek@kde.org> * + * * + * This program 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 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ +#ifndef GPSIM_H +#define GPSIM_H + +#include "common/common/version_data.h" +#include "progs/base/prog_specific.h" +#include "common/global/process.h" +#include "common/global/purl.h" + +namespace GPSim +{ +//----------------------------------------------------------------------------- +class Process : public ::Process::LineOutput, public Log::Base +{ +Q_OBJECT +public: + Process(Log::Base *base); + +private slots: + void stdoutDataReceivedSlot(); + +private: + bool _ready; + + virtual void addStdoutLine(const QString &line); + + friend class ProcessManager; +}; + +//----------------------------------------------------------------------------- +class ProcessManager : public Log::Base +{ +public: + ProcessManager(Log::Base *base); + Process &process() { return _process; } + bool isReady() const { return _process._ready; } + bool start(); + bool runSynchronously(); + bool sendCommand(const QString &cmd, bool synchronous); + bool sendSignal(uint n, bool synchronous); + bool getVersion(VersionData &version); + +private: + Process _process; +}; + +//----------------------------------------------------------------------------- +class Hardware : public Programmer::Hardware +{ +public: + Hardware(::Programmer::Base &base) : Programmer::Hardware(base, 0, QString::null), _manager(0) {} + virtual ~Hardware(); + bool isReady() const { return _manager->isReady(); } + bool execute(const QString &command, bool synchronous, QStringList *output = 0); + bool signal(uint n, bool synchronous, QStringList *output = 0); + const VersionData &version() const { return _version; } + +private: + ProcessManager *_manager; + VersionData _version; + + virtual bool internalConnectHardware(); + virtual void internalDisconnectHardware(); +}; + +//----------------------------------------------------------------------------- +class DeviceSpecific : public ::Programmer::DeviceSpecific +{ +public: + DeviceSpecific(::Programmer::Base &base) : ::Programmer::DeviceSpecific(base) {} + virtual bool setPowerOff() { return false; } + virtual bool setPowerOn() { return false; } + virtual bool setTargetPowerOn(bool) { return true; } +}; + +} // namespace + +#endif diff --git a/src/progs/gpsim/base/gpsim_debug.cpp b/src/progs/gpsim/base/gpsim_debug.cpp new file mode 100644 index 0000000..b2bcec5 --- /dev/null +++ b/src/progs/gpsim/base/gpsim_debug.cpp @@ -0,0 +1,282 @@ +/*************************************************************************** + * Copyright (C) 2006 Nicolas Hadacek <hadacek@kde.org> * + * * + * This program 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 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ +#include "gpsim_debug.h" + +#include <signal.h> +#include <qregexp.h> + +#include "devices/list/device_list.h" +#include "devices/pic/base/pic_register.h" +#include "coff/base/text_coff.h" +#include "progs/manager/debug_manager.h" + +//---------------------------------------------------------------------------- +GPSim::Debugger::Debugger(Programmer &programmer) + : ::Debugger::PicBase(programmer), _nbBreakpoints(0) +{} + +bool GPSim::Debugger::internalInit() +{ + if ( !hardware()->execute("processor pic" + device()->name().lower(), true) ) return false; + if ( _inputType==PURL::Cod ) return hardware()->execute("load s " + _filename, true); + Q_ASSERT( _inputType==PURL::Hex ); + return hardware()->execute("load h " + _filename, true); +} + +bool GPSim::Debugger::internalRun() +{ + return hardware()->execute("run", false); +} + +bool GPSim::Debugger::hardHalt() +{ + log(Log::LineType::Warning, i18n("Failed to halt target: kill process.")); + _programmer.disconnectHardware(); + return true; +} + +bool GPSim::Debugger::softHalt(bool &success) +{ + success = hardware()->signal(SIGINT, true); + return true; +} + +bool GPSim::Debugger::internalStep() +{ + return hardware()->execute("step", true); +} + +bool GPSim::Debugger::setBreakpoints(const QValueList<Address> &list) +{ + for (uint i=0; i<_nbBreakpoints; i++) + if ( !hardware()->execute("clear " + QString::number(i), true) ) return false; + for (uint i=0; i<uint(list.count()); i++) + if ( !hardware()->execute("break e 0x" + toHex(list[i], nbChars(list[i].toUInt())), true) ) return false; + _nbBreakpoints = list.count(); + return true; +} + +bool GPSim::Debugger::internalReset() +{ + if ( _programmer.state()==::Programmer::Running && !halt() ) return false; + return hardware()->execute("reset", true); +} + +bool GPSim::Debugger::updateState() +{ + if ( hardware()->isReady() ) _programmer.setState(::Programmer::Halted); + else _programmer.setState(::Programmer::Running); + return true; +} + +bool GPSim::Debugger::findRegExp(const QStringList &lines, const QString &pattern, + const QString &label, QString &value) const +{ + QRegExp rexp(pattern); + uint i = 0; + for (; i<uint(lines.count()); i++) { + int offset = 0; + for (;;) { + offset = rexp.search(lines[i], offset, QRegExp::CaretAtOffset); + if ( offset==-1 || rexp.cap(1)==label ) break; + offset += rexp.cap(0).length(); + } + if ( offset!=-1 ) break; + } + if ( i==uint(lines.count()) ) return false; + value = rexp.cap(2); + return true; +} + +bool GPSim::Debugger::readWreg(BitValue &value) +{ + // #### only known for version 4 and 11 + if ( hardware()->version()<=VersionData(0, 21, 7) || hardware()->version()>=VersionData(0, 22, 0) ) + return getRegister("W", value); + QStringList lines; + if ( !hardware()->execute("dump s", true, &lines) ) return false; + QString w = (_coff->symbol("_WREG") ? "_WREG" : "W"); + QString s; + if ( !findRegExp(lines, "^\\s*[0-9A-Fa-f]+\\s+(\\w+)\\s*=\\s*([0-9A-Fa-f]+)", w, s) ) { + log(Log::LineType::Error, i18n("Error reading register \"%1\"").arg(w)); + return false; + } + value = fromHex(s, 0); + return true; +} + +bool GPSim::Debugger::getRegister(const QString &name, BitValue &value) +{ + QStringList lines; + QRegExp r; + if ( hardware()->version()<VersionData(0, 22, 0) ) { + if ( !hardware()->execute("x " + name, true, &lines) ) return false; + r.setPattern("\\w+\\s*[][\\w]+\\s*=\\s*(?:0x|)([0-9A-Fa-f]+)(?:\\W.*|)"); + } else { + if ( !hardware()->execute(name, true, &lines) ) return false; + r.setPattern("[^=]*=\\s*(?:0x|\\$)([0-9A-Fa-f]+)(?:\\W.*|)"); + } + uint i = 0; + for (; i<uint(lines.count()); i++) + if ( r.exactMatch(lines[i]) ) break; + if ( i==uint(lines.count()) ) { + log(Log::LineType::Error, i18n("Error reading register \"%1\"").arg(name)); + return false; + } + value = fromHex(r.cap(1), 0); + return true; +} + +bool GPSim::Debugger::getRegister(Address address, BitValue &value) +{ + const Pic::RegistersData &rdata = device()->registersData(); + QString name = toHex(address, rdata.nbCharsAddress()); + if ( hardware()->version()<VersionData(0, 22, 0) ) return getRegister("0x" + name, value); + return getRegister(QString("ramData[$%1]").arg(name), value); +} + +bool GPSim::Debugger::readRegister(const Register::TypeData &data, BitValue &value) +{ + if ( data.type()==Register::Special ) { + if ( data.name()=="WREG" ) return readWreg(value); + if ( data.name()=="PC" ) return getRegister("pc", value); + Q_ASSERT(false); + return true; + } + QString name = device()->registersData().sfrNames[data.address()]; + if ( name=="WREG" ) return readWreg(value); + if ( !name.isEmpty() ) return getRegister(name.lower(), value); + return getRegister(data.address(), value); +} + +bool GPSim::Debugger::setRegister(const QString &name, BitValue value) +{ + if ( hardware()->version()<VersionData(0, 22, 0) ) { + log(Log::LineType::Warning, i18n("Writing registers is not supported by this version of gpsim")); + return true; + } + const Pic::RegistersData &rdata = device()->registersData(); + QString s = QString("%1 = %2").arg(name).arg(toHexLabel(value, rdata.nbChars())); + return hardware()->execute(s, true); +} + +bool GPSim::Debugger::setRegister(Address address, BitValue value) +{ + const Pic::RegistersData &rdata = device()->registersData(); + QString s = QString("ramData[$%1]").arg(toHex(address, rdata.nbCharsAddress())); + return setRegister(s, value); +} + +bool GPSim::Debugger::writeRegister(const Register::TypeData &data, BitValue value) +{ + if ( data.type()==Register::Special ) { + if ( data.name()=="WREG" ) return writeWreg(value); + if ( data.name()=="PC" ) { + log(Log::LineType::Warning, i18n("Writing PC is not supported by gpsim")); + return true; + } + Q_ASSERT(false); + return false; + } + const Pic::RegistersData &rdata = device()->registersData(); + QString name = rdata.sfrNames[data.address()]; + if ( !name.isEmpty() ) return setRegister(name.lower(), value); + return setRegister(data.address(), value); +} + +bool GPSim::Debugger::writeWreg(BitValue value) +{ + return setRegister("W", value); +} + +bool GPSim::Debugger::updatePortStatus(uint index, QMap<uint, Device::PortBitData> &bits) +{ + for (uint i=0; i<Device::MAX_NB_PORT_BITS; i++) { + if ( !device()->registersData().hasPortBit(index, i) ) continue; + QString name = device()->registersData().portName(index).lower() + QString::number(i); + QStringList lines; + if ( !hardware()->execute("symbol " + name, true, &lines) ) return false; + QString pattern = "^(\\w+)=([^\\s])+\\s*", value; + if ( !findRegExp(lines, pattern, "bitState", value) || value.length()!=1 ) { + log(Log::LineType::Error, i18n("Error reading state of IO bit: %1").arg(name)); + return false; + } + switch (value[0].latin1()) { + case 'H': + case '1': bits[i].state = Device::High; break; + case 'L': + case '0': bits[i].state = Device::Low; break; + case 'W': bits[i].state = Device::WeakPullUp; break; + case 'w': bits[i].state = Device::WeakPullDown; break; + case 'Z': bits[i].state = Device::HighImpedance; break; + case 'X': bits[i].state = Device::Unknown; break; + default: + bits[i].state = Device::Unknown; + log(Log::LineType::Warning, i18n("Unknown state for IO bit: %1 (%2)").arg(name).arg(value)); + break; + } + if ( !findRegExp(lines, pattern, "Driving", value) || value.length()!=1 ) { + log(Log::LineType::Error, i18n("Error reading driving state of IO bit: %1").arg(name)); + return false; + } + bits[i].driving = ( value[0]=='1' ); + if (bits[i].driving) { + if ( !findRegExp(lines, pattern, "drivingState", value) || value.length()!=1 ) { + log(Log::LineType::Error, i18n("Error reading driving state of IO bit: %1").arg(name)); + return false; + } + bits[i].drivingState = (value[0]=='0' ? Device::IoLow : Device::IoHigh); + bits[i].drivenState = Device::IoUnknown; + } else { + if ( !findRegExp(lines, pattern, "drivenState", value) || value.length()!=1 ) { + log(Log::LineType::Error, i18n("Error reading driven state of IO bit: %1").arg(name)); + return false; + } + bits[i].drivenState = (value[0]=='0' ? Device::IoLow : Device::IoHigh); + bits[i].drivingState = Device::IoUnknown; + } + } + return true; +} + +//---------------------------------------------------------------------------- +QString GPSim::Group::statusLabel() const +{ + return i18n("GPSim (4MHz)"); // #### FIXME: add config +} + +void GPSim::Group::initSupported() +{ + ProcessManager manager(0); + if ( !manager.start() ) return; + VersionData version; + if ( !manager.getVersion(version) ) return; + bool oldGpsim = ( version<VersionData(0, 21, 11) ); + if ( !manager.sendCommand("processor list", true) ) return; + QStringList devices = QStringList::split(" ", manager.process().sout().join(" ")); + for (uint i=0; i<uint(devices.count()); i++) { + QString s = devices[i].upper(); + if ( s.startsWith("PIC") ) s = s.mid(3); + const Pic::Data *data = static_cast<const Pic::Data *>(Device::lister().data(s)); + if (data) { + if ( data->architecture()==Pic::Architecture::P18F && oldGpsim ) continue; + addDevice(data->name(), data, ::Group::Support::Tested); + } + } +} + +Programmer::Hardware *GPSim::Group::createHardware(::Programmer::Base &base, const ::Programmer::HardwareDescription &) const +{ + return new Hardware(base); +} + +Programmer::DeviceSpecific *GPSim::Group::createDeviceSpecific(::Programmer::Base &base) const +{ + return new DeviceSpecific(base); +} diff --git a/src/progs/gpsim/base/gpsim_debug.h b/src/progs/gpsim/base/gpsim_debug.h new file mode 100644 index 0000000..ad838b2 --- /dev/null +++ b/src/progs/gpsim/base/gpsim_debug.h @@ -0,0 +1,95 @@ +/*************************************************************************** + * Copyright (C) 2006 Nicolas Hadacek <hadacek@kde.org> * + * * + * This program 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 of the License, or * + * (at your option) any later version. * + ***************************************************************************/ +#ifndef GPSIM_DEBUG_H +#define GPSIM_DEBUG_H + +#include "gpsim.h" +#include "devices/pic/prog/pic_prog.h" +#include "devices/pic/prog/pic_debug.h" + +namespace GPSim +{ +//----------------------------------------------------------------------------- +class Programmer : public ::Programmer::PicBase +{ +Q_OBJECT +public: + Programmer(const ::Programmer::Group &group, const Pic::Data *data) + : Programmer::PicBase(group, data, "gpsim_programmer") {} + +private: + virtual VersionData minVersion() const { return VersionData(0, 21, 0); } + virtual bool verifyDeviceId() { return true; } + virtual bool checkErase() { return false; } + virtual bool internalErase(const Device::MemoryRange &) { return false; } + virtual bool checkRead() { return false; } + virtual bool internalRead(Device::Memory &, const Device::MemoryRange &) { return false; } + virtual bool checkProgram(const Device::Memory &) { return false; } + virtual bool internalProgram(const Device::Memory &, const Device::MemoryRange &) { return false; } + virtual bool checkVerify() { return false; } + virtual bool internalVerify(const Device::Memory &, const Device::MemoryRange &, ::Programmer::VerifyActions) { return false; } +}; + +//----------------------------------------------------------------------------- +class Debugger : public ::Debugger::PicBase +{ +public: + Debugger(Programmer &programmer); + virtual bool setBreakpoints(const QValueList<Address> &list); + virtual bool readRegister(const Register::TypeData &data, BitValue &value); + virtual bool writeRegister(const Register::TypeData &data, BitValue value); + virtual bool updatePortStatus(uint index, QMap<uint, Device::PortBitData> &bits); + +private: + uint _nbBreakpoints; + + bool findRegExp(const QStringList &lines, const QString &pattern, + const QString &label, QString &value) const; + bool getRegister(const QString &name, BitValue &value); + bool setRegister(const QString &name, BitValue value); + bool getRegister(Address address, BitValue &value); + bool setRegister(Address address, BitValue value); + Hardware *hardware() { return static_cast<Hardware *>(_programmer.hardware()); } + const Pic::Data *device() const { return static_cast<const Pic::Data *>(_programmer.device()); } + virtual bool internalInit(); + virtual bool updateState(); + virtual bool internalRun(); + virtual bool internalStep(); + virtual bool softHalt(bool &success); + virtual bool hardHalt(); + virtual bool internalReset(); + bool readWreg(BitValue &value); + bool writeWreg(BitValue value); +}; + +//----------------------------------------------------------------------------- +class Group : public ::Programmer::PicGroup +{ +public: + virtual QString name() const { return "gpsim"; } + virtual QString label() const { return i18n("GPSim"); } + virtual QString statusLabel() const; + virtual ::Programmer::Properties properties() const { return ::Programmer::Debugger | ::Programmer::HasConnectedState; } + virtual ::Programmer::TargetPowerMode targetPowerMode() const { return ::Programmer::TargetSelfPowered; } + virtual bool isPortSupported(PortType) const { return false; } + virtual uint maxNbBreakpoints(const Device::Data *) const { return 100; } + virtual bool isInputFileTypeSupported(PURL::FileType type) const { return ( type==PURL::Cod || type==PURL::Hex ); } + +protected: + virtual void initSupported(); + virtual ::Programmer::Base *createBase(const Device::Data *data) const { return new Programmer(*this, static_cast<const Pic::Data *>(data)); } + virtual ::Programmer::Hardware *createHardware(::Programmer::Base &base, const ::Programmer::HardwareDescription &hd) const; + virtual ::Programmer::DeviceSpecific *createDeviceSpecific(::Programmer::Base &base) const; + virtual ::Debugger::Base *createDebuggerBase(::Programmer::Base &base) const { return new Debugger(static_cast<Programmer &>(base)); } + virtual ::Debugger::Specific *createDebuggerSpecific(::Debugger::Base &) const { return 0; } +}; + +} // namespace + +#endif |