| 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 |
|
|
|