1 |
210 |
ja_rd |
\chapter{Logic Simulation}
|
2 |
|
|
\label{logic_simulation}
|
3 |
|
|
|
4 |
|
|
The project has been simulated using Modelsim 6.3g. The test bench
|
5 |
|
|
uses some features not present in earlier versions (namely library Signal
|
6 |
|
|
Spy) so if you use some other simulator or some earlier version of Modelsim,
|
7 |
|
|
see section on Modelsim dependencies (\ref{modelsim_dependencies}) below.\\
|
8 |
|
|
|
9 |
|
|
In short, the simulation test bench is meant to run any of the code samples
|
10 |
|
|
provided in directory /src, under a controlled environment, while logging
|
11 |
|
|
the cpu state to a text log file.\\
|
12 |
|
|
|
13 |
|
|
This log file can then be compared to a log file generated by a software
|
14 |
221 |
ja_rd |
simulator for the same code sample (see section \ref{sw_simulator}). The software
|
15 |
210 |
ja_rd |
simulator is the 'golden model' against which the cpu is tested, so any
|
16 |
|
|
difference between both log files means trouble.\\
|
17 |
|
|
|
18 |
|
|
This method is far easier than building a fully automated test bench, and
|
19 |
|
|
much more convenient and reliable than a visual inspection of the simulation
|
20 |
|
|
state.\\
|
21 |
|
|
|
22 |
|
|
In addition to the main log file, there is a console log file to which all
|
23 |
|
|
data written to the UART is logged (see section~\ref{uart_logging}).\\
|
24 |
|
|
|
25 |
|
|
|
26 |
221 |
ja_rd |
The simulation test bench can be found in file '/vhdl/tb/mips\_tb.vhdl'.
|
27 |
|
|
This test bench is meant to be used with all the code samples.
|
28 |
210 |
ja_rd |
|
29 |
221 |
ja_rd |
Each of the code samples configures the simulation test bench with certain
|
30 |
|
|
parameters (such as simulation length or memory sizes) and of course each
|
31 |
|
|
sample has a different object code to be run. The way to pass these
|
32 |
|
|
parameters to the simulation is through a simulation package, in file
|
33 |
|
|
'/vhdl/tb/sim\_params\_pkg.vhdl'.
|
34 |
210 |
ja_rd |
|
35 |
221 |
ja_rd |
This file is generated from a template whenever you 'make' each code sample
|
36 |
|
|
(see section~\ref{samples}). The package is built using oe of the
|
37 |
|
|
provided tools, 'build\_pkg', explained in section ~\ref{build_pkg}.
|
38 |
|
|
|
39 |
210 |
ja_rd |
|
40 |
|
|
Note that all code samples share the same vhdl files: you need to run the
|
41 |
|
|
makefile target 'sim' for the sample you want to simulate; that will
|
42 |
221 |
ja_rd |
overwrite the package file mentioned above. So there's no vhdl file that is
|
43 |
210 |
ja_rd |
specific to a particular code sample.\\
|
44 |
221 |
ja_rd |
|
45 |
210 |
ja_rd |
While the test benches and sample code are good enough to catch MOST errors
|
46 |
|
|
in the full system (i.e. cache included) they don't help with diagnostic;
|
47 |
|
|
once you know there's an error, and the approximate address where it's
|
48 |
|
|
triggered (approximate because of the cache) you have to dig into the
|
49 |
221 |
ja_rd |
simulation waveforms to find it.\\
|
50 |
210 |
ja_rd |
|
51 |
|
|
\section{Running the Simulation}
|
52 |
|
|
\label{running_the_simulation}
|
53 |
|
|
|
54 |
|
|
A simulation script can be found at '/sim/mips\_tb.do'. This script will
|
55 |
|
|
simulate the test bench entity in file '/vhdl/tb/mips\_tb.vhdl'.\\
|
56 |
|
|
|
57 |
|
|
All the code samples are run with the same script.\\
|
58 |
|
|
|
59 |
|
|
The test bench files mentioned in the previous section are automatically
|
60 |
|
|
generated for each of the sample programs. This is automatically done by the
|
61 |
|
|
sample code makefile,
|
62 |
221 |
ja_rd |
assuming you have a MIPS cross-toolchain in your computer (see section~\ref{samples}).\\
|
63 |
210 |
ja_rd |
|
64 |
221 |
ja_rd |
For convenience, a pre-generated file 'sim\_params\_pkg.vhdl' is included
|
65 |
|
|
so you can launch a simulation without having to install toolchains, etc.
|
66 |
|
|
The code is that of the 'hello world' sample.\\
|
67 |
210 |
ja_rd |
|
68 |
|
|
I guess that if you are interested in this sort of stuff then you probably
|
69 |
|
|
know more about Modelsim than I do. Yet, here's a step-by-step guide to
|
70 |
|
|
simulating the 'hello world' sample:
|
71 |
|
|
|
72 |
|
|
\begin{enumerate}
|
73 |
|
|
\item Run 'make hello\_sim' from directory '/src/hello'.
|
74 |
|
|
This will compile the program sources, build the necessary binary object
|
75 |
221 |
ja_rd |
files and then create the package file mentioned above.\\
|
76 |
210 |
ja_rd |
Read the makefile and comments in the python script '/src/bin2hdl.py'
|
77 |
|
|
for details.\\
|
78 |
|
|
|
79 |
|
|
ALTERNATIVELY, if you don't have a toolchain you can just skip this
|
80 |
|
|
step and use the default vhdl files provided, which are those of the
|
81 |
|
|
'hello world' sample.\\
|
82 |
|
|
|
83 |
|
|
\item Within Modelsim, change directory to /syn. Modelsim will create its
|
84 |
|
|
stuff in this directory. This includes the
|
85 |
|
|
log file, which by default will be '/syn/hw\_sim\_log.txt', and the
|
86 |
|
|
console log file '/syn/hw\_sim\_console.log'.\\
|
87 |
|
|
(You could use any other directory, this is just a convenient place to
|
88 |
|
|
put modelsim data out of the way. Just remember where the log files
|
89 |
|
|
are.).
|
90 |
|
|
|
91 |
|
|
\item Run script '/sim/mips\_tb.do' (menu tools-\textgreater tcl-\textgreater execute macro)
|
92 |
|
|
The simulation will run to completion and print a message in Modelsim's
|
93 |
|
|
transcript window when it's done. You can open the console log file
|
94 |
|
|
to see the program output, in this case the 'Hello world' message.\\
|
95 |
|
|
|
96 |
|
|
\end{enumerate}
|
97 |
|
|
|
98 |
|
|
The test bench terminates the simulation when:
|
99 |
|
|
|
100 |
|
|
\begin{enumerate}
|
101 |
|
|
|
102 |
|
|
\item It detects two consecutive code fetches from the same address.
|
103 |
|
|
\item The simulation timeout is reached.
|
104 |
|
|
|
105 |
|
|
\end{enumerate}
|
106 |
|
|
|
107 |
|
|
Condition 1 is meant to detect single-instruction loops such as those
|
108 |
|
|
commonly found after the invocation of the main() function in a C program.
|
109 |
|
|
This is a convenient way for the software to signal its termination.\\
|
110 |
|
|
|
111 |
|
|
The timeout is one of the simulation parameters which is defined in
|
112 |
|
|
the makefiles. It is arbitrarily fixed for each sample by trial and error
|
113 |
|
|
so that the program has time to execute. Change them if necessary.\\
|
114 |
|
|
|
115 |
|
|
\section{Simulation File Logging}
|
116 |
|
|
\label{sim_logging}
|
117 |
|
|
|
118 |
|
|
The simulation test bench will log any of the following events:
|
119 |
|
|
|
120 |
|
|
\begin{itemize}
|
121 |
|
|
\item Changes in the register bank.
|
122 |
|
|
\item Changes in registers HI and LO (implemented even if mul/div is not).
|
123 |
|
|
\item Changes in registers EPC and SR.
|
124 |
|
|
\item Data loads (any resulting register change is logged separately).
|
125 |
|
|
\item Data stores.
|
126 |
|
|
\end{itemize}
|
127 |
|
|
|
128 |
|
|
Note that changes in other internal registers, including PC, are not logged.
|
129 |
|
|
This means that for example a long chain of NOPs, or MOVEs that don't change
|
130 |
|
|
register values, will not be seen in the log file. This is on purpose.\\
|
131 |
|
|
|
132 |
|
|
Events are logged with the address of the instruction that triggered
|
133 |
|
|
the change. This holds true even for load instructions.\\
|
134 |
|
|
|
135 |
|
|
The simulation log file is stored by default in modelsim's working directory
|
136 |
|
|
(see above). I don't provide any automated script to do the comparison, you
|
137 |
|
|
should use whatever diff tool you like best.\\
|
138 |
|
|
|
139 |
|
|
\section{Log File Format}
|
140 |
|
|
\label{log_file_format}
|
141 |
|
|
|
142 |
|
|
There is a text line for each of the following events:
|
143 |
|
|
|
144 |
|
|
\begin{itemize}
|
145 |
|
|
\item Register change
|
146 |
|
|
|
147 |
|
|
"(pc) [reg\_num]=value"\\
|
148 |
|
|
|
149 |
|
|
Where:
|
150 |
|
|
|
151 |
|
|
\begin{tabular}{ l l }
|
152 |
|
|
pc & =\textgreater PC value (8-digit hex)\\
|
153 |
|
|
reg\_num & =\textgreater Register index (2-digit hex), or any of {LO,HI,EP}\\
|
154 |
|
|
value & =\textgreater New register value (8-digit hex)\\
|
155 |
|
|
\end{tabular}\\
|
156 |
|
|
|
157 |
|
|
|
158 |
|
|
\item Write cycle (store)
|
159 |
|
|
|
160 |
|
|
"(pc) [address] |mask|=value WR"\\
|
161 |
|
|
|
162 |
|
|
Where:
|
163 |
|
|
|
164 |
|
|
\begin{tabular}{ l l }
|
165 |
|
|
pc & =\textgreater PC value (8-digit hex)\\
|
166 |
|
|
address & =\textgreater Write address\\
|
167 |
|
|
mask & =\textgreater Byte-enable mask (2-digit hex)\\
|
168 |
|
|
value & =\textgreater Write data\\
|
169 |
|
|
\end{tabular}\\
|
170 |
|
|
|
171 |
|
|
The PC value is the address of the instruction that caused the logged
|
172 |
|
|
change, NOT the actual value of the PC at the time of the change.
|
173 |
|
|
This is so to make the hardware logger's life easier -- the SW simulator
|
174 |
|
|
and the real HW don't work exactly the same when the cache starts
|
175 |
|
|
stalling the cpu (which the SW does not simulate) and the best reference
|
176 |
|
|
point for all instructions is their own adddress.
|
177 |
|
|
|
178 |
|
|
The mask will have a '1' at bits 3..0 for each byte write-enabled. MSB
|
179 |
|
|
is bit 3, LSB is bit 0. Note that the data is big endian, so the MSB
|
180 |
|
|
is actually the LOWER address. The upper nibble of the mask is always 0.
|
181 |
|
|
|
182 |
|
|
The value will match the behavior of the ion cpu; the significant
|
183 |
|
|
byte(s) will have the actual write data and the other bytes will not
|
184 |
|
|
be relevant but will behave exactly as the real hardware (so that the
|
185 |
|
|
logs are directly comparable).
|
186 |
|
|
|
187 |
|
|
The WR at the end of the line is for visual reference only.
|
188 |
|
|
|
189 |
|
|
\item Read cycle (load)
|
190 |
|
|
|
191 |
|
|
"(pc) [address] \textless ** \textgreater =value RD"\\
|
192 |
|
|
|
193 |
|
|
Where:
|
194 |
|
|
|
195 |
|
|
\begin{tabular}{ l l }
|
196 |
|
|
pc & =\textgreater PC value (8-digit hex)\\
|
197 |
|
|
address & =\textgreater Read address\\
|
198 |
|
|
\textless ** \textgreater & =\textgreater Padding (ignore)\\
|
199 |
|
|
value & =\textgreater Read data\\
|
200 |
|
|
\end{tabular}\\
|
201 |
|
|
|
202 |
|
|
Note that in the real machine, the data is read into the cpu one cycle
|
203 |
|
|
after the address bus is output (because the memory is synchronous) so
|
204 |
|
|
that the full read cycle spans 2 clock cycles (when proper interlocking
|
205 |
|
|
is implemented, the load will overlap the next instruction; right now
|
206 |
|
|
it just stalls the pipeline for 1 cycle). This is simplified in the log
|
207 |
|
|
files for readability.
|
208 |
|
|
|
209 |
|
|
Note that the size of the read (LH/LB/LW) instruction is not recorded:
|
210 |
|
|
the CPU always reads 32-bit words.
|
211 |
|
|
|
212 |
|
|
The RD at the end of the line is for visual reference only.
|
213 |
|
|
|
214 |
|
|
\end{itemize}
|
215 |
|
|
|
216 |
|
|
|
217 |
|
|
For example, these are lines 1153-1162 of the simulation log for the
|
218 |
|
|
default 'hello world' test program:
|
219 |
|
|
|
220 |
|
|
\begin{verbatim}
|
221 |
|
|
...
|
222 |
|
|
(BFC009AC) [05]=20000000
|
223 |
|
|
(BFC009B0) [20000020] <**>=00000003 RD
|
224 |
|
|
(BFC009B0) [03]=00000003
|
225 |
|
|
(BFC009B8) [03]=00000002
|
226 |
|
|
(BFC009C0) [03]=20000000
|
227 |
|
|
(BFC009C4) [20000000] |0F|=00000070 WR
|
228 |
|
|
(BFC00E74) [12]=00000004
|
229 |
|
|
(BFC00E78) [10]=BFC01048
|
230 |
|
|
(BFC00E7C) [BFC01048] <**>=00000069 RD
|
231 |
|
|
(BFC00E7C) [05]=00000069
|
232 |
|
|
...
|
233 |
|
|
\end{verbatim}\\
|
234 |
|
|
|
235 |
|
|
(NOTE: this example taken from revision 176, yours may vary)\\
|
236 |
|
|
|
237 |
|
|
The read cycle at pc=0xbfc009b0 modifies register 0x03; that's why there
|
238 |
|
|
are two lines with the same pc value.\\
|
239 |
|
|
|
240 |
|
|
The code that produced that log is this (from hello.lst):
|
241 |
|
|
|
242 |
|
|
\begin{verbatim}
|
243 |
|
|
...
|
244 |
|
|
bfc009ac: 3c052000 lui a1,0x2000
|
245 |
|
|
bfc009b0: 8ca30020 lw v1,32(a1)
|
246 |
|
|
bfc009b4: 00000000 nop
|
247 |
|
|
bfc009b8: 30630002 andi v1,v1,0x2
|
248 |
|
|
bfc009bc: 1060fffc beqz v1,0xbfc009b0
|
249 |
|
|
bfc009c0: 3c032000 lui v1,0x2000
|
250 |
|
|
bfc009c4: ac620000 sw v0,0(v1)
|
251 |
|
|
bfc009c8: 03e00008 jr ra
|
252 |
|
|
bfc009cc: 00000000 nop
|
253 |
|
|
...
|
254 |
|
|
bfc00e74: 26520001 addiu s2,s2,1
|
255 |
|
|
bfc00e78: 26100001 addiu s0,s0,1
|
256 |
|
|
bfc00e7c: 92050000 lbu a1,0(s0)
|
257 |
|
|
bfc00e80: 00000000 nop
|
258 |
|
|
...
|
259 |
|
|
\end{verbatim}\\
|
260 |
|
|
|
261 |
|
|
(Remember the register numbers: \$v0=0x02, \$v1=0x03, \$a1=0x05, \$s0=0x10,
|
262 |
|
|
\$s2=0x12)\\
|
263 |
|
|
|
264 |
|
|
Note that, unlike previous versions of this project, all changes are logged
|
265 |
|
|
with the address of the instruction that caused them.\\
|
266 |
|
|
|
267 |
|
|
The log file format is hardcoded into vhdl package mips\_sim\_pkg
|
268 |
|
|
and the software simulator C source that implement it. It will
|
269 |
|
|
be probably modified as the project moves on so it is best if you verify
|
270 |
|
|
all of this yourself with the project version you intend to use before
|
271 |
|
|
using this information.\\
|
272 |
|
|
|
273 |
|
|
Note that the software simulation log and the modelsim log need not be the
|
274 |
|
|
same size even if both CPUs behave identically; the one that spans a longer
|
275 |
|
|
simulated time will be longer.\\
|
276 |
|
|
The point is that both need to be identical up to the last line of the
|
277 |
|
|
shortest file.\\
|
278 |
|
|
|
279 |
|
|
\section{Console Output Logging}
|
280 |
|
|
\label{uart_logging}
|
281 |
|
|
|
282 |
|
|
Every byte written to the UART TX register is logged (in ascii) to a text
|
283 |
|
|
file which by default is '/syn/hw\_sim\_console.log'. Apart from the
|
284 |
|
|
automatic insertion of a CR after every LF, the data is logged verbatim.\\
|
285 |
|
|
|
286 |
|
|
Though the UART is included in the test bench, the actual UART operation is
|
287 |
|
|
bypassed: The test bench forces the 'tx ready' high so that the CPU never has
|
288 |
|
|
to wait for a character to be transmitted. This is a simplification that
|
289 |
|
|
saves me the trouble to do a cycle-accurate simulation of the UART in the
|
290 |
|
|
software simulator.\\
|
291 |
|
|
|
292 |
|
|
The UART input is not simulated at all, for simplicity. So, for example, the
|
293 |
|
|
Adventure sample, which does read the console input, can't be properly
|
294 |
|
|
simulated past the first console input -- there is plenty of code to
|
295 |
|
|
simulate before that so this is no problem for the moment.\\
|
296 |
|
|
|
297 |
|
|
|
298 |
|
|
\section{Use of Modelsim Features}
|
299 |
|
|
\label{modelsim_dependencies}
|
300 |
|
|
|
301 |
|
|
Apart from the format of the simulation scripts, which would be easy to port
|
302 |
|
|
to any other simulation tool, the simulation test bench uses a feature of
|
303 |
|
|
Modelsim 6.3 that is not even present in earlier versions -- SignalSpy.\\
|
304 |
|
|
|
305 |
|
|
The test bench uses SignalSpy to examine internal cpu signals from the top
|
306 |
|
|
entity, including the whole register bank. There is no other way to examine
|
307 |
|
|
those signals in vhdl, unless you want to add them to the module interface.\\
|
308 |
|
|
|
309 |
|
|
The test bench needs to access those signals in order to detect changes in
|
310 |
|
|
the internal cpu state that should be logged. That is, it really needs to
|
311 |
|
|
look at those signals if it is to be of any use.\\
|
312 |
|
|
|
313 |
|
|
If you are using any other simulation tool, look for an alternative method
|
314 |
|
|
to get those internal signals or just add them to the core interface. I
|
315 |
|
|
would suggest adding a debug port of type record to mips\_cpu -- and hope the
|
316 |
|
|
synthesis tool does not choke on it. Adding individual debug ports would be
|
317 |
|
|
a PITA.\\
|
318 |
|
|
I guess this is why Mentor people took the trouble to write SygnalSpy.\\
|
319 |
|
|
|
320 |
|
|
I plan to move to Symphony EDA eventually, so I'll have to fix this.\\
|
321 |
|
|
|
322 |
|
|
Using GHDL would be an option, except because it only supports vhdl. The
|
323 |
|
|
project will use a SDRAM model in verilog for which I could not find a
|
324 |
|
|
vhdl replacement. If the project is to be ported to GHDL (a very desirable
|
325 |
|
|
goal even if only because not everybody has access to Modelsim) this will
|
326 |
|
|
have to be worked around.\\
|
327 |
|
|
|