Preface
The aim of this text is to provide a basic introduction into the world of nsim. The following chapters cover basic skills necessary to work with nsim.
Part I. First steps
Our journey begins in the CVS directory
liberouter/sys_sw/nsim/doc
it is the location of both files that we need.
sample_nanoprogram -- nanoprogram
instruction.def -- definitions of instructions
What instructions are defined in `instruction.def'?
STA n | saves the value `n' into the accumulator |
LDA addr | saves the content of the accumulator into the memory at address `addr' |
MOVR addr1, addr2 | saves the value stored at address `addr2' into the memory at address `addr1' |
XOR addr1, addr2 | performs xor operation between values stored at addresses `addr1' and `addr2' |
IN | reads a number from the input stream and saves it into the accumulator |
OUT | writes the content of the accumulator into the output stream |
NSIM installation
The first thing you need is a compiled and installed version of nsim. Nsim can be found in CVS in the directory
liberouter/sys_sw/nsim/
To install nsim go to the directory and type:
make clean
make
make install
The last command `make install' can be omitted, but do not forget to add your own location of nsim to your environment variable PATH in that case, i.e.,
export PATH=$PATH:~/liberouter/sys_sw/nsim/
Part II. Compiling
If we are prepared, let us begin with our lesson. Compilation of above mentioned nanoprogram can be done by typing
nsim sample_nanoprogram
This command does not produce any output. It only compiles given nanoprogram and checks for errors.
Compiled nanoprogram can be obtained by the command
nsim sample_nanoprogram -o sample_nanoprogram.bin
Parameter `-o' specifies the name of the output file where nsim saves the binary nanoprogram.
And really, try
bash-2.05b$ hexdump -C sample_nanoprogram.bin
00000000 0f 00 03 00 00 00 00 00 65 00 07 00 00 00 00 00 |........e.......|
00000010 05 00 03 00 00 00 00 00 64 00 07 00 00 00 00 00 |........d.......|
00000020 65 64 0f 00 00 00 00 00 64 00 07 00 00 00 00 00 |ed......d.......|
00000030 64 65 01 00 00 00 00 00 |de......|
00000038
There are options that can be used to change the format of the output. For example
nsim sample_nanoprogram -b -o sample_nanoprogram.txt
saves the binary output of the compilation in a text format as a sequence of zeros and ones.
bash-2.05b$ cat sample_nanoprogram.txt
00110000000000001111
01110000000001100101
00110000000000000101
01110000000001100100
11110110010001100101
01110000000001100100
00010110010101100100
Similarly, the option `-h' tells nsim to produce a text output in hexadecimal numbers.
Part III. Interpretation & Debugging
The compiled nanoprogram may be executed by an implementation of the nanoprocessor (i.e., VHDL simulation, programmed FPGA, ASIC). Nsim provides the possibility to execute the nanoprogram without any such hardware or software. Provided that the instruction set is proprely defined, nsim is capable of interpreting the program with real data and tracing how the program functions.
Interpretation of the nanoprogram can be done by typing
nsim sample_nanoprogram -i
This command causes nsim to invoke a debugger and begin the interpretation of the given program
Nanoprocessor assembler preprocessor (pass 1)
Preprocessing complete.
Building interpreter...
Success!
Nanoprocessor assembler preprocessor (pass 2)
Preprocessing complete.
Nanoprocessor assembler parser & compiler
Parsing & compilation complete.
Nanoprocessor interpreter
Beginning of program:
5 > lda 15
(nsim)
Debugger provides a set of commands. The list of the commands is available via the `help' command of the debugger (type `h' or `help' on the nsim prompt).
(nsim) h
run run program
continue continue till the next breakpoint
next interpret a line
step interpret a single instruction
setip ip set instruction pointer
break line_number set breakpoint
break label set breakpoint
delete line_number delete breakpoint
delete label delete breakpoint
info break list all breakpoints
print address[,to] print values of registers stored at specified address
printin address[,to] print values stored at specified data input address
printout address[,to] print values stored at specified data output address
list [from[,to]] list specified range of source lines
listp [from[,to]] list specified range of preprocessed source lines
where print current source line
i2l instruction_address conversion: instruction address to source line number
l2i source_line_number conversion: source line number to instruction address
count print number of instructions since last stop
silent do not print source lines when interpreting
loud [number] print [number] source lines between two stops
echo text print text on console
prompt text set prompt
quit quit program
(nsim)
What nanoprogram are we going to debug? It is easy to get this information using the `list' command
(nsim) list
1 #include "instruction.def"
2 #define A 100
3 #define B 101
4
5 > lda 15
6 sta B
7 lda 5
8 sta A
9 xor A, B
10 sta A
11 movr B, A
Not a long program, isn't it? Currently, we are standing on the line 5. That means that we have not processed the instruction at this line yet.
Line 1 specifies the name and location of the file with definitions of instructions.
Lines 2 and 3 assign the name A to the address 100 and the name B to the address 101.
The rest is a nanoprogram written in instructions specified by the file `instruction.def'.
If we abstract from the current line, we can easily obtain this info using the `where' command
(nsim) where
5 > lda 15
Instruction pointer (ip) = 0.
Let us proceed to the next instruction by typing
(nsim) next
6 > sta B
Now we have processed the instruction `lda 15'. It means that we have stored the value 15 into the special register named accumulator. To verify the content of the accumulator we type
(nsim) print ACCUMULATOR
register 0x100 stores value 0x0f 0000 1111 15
Correct, the accumulator holds the value 15---that is exactly what we have expected from the behavior of the instruction `lda 15'.
Now let us proceed to the next instruction and type `next' followed by commands `print 101' and `print B'. Command `print' prints the content of the memory at the given address
(nsim) next
7 > lda 5
(nsim) print 101
register 0x65 stores value 0x0f 0000 1111 15
(nsim) print B
register 0x65 stores value 0x0f 0000 1111 15
(nsim)
It is obvious from the lines above that there are two ways we can access the memory. The first way is direct through memory address. The second way is to access the memory via (in the nanoprogram) predefined variable. And we can simply check if register B is really in our nanoprogram attached to the memory 101.
(nsim) list 1,5
1 #include "instruction_m.def"
2 #define A 100
3 #define B 101
4
5 lda 15
When we are talking about variables and their addresses, there is another interesting variation of the `list' command, which prints out preprocessed form of debugged nanoprogram.
(nsim) listp
1
2
3
4
5 LDA 15
6 STA 101
7 > LDA 5
8 STA 100
9 XOR 100, 101
10 STA 100
11 MOVR 101, 100
The preprocessor has expanded the names of registers A, B into their address interpretations.
Before we proceed in our debugging, we will type
(nsim) list 1
1 #include "instruction_m.def"
2 #define A 100
3 #define B 101
4
5 lda 15
6 sta B
7 > lda 5
8 sta A
9 xor A, B
This command prints out the neighborhood of line 1. Now we are standing on the line 7 and I suppose that lines 7 and 8 are quite boring---so let us perform the interpretation until the line 9 with the `xor' instruction.
Help screen hints to try the `break' command.
(nsim) break 9
Breakpoint set at line 9. (instruction address 4)
(nsim) info break
line 9 instruction address 4 break 9
The command `break 9' has set the breakpoint at the line 9. The second command `info break' has printed out the list of all active breakpoints. We will proceed with the command `continue'
(nsim) continue
6 sta B
7 lda 5
8 sta A
9 > xor A, B
(nsim)
Excellent. Debugger has processed lines 7 and 8 and stopped at the line 9.
Note that nsim remembers the set of breakpoints from previous debugging sessions. These breakpoints are saved in `.break' files (in our case in the file `sample_nanoprogram.break'). You can remove breakpoints either manually during the process of debugging using the command `delete'
(nsim) delete 9
Breakpoint cleared at line 9.
or simply by removing the `.break' file.
Now we will continue in our lesson. We have stopped at the line 9 before processing the `xor' instruction. The accumulator holds the value 5. Let's process the ninth line.
(nsim) print ACCUMULATOR
register 0x100 stores value 0x05 0000 0101 5
(nsim) next
10 > sta A
(nsim) print ACCUMULATOR
register 0x100 stores value 0x0a 0000 1010 10
Accumulator holds the value 10. It is the result of the instruction `xor A, B'---which has in fact performed the action 15 XOR 5 (what gives 10). Note that operation `xor' saves its result into the accumulator.
Then the nanoprogram will save the content of the accumulator into the register A.
(nsim) next
11 > movr B, A
The instruction `movr B, A' follows---its semantics is saving the content of the register A into the register B.
(nsim) next
End of simulation.
Type `continue' to finish interpreted program.
(nsim) count
Total instructions: 7
(nsim)
Well. We are at the end of our program. As your homework verify that registers A, B and the accumulator hold the same value 10.
Note: Debugger supports shortcuts for the most of commands. You can write `n' instead of `next' or `c' instead of 'continue' and so on.


