Line 5... |
Line 5... |
// Project: WBScope, a wishbone hosted scope
|
// Project: WBScope, a wishbone hosted scope
|
//
|
//
|
// Purpose: After rebuilding the same code over and over again for every
|
// Purpose: After rebuilding the same code over and over again for every
|
// "scope" I tried to interact with, I thought it would be simpler
|
// "scope" I tried to interact with, I thought it would be simpler
|
// to try to make a more generic interface, that other things could plug
|
// to try to make a more generic interface, that other things could plug
|
// into. This is that more generic interface.
|
// into. This file defines and describes that more generic interface.
|
|
//
|
|
// More recent updates have added to this interface those things necessary
|
|
// to create a .VCD file for viewing in GTKWave.
|
//
|
//
|
// Creator: Dan Gisselquist, Ph.D.
|
// Creator: Dan Gisselquist, Ph.D.
|
// Gisselquist Technology, LLC
|
// Gisselquist Technology, LLC
|
//
|
//
|
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
Line 42... |
Line 45... |
#define SCOPECLS_H
|
#define SCOPECLS_H
|
|
|
#include <vector>
|
#include <vector>
|
#include "devbus.h"
|
#include "devbus.h"
|
|
|
|
|
|
/*
|
|
* TRACEINFO
|
|
*
|
|
* The TRACEINFO class describes a wire (or set of wires) internal to the
|
|
* scope data word. These wires are assumed to be contiguous, and given by:
|
|
* ((data_word>>m_nshift)&((1<<m_nbits)-1). That is, there are m_nbits bits
|
|
* to this value, and a shift of m_nshift is required to bring them down to
|
|
* zero.
|
|
*
|
|
* Other key pieces include the human readable name given to the signal, m_name,
|
|
* as well as the VCD name, m_key.
|
|
*
|
|
*/
|
class TRACEINFO {
|
class TRACEINFO {
|
public:
|
public:
|
const char *m_name;
|
const char *m_name;
|
char m_key[4];
|
char m_key[4];
|
unsigned m_nbits, m_nshift;
|
unsigned m_nbits, m_nshift;
|
};
|
};
|
|
|
|
/*
|
|
* SCOPE
|
|
*
|
|
* This class is designed to be a generic SCOPE class, one which has all of the
|
|
* logic other scopes will require. Hence, if more than one scope needs this
|
|
* logic, I stuff it in here for all scopes to use.
|
|
*/
|
class SCOPE {
|
class SCOPE {
|
DEVBUS *m_fpga;
|
DEVBUS *m_fpga; // Access to the FPGA
|
DEVBUS::BUSW m_addr;
|
DEVBUS::BUSW m_addr; // The address of the scope control reg
|
bool m_compressed, m_vector_read;
|
// Set m_compressed to be true if the scope is a
|
unsigned m_scoplen;
|
// compressed scope, that is if it uses the wbscopc.v
|
unsigned *m_data;
|
// core.
|
|
bool m_compressed,
|
|
// Set m_vector_read if you trust the bus enough to
|
|
// issue vector reads (multiple words at once)
|
|
m_vector_read;
|
|
unsigned m_scoplen, // Number of words in the scopes memory
|
|
m_holdoff; // The bias, or samples since trigger
|
|
unsigned *m_data; // Data read from the scope
|
|
unsigned m_clkfreq_hz;
|
|
|
|
// The m_traces variable holds a list of all of the various wire
|
|
// definitions within the scope data word.
|
std::vector<TRACEINFO *> m_traces;
|
std::vector<TRACEINFO *> m_traces;
|
|
|
public:
|
public:
|
SCOPE(DEVBUS *fpga, unsigned addr,
|
SCOPE(DEVBUS *fpga, unsigned addr,
|
bool compressed=false, bool vecread=true)
|
bool compressed=false, bool vecread=true)
|
: m_fpga(fpga), m_addr(addr),
|
: m_fpga(fpga), m_addr(addr),
|
m_compressed(compressed), m_vector_read(vecread),
|
m_compressed(compressed), m_vector_read(vecread),
|
m_scoplen(0), m_data(NULL) {}
|
m_scoplen(0), m_data(NULL) {
|
~SCOPE(void) { if (m_data) delete[] m_data; }
|
//
|
|
// First thing we want to do upon allocating a scope, is to
|
|
// define the traces for that scope. Sad thing is ... we can't
|
|
// call it here, since the class inheriting from us isn't
|
|
// defined yet.
|
|
// define_traces();
|
|
|
|
|
|
// Default clock frequency: 100MHz.
|
|
m_clkfreq_hz = 100000000;
|
|
}
|
|
|
|
// Free up any of our allocated memory.
|
|
~SCOPE(void) {
|
|
for(unsigned i=0; i<m_traces.size(); i++)
|
|
delete m_traces[i];
|
|
if (m_data) delete[] m_data;
|
|
}
|
|
|
|
// Query the scope: Is it ready? Has it primed, triggered, and stopped?
|
|
// If so, this routine returns true, false otherwise.
|
bool ready();
|
bool ready();
|
|
|
|
// Read the control word from the scope, and send to the standard output
|
|
// a description of that.
|
void decode_control(void);
|
void decode_control(void);
|
|
|
|
// Read the scope's control word, decode the memory size of the scope,
|
|
// and return that to our caller.
|
int scoplen(void);
|
int scoplen(void);
|
|
|
|
// Set the clock speed that we are referencing
|
|
void set_clkfreq_hz(unsigned clkfreq_hz) {
|
|
m_clkfreq_hz = clkfreq_hz;
|
|
}
|
|
|
|
// Read any previously set clock speed.
|
|
unsigned get_clkfreq_hz(void) { return m_clkfreq_hz; }
|
|
|
|
// Read the data from the scope and place it into our m_data array.
|
|
// Nothing more is done with it beyond that.
|
virtual void rawread(void);
|
virtual void rawread(void);
|
|
|
|
// Walk through the data, and print out to the standard output, what is
|
|
// in it. If multiple lines have the same data, print() will avoid
|
|
// printing those lines for the purpose of keeping the output from
|
|
// getting cluttered, but it will print a **** spacer, so you know
|
|
// lines were skipped.
|
void print(void);
|
void print(void);
|
|
|
|
// decode() works together with print() above. The print() routine
|
|
// calls decode() for every memory word within the scope's buffer.
|
|
// More than that, the print() routine starts each line with the
|
|
// clock number of the item, followed by the 32-bit data word in hex and
|
|
// a colon. Then it calls decode() to fill in the line with whatever
|
|
// useful information was in the scope's data word. Then it prints
|
|
// a "\n" and continues. Hence ... the purpose of the decode()
|
|
// function--and why it needs to be scope specific.
|
|
virtual void decode(DEVBUS::BUSW v) const = 0;
|
|
|
|
//
|
|
//
|
|
// The following routines are provided to enable the creation and
|
|
// writing of VCD files.
|
|
//
|
|
//
|
|
|
|
// Write the timescale line to a VCD file.
|
virtual void write_trace_timescale(FILE *fp);
|
virtual void write_trace_timescale(FILE *fp);
|
virtual void write_trace_header(FILE *fp);
|
|
|
// Write the offset from the time within the file, to the time of the
|
|
// trigger, into the file.
|
|
virtual void write_trace_timezero(FILE *fp, int offset);
|
|
|
|
// Write the VCD file's header
|
|
virtual void write_trace_header(FILE *fp, int offset = 0);
|
|
|
|
// Given a value, and the number of bits required to define that value,
|
|
// write a single line to our VCD file.
|
|
//
|
|
// This is an internal call that you are not likely to need to modify.
|
void write_binary_trace(FILE *fp, const int nbits,
|
void write_binary_trace(FILE *fp, const int nbits,
|
unsigned val, const char *str);
|
unsigned val, const char *str);
|
|
|
|
// Same as write_binary_trace above, but this time we are given the
|
|
// trace definition and the un-decomposed word to decompose first before
|
|
// writing to the file.
|
|
//
|
|
// This is also an internal call that you are not likely to need to
|
|
// modify.
|
void write_binary_trace(FILE *fp, TRACEINFO *info,
|
void write_binary_trace(FILE *fp, TRACEINFO *info,
|
unsigned value);
|
unsigned value);
|
|
|
|
// This is the user entry point. When you know the scope is ready,
|
|
// you may call writevcd to start the VCD generation process.
|
void writevcd(const char *trace_file_name);
|
void writevcd(const char *trace_file_name);
|
|
// This is an alternate entry point, useful if you already have a
|
|
// FILE *. This will write the data to the file, but not close the
|
|
// file.
|
|
void writevcd(FILE *fp);
|
|
|
|
// Calculate the number of points the scope covers. Nominally, this
|
|
// will be m_scopelen, the length of the scope. However, if the
|
|
// scope is compressed, this could be greater.
|
|
//
|
|
unsigned getaddresslen(void);
|
|
|
|
// Your program needs to define a define_traces() function, which will
|
|
// then be called before trying to write the VCD file. This function
|
|
// must call register_trace for each of the traces within your data
|
|
// word.
|
|
virtual void define_traces(void);
|
|
|
|
// Register_trace() defines the elements of a TRACEINFO structure
|
|
// above. These are then inserted into the list of TRACEINFO
|
|
// structures, for reference when writing the VCD file.
|
void register_trace(const char *varname,
|
void register_trace(const char *varname,
|
unsigned nbits, unsigned shift);
|
unsigned nbits, unsigned shift);
|
virtual void decode(DEVBUS::BUSW v) const = 0;
|
|
virtual void define_traces(void);
|
unsigned operator[](unsigned addr) {
|
|
if ((m_data)&&(m_scoplen > 0))
|
|
return m_data[(addr)&(m_scoplen-1)];
|
|
return 0;
|
|
}
|
};
|
};
|
|
|
#endif // SCOPECLS_H
|
#endif // SCOPECLS_H
|
|
|
No newline at end of file
|
No newline at end of file
|