1 |
12 |
dgisselq |
////////////////////////////////////////////////////////////////////////////////
|
2 |
|
|
//
|
3 |
|
|
// Filename: scopecls.h
|
4 |
|
|
//
|
5 |
|
|
// Project: WBScope, a wishbone hosted scope
|
6 |
|
|
//
|
7 |
|
|
// Purpose: After rebuilding the same code over and over again for every
|
8 |
|
|
// "scope" I tried to interact with, I thought it would be simpler
|
9 |
|
|
// to try to make a more generic interface, that other things could plug
|
10 |
13 |
dgisselq |
// into. This file defines and describes that more generic interface.
|
11 |
12 |
dgisselq |
//
|
12 |
13 |
dgisselq |
// More recent updates have added to this interface those things necessary
|
13 |
|
|
// to create a .VCD file for viewing in GTKWave.
|
14 |
|
|
//
|
15 |
12 |
dgisselq |
// Creator: Dan Gisselquist, Ph.D.
|
16 |
|
|
// Gisselquist Technology, LLC
|
17 |
|
|
//
|
18 |
|
|
////////////////////////////////////////////////////////////////////////////////
|
19 |
|
|
//
|
20 |
|
|
// Copyright (C) 2015-2017, Gisselquist Technology, LLC
|
21 |
|
|
//
|
22 |
|
|
// This program is free software (firmware): you can redistribute it and/or
|
23 |
|
|
// modify it under the terms of the GNU General Public License as published
|
24 |
|
|
// by the Free Software Foundation, either version 3 of the License, or (at
|
25 |
|
|
// your option) any later version.
|
26 |
|
|
//
|
27 |
|
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
28 |
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
|
29 |
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
30 |
|
|
// for more details.
|
31 |
|
|
//
|
32 |
|
|
// You should have received a copy of the GNU General Public License along
|
33 |
|
|
// with this program. (It's in the $(ROOT)/doc directory. Run make with no
|
34 |
|
|
// target there if the PDF file isn't present.) If not, see
|
35 |
|
|
// <http://www.gnu.org/licenses/> for a copy.
|
36 |
|
|
//
|
37 |
|
|
// License: GPL, v3, as defined and found on www.gnu.org,
|
38 |
|
|
// http://www.gnu.org/licenses/gpl.html
|
39 |
|
|
//
|
40 |
|
|
//
|
41 |
|
|
////////////////////////////////////////////////////////////////////////////////
|
42 |
|
|
//
|
43 |
|
|
//
|
44 |
|
|
#ifndef SCOPECLS_H
|
45 |
|
|
#define SCOPECLS_H
|
46 |
|
|
|
47 |
|
|
#include <vector>
|
48 |
|
|
#include "devbus.h"
|
49 |
|
|
|
50 |
13 |
dgisselq |
|
51 |
|
|
/*
|
52 |
|
|
* TRACEINFO
|
53 |
|
|
*
|
54 |
|
|
* The TRACEINFO class describes a wire (or set of wires) internal to the
|
55 |
|
|
* scope data word. These wires are assumed to be contiguous, and given by:
|
56 |
|
|
* ((data_word>>m_nshift)&((1<<m_nbits)-1). That is, there are m_nbits bits
|
57 |
|
|
* to this value, and a shift of m_nshift is required to bring them down to
|
58 |
|
|
* zero.
|
59 |
|
|
*
|
60 |
|
|
* Other key pieces include the human readable name given to the signal, m_name,
|
61 |
|
|
* as well as the VCD name, m_key.
|
62 |
|
|
*
|
63 |
|
|
*/
|
64 |
12 |
dgisselq |
class TRACEINFO {
|
65 |
|
|
public:
|
66 |
|
|
const char *m_name;
|
67 |
|
|
char m_key[4];
|
68 |
|
|
unsigned m_nbits, m_nshift;
|
69 |
|
|
};
|
70 |
|
|
|
71 |
13 |
dgisselq |
/*
|
72 |
|
|
* SCOPE
|
73 |
|
|
*
|
74 |
|
|
* This class is designed to be a generic SCOPE class, one which has all of the
|
75 |
|
|
* logic other scopes will require. Hence, if more than one scope needs this
|
76 |
|
|
* logic, I stuff it in here for all scopes to use.
|
77 |
|
|
*/
|
78 |
12 |
dgisselq |
class SCOPE {
|
79 |
13 |
dgisselq |
DEVBUS *m_fpga; // Access to the FPGA
|
80 |
|
|
DEVBUS::BUSW m_addr; // The address of the scope control reg
|
81 |
|
|
// Set m_compressed to be true if the scope is a
|
82 |
|
|
// compressed scope, that is if it uses the wbscopc.v
|
83 |
|
|
// core.
|
84 |
|
|
bool m_compressed,
|
85 |
|
|
// Set m_vector_read if you trust the bus enough to
|
86 |
|
|
// issue vector reads (multiple words at once)
|
87 |
|
|
m_vector_read;
|
88 |
|
|
unsigned m_scoplen, // Number of words in the scopes memory
|
89 |
|
|
m_holdoff; // The bias, or samples since trigger
|
90 |
|
|
unsigned *m_data; // Data read from the scope
|
91 |
|
|
unsigned m_clkfreq_hz;
|
92 |
|
|
|
93 |
|
|
// The m_traces variable holds a list of all of the various wire
|
94 |
|
|
// definitions within the scope data word.
|
95 |
12 |
dgisselq |
std::vector<TRACEINFO *> m_traces;
|
96 |
|
|
|
97 |
|
|
public:
|
98 |
|
|
SCOPE(DEVBUS *fpga, unsigned addr,
|
99 |
|
|
bool compressed=false, bool vecread=true)
|
100 |
|
|
: m_fpga(fpga), m_addr(addr),
|
101 |
|
|
m_compressed(compressed), m_vector_read(vecread),
|
102 |
13 |
dgisselq |
m_scoplen(0), m_data(NULL) {
|
103 |
|
|
//
|
104 |
|
|
// First thing we want to do upon allocating a scope, is to
|
105 |
|
|
// define the traces for that scope. Sad thing is ... we can't
|
106 |
|
|
// call it here, since the class inheriting from us isn't
|
107 |
|
|
// defined yet.
|
108 |
|
|
// define_traces();
|
109 |
12 |
dgisselq |
|
110 |
13 |
dgisselq |
|
111 |
|
|
// Default clock frequency: 100MHz.
|
112 |
|
|
m_clkfreq_hz = 100000000;
|
113 |
|
|
}
|
114 |
|
|
|
115 |
|
|
// Free up any of our allocated memory.
|
116 |
|
|
~SCOPE(void) {
|
117 |
|
|
for(unsigned i=0; i<m_traces.size(); i++)
|
118 |
|
|
delete m_traces[i];
|
119 |
|
|
if (m_data) delete[] m_data;
|
120 |
|
|
}
|
121 |
|
|
|
122 |
|
|
// Query the scope: Is it ready? Has it primed, triggered, and stopped?
|
123 |
|
|
// If so, this routine returns true, false otherwise.
|
124 |
12 |
dgisselq |
bool ready();
|
125 |
13 |
dgisselq |
|
126 |
|
|
// Read the control word from the scope, and send to the standard output
|
127 |
|
|
// a description of that.
|
128 |
12 |
dgisselq |
void decode_control(void);
|
129 |
13 |
dgisselq |
|
130 |
|
|
// Read the scope's control word, decode the memory size of the scope,
|
131 |
|
|
// and return that to our caller.
|
132 |
12 |
dgisselq |
int scoplen(void);
|
133 |
13 |
dgisselq |
|
134 |
|
|
// Set the clock speed that we are referencing
|
135 |
|
|
void set_clkfreq_hz(unsigned clkfreq_hz) {
|
136 |
|
|
m_clkfreq_hz = clkfreq_hz;
|
137 |
|
|
}
|
138 |
|
|
|
139 |
|
|
// Read any previously set clock speed.
|
140 |
|
|
unsigned get_clkfreq_hz(void) { return m_clkfreq_hz; }
|
141 |
|
|
|
142 |
|
|
// Read the data from the scope and place it into our m_data array.
|
143 |
|
|
// Nothing more is done with it beyond that.
|
144 |
12 |
dgisselq |
virtual void rawread(void);
|
145 |
13 |
dgisselq |
|
146 |
|
|
// Walk through the data, and print out to the standard output, what is
|
147 |
|
|
// in it. If multiple lines have the same data, print() will avoid
|
148 |
|
|
// printing those lines for the purpose of keeping the output from
|
149 |
|
|
// getting cluttered, but it will print a **** spacer, so you know
|
150 |
|
|
// lines were skipped.
|
151 |
12 |
dgisselq |
void print(void);
|
152 |
13 |
dgisselq |
|
153 |
|
|
// decode() works together with print() above. The print() routine
|
154 |
|
|
// calls decode() for every memory word within the scope's buffer.
|
155 |
|
|
// More than that, the print() routine starts each line with the
|
156 |
|
|
// clock number of the item, followed by the 32-bit data word in hex and
|
157 |
|
|
// a colon. Then it calls decode() to fill in the line with whatever
|
158 |
|
|
// useful information was in the scope's data word. Then it prints
|
159 |
|
|
// a "\n" and continues. Hence ... the purpose of the decode()
|
160 |
|
|
// function--and why it needs to be scope specific.
|
161 |
|
|
virtual void decode(DEVBUS::BUSW v) const = 0;
|
162 |
|
|
|
163 |
|
|
//
|
164 |
|
|
//
|
165 |
|
|
// The following routines are provided to enable the creation and
|
166 |
|
|
// writing of VCD files.
|
167 |
|
|
//
|
168 |
|
|
//
|
169 |
|
|
|
170 |
|
|
// Write the timescale line to a VCD file.
|
171 |
12 |
dgisselq |
virtual void write_trace_timescale(FILE *fp);
|
172 |
13 |
dgisselq |
|
173 |
|
|
// Write the offset from the time within the file, to the time of the
|
174 |
|
|
// trigger, into the file.
|
175 |
|
|
virtual void write_trace_timezero(FILE *fp, int offset);
|
176 |
|
|
|
177 |
|
|
// Write the VCD file's header
|
178 |
|
|
virtual void write_trace_header(FILE *fp, int offset = 0);
|
179 |
|
|
|
180 |
|
|
// Given a value, and the number of bits required to define that value,
|
181 |
|
|
// write a single line to our VCD file.
|
182 |
|
|
//
|
183 |
|
|
// This is an internal call that you are not likely to need to modify.
|
184 |
12 |
dgisselq |
void write_binary_trace(FILE *fp, const int nbits,
|
185 |
|
|
unsigned val, const char *str);
|
186 |
13 |
dgisselq |
|
187 |
|
|
// Same as write_binary_trace above, but this time we are given the
|
188 |
|
|
// trace definition and the un-decomposed word to decompose first before
|
189 |
|
|
// writing to the file.
|
190 |
|
|
//
|
191 |
|
|
// This is also an internal call that you are not likely to need to
|
192 |
|
|
// modify.
|
193 |
12 |
dgisselq |
void write_binary_trace(FILE *fp, TRACEINFO *info,
|
194 |
|
|
unsigned value);
|
195 |
13 |
dgisselq |
|
196 |
|
|
// This is the user entry point. When you know the scope is ready,
|
197 |
|
|
// you may call writevcd to start the VCD generation process.
|
198 |
12 |
dgisselq |
void writevcd(const char *trace_file_name);
|
199 |
13 |
dgisselq |
// This is an alternate entry point, useful if you already have a
|
200 |
|
|
// FILE *. This will write the data to the file, but not close the
|
201 |
|
|
// file.
|
202 |
|
|
void writevcd(FILE *fp);
|
203 |
|
|
|
204 |
|
|
// Calculate the number of points the scope covers. Nominally, this
|
205 |
|
|
// will be m_scopelen, the length of the scope. However, if the
|
206 |
|
|
// scope is compressed, this could be greater.
|
207 |
|
|
//
|
208 |
|
|
unsigned getaddresslen(void);
|
209 |
|
|
|
210 |
|
|
// Your program needs to define a define_traces() function, which will
|
211 |
|
|
// then be called before trying to write the VCD file. This function
|
212 |
|
|
// must call register_trace for each of the traces within your data
|
213 |
|
|
// word.
|
214 |
|
|
virtual void define_traces(void);
|
215 |
|
|
|
216 |
|
|
// Register_trace() defines the elements of a TRACEINFO structure
|
217 |
|
|
// above. These are then inserted into the list of TRACEINFO
|
218 |
|
|
// structures, for reference when writing the VCD file.
|
219 |
12 |
dgisselq |
void register_trace(const char *varname,
|
220 |
|
|
unsigned nbits, unsigned shift);
|
221 |
13 |
dgisselq |
|
222 |
|
|
unsigned operator[](unsigned addr) {
|
223 |
|
|
if ((m_data)&&(m_scoplen > 0))
|
224 |
|
|
return m_data[(addr)&(m_scoplen-1)];
|
225 |
|
|
return 0;
|
226 |
|
|
}
|
227 |
12 |
dgisselq |
};
|
228 |
|
|
|
229 |
|
|
#endif // SCOPECLS_H
|