Subject:
Re: gpsim logging writes to IO ports
From:
Scott Dattalo ####@####.####
Date:
9 Feb 2003 14:05:32 -0000
Message-Id: <Pine.LNX.4.44.0302090511300.19663-100000@ruckus.brouhaha.com>
On 9 Feb 2003, Alex Holden wrote:
> On Sat, 2003-02-08 at 22:42, Alex Holden wrote:
> > I've just been messing about with the CVS version of gpsim, and I tried
> > to use the log facility to log all writes to an IO port, but it didn't
> > log anything at all. Telling it to log writes to an ordinary memory
> > register instead worked as expected. Is this a bug, and if so how would
> > I go about fixing it?
>
> Doh, after poring over the gpsim source code for an hour I realised that
> it was working all along. Two things confused me into thinking it wasn't
> working when actually it was:
>
> * There are two symbols defined for the 12c509, gpio (ioport 0) and GPIO
> (constant 0x06), and only the upper case one works.
> * gpsim.log is backed by quite a large buffer so it typically remains
> empty until you do a "log off" or quit gpsim. It worked when I set it to
> log writes to a loop counter variable because it generated so many
> writes that it flushed the buffer almost straight away.
I'm glad you got it resolved!
>
> Now I'm attempting to write a module with very little experience of
> coding in C++. I don't suppose anyone's written a "subtitles for the
> hard of thinking" guide to writing gpsim modules have they? I want my
> module to be able to:
>
> * Watch the state of some digital input pins, and read the cycle counter
> when they change.
> * Get called every "n" processor simulation cycles (or getting called
> once every cycle would do) to do some background simulation work, and be
> able to read the processor cycle counter from here also.
> * Control the state of some digital output pins from the background
> simulation.
> * Write data to the log (Ok, the module itself could open a file and log
> to it, but it'd be nicer to use the Gpsim log facility).
> * Get notified when the processor has been reset so I can also reset my
> simulation.
All of these except for possible the reset is possible.
I'd suggest as a good working example the code for usart.cc:
http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/gpsim/modules/usart.cc
Originally I wrote the module interface with the idea that gpsim's API
would be totally abstracted. In fact, the whole gui was originally
implemented this way (and still mostly is implemented this way). But,
there are many, many features buried deeply within gpsim that just don't
make sense to abstract. Further more, there really is no reason for the
abstraction (the original reason was that the gui was written in C, but
the simulation engine was in C++).
The simulation engine of gpsim is built into a library. If you wanted to
build your own custom simulator, you could strip away the CLI and the GUI
and link directly against libgpsim. That would be a whole lot of work
however. But the main reason for having libgpsim is that the external
modules know the interface to gpsim. For example, along with installing
libgpsim, the config scripts also install all of the include files. So,
there's no reason why you can't link against these!
So in the usart.cc module, that's what I've done:
#include "../src/attribute.h"
#include "../src/modules.h"
#include "../src/packages.h"
#include "../src/stimuli.h"
#include "../src/ioports.h"
#include "../src/symbol.h"
#include "../src/interface.h"
This exposes the guts of gpsim to the usart.cc code. So the pic processor
base class is fully exposed to you, the programmer.
Another example that is probably more useful is the LCD module. The reason
is that it is a stand alone module. The configuration scripts know how to
search for the presence of gpsim and automagically provide paths to the
libraries and include files. See:
http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/gpsim/extras/lcd/
This allows you to write the includes like:
#include <gpsim/stimuli.h>
#include <gpsim/ioports.h>
#include <gpsim/symbol.h>
#include <gpsim/trace.h>
-----------
Some specific examples...
You can get total control of your module's I/O pins by creating a new
class derived from gpsim IO pin classes. For example,
class USART_TXPIN : public IO_bi_directional
Creates a new class type for the USART Tx Pin. It's based on gpsim's
bi-directional IO pin class (which is defined in stimuli.h). Since all of
the member functions in the IO_bi_directional class are virtual, you can
easily override them (or not) with your own custom function.
The IO_bi_directional class is actually a class derived from the IOPIN
(which is a class derived from stimuli). So all of these functions are
available for re-definition:
virtual int get_voltage(guint64 current_time) {return state;};
virtual int get_state(void);
virtual file_register *get_iop(void);
virtual void put_state(int new_state) {state=new_state;};
virtual void put_node_state(int new_state) {state=new_state;}; // From
attached node
virtual void put_state_value(int new_state);
virtual void toggle(void) {state = !state;};
virtual void attach(Stimulus_Node *s);
virtual void change_direction(unsigned int){return;};
virtual void update_direction(unsigned int x){return;};
At this point, the obvious question is "what do these functions do?".
Unfortunately, except for the comments in the source code, I haven't
documented gpsim's api. Which is why at the beginning I referred you to
usart.cc and lcd.cc.
For example, here's the USART_TXPIN overriding ::put_state which is a
function that get's called when an I/O pin decides to update it's "state":
void put_state( int new_digital_state) {
file_register *port = get_iop();
if(port) {
// If the new state to which the stimulus is being set is different
than
// the current state of the bit in the ioport (to which this
stimulus is
// mapped), then we need to update the ioport.
if((new_digital_state!=0) ^ ( port->value & (1<<iobit))) {
port->setbit(iobit,new_digital_state);
digital_state = new_digital_state;
// If this stimulus is attached to a node, then let the node be
updated
// with the new state as well.
if(snode)
snode->update(0);
// Note that this will auto magically update
// the io port.
}
}
--------
To get cyclic response, you'll need to define an object derived from the
BreakCallBack class. gpsim has a function call-back mechanism that is
based on the simulator's cycle counter. A function can be programmed to be
called when the cycle counter reaches a certain value. (The cycle counter
is a 64-bit counter that advances by one count for every PIC cpu cycle.
The reason that it's 64 instead of 32 bits is that the latter gives you
*only* 4 billion simulation cycles which is something like 7 minutes of
run time in a 20 Mhz PIC).
I typically define registers derived from the BreakCallBack class (in fact
all SFR's are defined that way). For example, from the usart.cc code the
Transmit Register is defined like so:
class TXREG : public BreakCallBack
The key function is:
virtual void callback(void)
This function is defined in the BreakCallBack class and can be overridden
by your special register definition. All you need to do to use it is to
set a "cyclic break point". This is done by calling:
gpsim_set_break()
For example:
last_time = gpsim_get_current_time();
future_time = last_time + time_per_bit * 12;
gpsim_set_break(future_time, this);
This snippet reads the current time and sets a new break point in the
future. When the simulation cycle counter reaches "future_time", the
function "callback" will get called.
-------
okay, that oughta be enough for now...
Scott