/* Copyright (C) 1998 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. */ #include #include #include #include #include #include "../config.h" #include "cmd_gpsim.h" #include "ioports.h" #include "trace.h" #include "stimuli.h" #include "xref.h" //#define DEBUG #if defined(DEBUG) #define Dprintf(arg) {printf("%s:%d ",__FILE__,__LINE__); printf arg; } #else #define Dprintf(arg) {} #endif //-------------------------------------------------- // //-------------------------------------------------- PeripheralSignalSource::PeripheralSignalSource(PinModule *_pin) : m_pin(_pin), m_cState('?') { assert(m_pin); } // getState is called when the PinModule is attempting to // update the output state for the I/O Pin. char PeripheralSignalSource::getState() { return m_cState; } /// putState is called when the peripheral output source /// wants to change the output state. void PeripheralSignalSource::putState(const char new3State) { if (new3State != m_cState) { m_cState = new3State; m_pin->updatePinModule(); } } void PeripheralSignalSource::toggle() { switch (m_cState) { case '1': case 'W': putState('0'); break; case '0': case 'w': putState('1'); break; } } //------------------------------------------------------------------- // // ioports.cc // // The ioport infrastructure for gpsim is provided here. The class // taxonomy for the IOPORT class is: // // file_register // |-> sfr_register // |-> IOPORT // |-> PORTA // |-> PORTB // |-> PORTC // |-> PORTD // |-> PORTE // |-> PORTF // // Each I/O port has an associated array of I/O pins which provide an // interface to the virtual external world of the stimuli. // //------------------------------------------------------------------- class SignalSource : public SignalControl { public: SignalSource(PortRegister *_reg, unsigned int bitPosition) : m_register(_reg), m_bitMask(1<getDriving()&m_bitMask)!=0)?'1':'0') : 'Z'; char r = m_register ? (((m_register->getDriving()&m_bitMask)!=0)?'1':'0') : 'Z'; /**/ Dprintf(("SignalSource::getState() %s bitmask:0x%x state:%c\n", (m_register?m_register->name().c_str():"NULL"), m_bitMask,r)); /**/ return r; } private: PortRegister *m_register; unsigned int m_bitMask; }; PortSink::PortSink(PortRegister *portReg, unsigned int iobit) : m_PortRegister(portReg), m_iobit(iobit) { assert (m_PortRegister); } void PortSink::setSinkState(char cNewSinkState) { Dprintf((" PortSink::setSinkState:bit=%d,val=%c\n",m_iobit,cNewSinkState)); m_PortRegister->setbit(m_iobit,cNewSinkState); } //------------------------------------------------------------------------ PortRegister::PortRegister(unsigned int numIopins, unsigned int _mask) : sfr_register(), PortModule(numIopins), mEnableMask(_mask), drivingValue(0), rvDrivenValue(0,0) { } void PortRegister::setEnableMask(unsigned int newEnableMask) { //unsigned int maskDiff = getEnableMask() ^ newEnableMask; unsigned int oldEnableMask = getEnableMask(); for (unsigned int i=0, m=1; isetDefaultSource(new SignalSource(this, i)); pmP->addSink(new PortSink(this, i)); } else { if (pmP->getSourceState() == '?') { pmP->setDefaultSource(new SignalSource(this, i)); pmP->addSink(new PortSink(this, i)); } } } mEnableMask = newEnableMask; } void PortRegister::put(unsigned int new_value) { trace.raw(write_trace.get() | value.data); put_value(new_value); } void PortRegister::put_value(unsigned int new_value) { Dprintf(("PortRegister::put_value old=0x%x:new=0x%x\n",value.data,new_value)); unsigned int diff = mEnableMask & (new_value ^ value.data); drivingValue = new_value & mEnableMask; value.data = drivingValue; if(diff) { // If no stimuli are connected to the Port pins, then the driving // value and the driven value are the same. If there are external // stimuli (or perhaps internal peripherals) overdriving or overriding // this port, then the call to updatePort() will update 'drivenValue' // to its proper value. In either case, calling updatePort ensures // the drivenValue is updated properly updatePort(); } } //------------------------------------------------------------------------ // PortRegister::updateUI() UI really means GUI. // We just pass control to the update method, which is defined in gpsimValue. void PortRegister::updateUI() { update(); } //------------------------------------------------------------------------ // PortRegister::setbit // // This method is called whenever a stimulus changes the state of // an I/O pin associated with the port register. 3-state logic is // used. // FIXME - rvDrivenValue and value are always the same, so why have // FIXME - both? void PortRegister::setbit(unsigned int bit_number, char new3State) { if(bit_number <= bit_mask) { trace.raw(write_trace.get() | value.data); trace.raw(write_trace.geti() | value.init); Dprintf(("PortRegister::setbit() %s bit=%d,val=%c\n",name().c_str(), bit_number,new3State)); if (new3State=='1' || new3State=='W') { rvDrivenValue.data |= (1<updatePinModule(); } void PortModule::updateUI() { // hmmm nothing } void PortModule::updatePin(unsigned int iPinNumber) { if (iPinNumber < mNumIopins) iopins[iPinNumber]->updatePinModule(); } SignalSink *PortModule::addSink(SignalSink *new_sink, unsigned int iPinNumber) { if (iPinNumber < mNumIopins) iopins[iPinNumber]->addSink(new_sink); return new_sink; } IOPIN *PortModule::addPin(IOPIN *new_pin, unsigned int iPinNumber) { if (iPinNumber < mNumIopins) { // If there is not a PinModule for this pin, then add one. if (iopins[iPinNumber] == &AnInvalidPinModule) iopins[iPinNumber] = new PinModule(this,iPinNumber); iopins[iPinNumber]->setPin(new_pin); } return new_pin; } void PortModule::addPinModule(PinModule *newModule, unsigned int iPinNumber) { if (iPinNumber < mNumIopins && iopins[iPinNumber] == &AnInvalidPinModule) iopins[iPinNumber] = newModule; } IOPIN *PortModule::getPin(unsigned int iPinNumber) { if (iPinNumber < mNumIopins) { return &iopins[iPinNumber]->getPin(); } return 0; } //------------------------------------------------------------------------ // PinModule PinModule::PinModule() : PinMonitor(), m_cLastControlState('?'), m_cLastSinkState('?'), m_cLastSourceState('?'), m_cLastPullupControlState('?'), m_defaultSource(0), m_activeSource(0), m_defaultControl(0), m_activeControl(0), m_defaultPullupControl(0), m_activePullupControl(0), m_pin(0), m_port(0), m_pinNumber(0) { } PinModule::PinModule(PortModule *_port, unsigned int _pinNumber, IOPIN *_pin) : PinMonitor(), m_cLastControlState('?'), m_cLastSinkState('?'), m_cLastSourceState('?'), m_cLastPullupControlState('?'), m_defaultSource(0), m_activeSource(0), m_defaultControl(0), m_activeControl(0), m_defaultPullupControl(0), m_activePullupControl(0), m_pin(_pin), m_port(_port), m_pinNumber(_pinNumber), m_bForcedUpdate(false) { setPin(m_pin); } void PinModule::setPin(IOPIN *new_pin) { // Replace our pin only if this one is valid and we don't have one already. if (!m_pin && new_pin) { m_pin = new_pin; m_pin->setMonitor(this); m_cLastControlState = getControlState(); m_cLastSourceState = getSourceState(); } } void PinModule::refreshPinOnUpdate(bool bForcedUpdate) { m_bForcedUpdate = bForcedUpdate; } void PinModule::updatePinModule() { if (!m_pin) return; bool bStateChange=m_bForcedUpdate; Dprintf(("PinModule::updatePinModule():%s enter cont=%c,source=%c,pullup%c\n", (m_pin ? m_pin->name().c_str() : "NOPIN"), m_cLastControlState,m_cLastSourceState,m_cLastPullupControlState)); char cCurrentControlState = getControlState(); if (cCurrentControlState != m_cLastControlState) { m_cLastControlState = cCurrentControlState; m_pin->update_direction((cCurrentControlState=='1') ? IOPIN::DIR_INPUT : IOPIN::DIR_OUTPUT, false); bStateChange = true; } char cCurrentSourceState = getSourceState(); if (cCurrentSourceState != m_cLastSourceState) { m_cLastSourceState = cCurrentSourceState; m_pin->setDrivingState(cCurrentSourceState); bStateChange = true; } char cCurrentPullupControlState = getPullupControlState(); if (cCurrentPullupControlState != m_cLastPullupControlState) { m_cLastPullupControlState = cCurrentPullupControlState; m_pin->update_pullup(m_cLastPullupControlState,false); bStateChange = true; } if (bStateChange) { Dprintf(("PinModule::updatePinModule() exit cont=%c,source=%c,pullup%c\n", m_cLastControlState,m_cLastSourceState, m_cLastPullupControlState)); if (m_pin->snode) m_pin->snode->update(); else setDrivenState(cCurrentSourceState); } } void PinModule::setDefaultControl(SignalControl *newDefaultControl) { if(!m_defaultControl && newDefaultControl) { m_defaultControl = newDefaultControl; setControl(m_defaultControl); } else delete newDefaultControl; } void PinModule::setControl(SignalControl *newControl) { m_activeControl = newControl ? newControl : m_defaultControl; } void PinModule::setDefaultSource(SignalControl *newDefaultSource) { if(!m_defaultSource && newDefaultSource) { m_defaultSource = newDefaultSource; setSource(m_defaultSource); } } void PinModule::setSource(SignalControl *newSource) { m_activeSource = newSource ? newSource : m_defaultSource; } void PinModule::setDefaultPullupControl(SignalControl *newDefaultPullupControl) { if(!m_defaultPullupControl && newDefaultPullupControl) { m_defaultPullupControl = newDefaultPullupControl; setPullupControl(m_defaultPullupControl); } } void PinModule::setPullupControl(SignalControl *newPullupControl) { m_activePullupControl = newPullupControl ? newPullupControl : m_defaultPullupControl; } char PinModule::getControlState() { return m_activeControl ? m_activeControl->getState() : '?'; } char PinModule::getSourceState() { return m_activeSource ? m_activeSource->getState() : '?'; } char PinModule::getPullupControlState() { return m_activePullupControl ? m_activePullupControl->getState() : '?'; } void PinModule::setDrivenState(char new3State) { m_cLastSinkState = new3State; list :: iterator ssi; for (ssi = sinks.begin(); ssi != sinks.end(); ++ssi) (*ssi)->setSinkState(new3State); } void PinModule::setDrivingState(char new3State) { //printf("PinModule::%s -- does nothing\n",__FUNCTION__); } void PinModule::set_nodeVoltage(double) { //printf("PinModule::%s -- does nothing\n",__FUNCTION__); } void PinModule::putState(char) { //printf("PinModule::%s -- does nothing\n",__FUNCTION__); } void PinModule::setDirection() { //printf("PinModule::%s -- does nothing\n",__FUNCTION__); } void PinModule::updateUI() { m_port->updateUI(); } // The IOPORT class is deprecated. #if defined(OLD_IOPORT_DESIGN) //------------------------------------------------------------------- // // IOPORT::update_stimuli // // input: none // return: the states of the stimuli that are driving this ioport // // This member function will update each node that is attached to the // iopins of this port. If there are no pins attached, 0 is returned. // // //------------------------------------------------------------------- int IOPORT::update_stimuli(void) { unsigned int v = value.get(); return v ^ get_value(); } //------------------------------------------------------------------- //------------------------------------------------------------------- double IOPORT::get_bit_voltage(unsigned int bit_number) { double v; if(pins[bit_number]) { if(pins[bit_number]->snode) { cout << "Warning IOPORT::get_bit_voltage has changed\n"; v = pins[bit_number]->snode->get_nodeVoltage(); } else v = pins[bit_number]->get_Vth(); } else v = (value.get() & (1<snode) { double v = pins[i]->snode->get_nodeVoltage(); if(current_value & m) { // this io bit is currently a high if(v <= pins[i]->get_h2l_threshold()) current_value ^= m; } else if (v > pins[i]->get_l2h_threshold()) current_value ^= m; } } value.put(current_value); return(value.get()); } //------------------------------------------------------------------- // IOPORT::get(void) // // inputs: none // returns: the current state of the ioport // // get is identical to get_value except that tracing is performed. // //------------------------------------------------------------------- unsigned int IOPORT::get(void) { trace.raw(read_trace.get() | value.get()); return get_value(); } //------------------------------------------------------------------- // IOPORT::put(unsigned int new_value) // // inputs: new_value - // // returns: none // // The I/O Port is updated with the new value. If there are any stimuli // attached to the I/O pins then they will be updated as well. // //------------------------------------------------------------------- void IOPORT::put(unsigned int new_value) { // The I/O Ports have an internal latch that holds the state of the last // write, even if the I/O pins are configured as inputs. If the tris port // changes an I/O pin from an input to an output, then the contents of this // internal latch will be placed onto the external I/O pin. internal_latch = new_value; trace.raw(write_trace.get() | value.get()); unsigned int current_value = value.get(); value.put(new_value); if(stimulus_mask && (current_value != new_value)) { unsigned int diff = current_value ^ new_value; // Update all I/O pins that have stimuli attached to // them and their state is being changed by this put() operation. for(unsigned int i = 0; i>=1) if((diff&1) && pins[i] && pins[i]->snode) pins[i]->snode->update(); } } //------------------------------------------------------------------- // void IOPORT::put_value(unsigned int new_value) // // When there's a gui initiated change to the IO port, we'll pass // though here. There are three things that we do. First, we update // the I/O port the way the gui asks us. Note however, that it's // possible that the gui's requested will go un-honored (if for example, // we try to force an output to change states or if there's a stimulus // driving the bus already). // Next, after updating the IO port (and all of it's connected stimuli), // we'll call the gui to update its windows. This is done through the // xref->update call. // Finally, we'll check all of the I/O pins that have changed as a // result of the IO port update and individually call each of their // cross references. // //------------------------------------------------------------------- void IOPORT::put_value(unsigned int new_value) { unsigned int i,j; unsigned int old_value = value.get(); unsigned int diff; value.put(new_value); // Update the stimuli - if there are any if(stimulus_mask) update_stimuli(); update(); // Find the pins that have changed states diff = (old_value ^ value.get()) & valid_iopins; // Update the cross references for each pin that has changed. for(i=0,j=1; iupdate(); } } //------------------------------------------------------------------- //------------------------------------------------------------------- void IOPORT::setbit(unsigned int bit_number, bool new_value) { int bit_mask = 1<snode == 0) { // If this I/O pin is not attached to a node yet, // then create a node and attach it. pins[bit_position]->snode = new Stimulus_Node(); pins[bit_position]->snode->attach_stimulus(pins[bit_position]); } // attach the new stimulus to the same node as this I/O pin's pins[bit_position]->snode->attach_stimulus(new_stimulus); } } //------------------------------------------------------------------- //------------------------------------------------------------------- void IOPORT::attach_node(Stimulus_Node *new_node, unsigned int bit_position) { if(pins[bit_position]) { stimulus_mask |= (1<snode == new_node; } else cout << "Error: attaching node to a non-existing I/O pin.\n"; } //------------------------------------------------------------------- // trace_register_write // - a wrapper for trace.register_write // This provides an option for IOPORTs derived from the IOPORT class // to override the behavior of IOPORT traces. //------------------------------------------------------------------- void IOPORT::trace_register_write(void) { trace.raw(write_trace.get() | value.get()); //trace.register_write(address,value.get()); } IOPORT::IOPORT(unsigned int _num_iopins) : sfr_register() { stimulus_mask = 0; num_iopins = _num_iopins; valid_iopins = (1<