Search
Gain an understanding of how are the machine instructions executed/processed by a simple processor.
The simplicity, straightforwardness, and orthogonality of the instruction encoding is an essential reason why most textbooks choose as a model architecture MIPS processor. The processor reads instructions encoded in binary form. PC-class personal computers do not allow us to execute MIPS-based machine code directly on their processor (architecture X86), but there exist many simulators of this architecture. The simulator QtMips was developed to support the practical experience of this course.
Detailed description:
A more detailed description of the instructions can be found at Wikipedia https://en.wikipedia.org/wiki/MIPS_architecture.
Autoritative description of architecture https://www.mips.com/:
MIPS32 Instruction Set Quick Reference v1.01
The MIPS32 Instruction Set v6.06
There is no restriction to use any plain text editor for writing assembler source code. In the laboratory, there are installed more text editors and development environments. For example vim, Emacs, etc. We suggest to select Geany for those who have no personal preference.
Open a text editor and prepare simple assembler source file named simple-lw-sw.S. The suffix capital “.S” is crucial. This suffix is assigned by standard development tools for the source files/code in assembler language and compiler decides according to the suffix how to process the file. Other recognized suffixes are .c for C language source files. .cc or .cpp for C++, .o for object files (files which content is already translated source code into target machine native instructions but there is not defined to which address will be these code fragments located). Library files suffix .a (archive) is used for functions libraries, which are included in final executable according to their requirements/use by other files. A final executable file (program) is stored without extension on Unix class systems..
simple-lw-sw.S
.S
.c
.cc
.cpp
.o
.a
.globl _start .set noat .set noreorder .text _start: // load the word from absolute address lw $2, 0x2000($0) // store the word to absolute address sw $2, 0x2004($0) loop: // stop execution wait for debugger/user break // ensure that continuation does not // interpret random data beq $0, $0, loop nop .data src_val: .word 0x12345678 dst_val:
Assembly code source file consists of
lw
sw
add
sub
.
The following directives are used in the provided sample code
_start
_ _start
la
lui
ori
.text
.data
The complete manual for GNU assembler and all its directives can be found at GNU assembleru.
The compilation is performed by cross-compiler mips-elf-gcc (cross-compiler means that compiler host system, PC in our case, is different than compilation target system with MIPS architecture).
mips-elf-gcc -Wl,-Ttext,0x1000 -Wl,-Tdata,0x2000 -nostdlib -nodefaultlibs -nostartfiles -o simple-lw-sw simple-lw-sw.S
The invocation requires multiple parameters, because standard programs in C language require linking of initialization sequences and library functions. But our actual goal is to describe the lowest level without this automatization the first which allows understanding how it is extended and equipped by automatic initialization sequences and constructs which allows comfortable code writing of programs at a higher level and in higher level languages. Parameters -Wl, are passed to the linker program and specify to which address is located .text section with program instructions and to where starts .data section with initialized variables. Following parameters in the order of their appearance disable automatic addition of startup and initialization sequence (-nostartfiles) and disable use of standard libraries. The file name after switch -o specifies the name of the final executable file (output). File names of one or more source files follow.
-Wl,
-nostartfiles
-o
/opt/apo/qtmips-semstart
On Windows, complete MSys with make utility can be installed or plain compiler mips-elf-gcc-i686-mingw32 can be called from following batch file.
make
PATH=%PATH%;c:\path\to\mips-elf-gcc-i686-mingw32\bin mips-elf-gcc -Wl,-Ttext,0x1000 -Wl,-Tdata,0x2000 -nostdlib -nodefaultlibs -nostartfiles -o simple-lw-sw test.S
QtMips simulator is used to execute the program. Select the most simple variant of simulated processor “No pipeline no cache”. Button “Browse” is used to select executable file name (simple-lw-sw without suffix in our case) for field “Elf executable”.
simple-lw-sw
Select tab Core next and disable checkbox Delay slot. This configuration makes simulator to diverge from real MIPS architecture, but it is more straightforward for initial experiments. The program is written in the mode set .noreorder is translated 1:1 to the instruction sequence for this setup and branch instruction execution would be more intuitive - they are processed immediately. We return to this topic later with full reasons for real processor behavior.
Core
Delay slot
set .noreorder
The diagram with the processor is opened. Use double-click on program counter register (PC) opens program listing with actual instructions. Double click on registers blocks opens a view with the list of architectural registers. Double click on data memory shows memory content. The windows layout shown on the next picture is the appropriate and intuitive starting point
Select “Follow fetch” option in the “Program” listing window which highlights instruction/line actually fetched by the processor for execution. The start of listing in “Memory” windows should be set to the address 0x2000 on which data value 0x12345678 was placed. The program can be stepped through by “Machine” → “Step” menu entry or by the corresponding button on the toolbar. The value is loaded to the register first, and then it is stored to the following memory cell.
Change the program to process a load-store sequence in the loop. Instruction break has to be removed from the loop because its purpose is to stop program execution when reached. Test that edited value at address 0x2000 is always copied to the address 0x2004. Modify the program to add two input values on 0x2000 and 0x2004 address and stored the result at address 0x2008.
break
Next step is to modify the program to add two vectors with a length of four words. Use assembler macroinstruction la vect_a (load address) to set registers to point to the start of the vectors.
la vect_a
... .data vect_a: .word 0x12345678 .word 0x12345678 .word 0x12345678 .word 0x12345678 vect_b: .word 0x12345678 ...
Continue with implementation of the program to compute an average value from eight numbers.
The compiler invocation is desirable to document at least and better automate. The one way is to use a script with the sequence of commands required for compilation. Such script can be written directly for shell - command line interpreter (BASH or DASH on GNU/Linux usually). But it is not practical to translate all compilation units of a larger project when only small change modifies only one or a small subset of source files. More different systems have been developed to automate exactly these tasks. Some examples are Make, Ant, qmake, Cmake, meson, etc.
Make is the tool which allows to automate compilation of source codes, description of the compilation process is described in Makefile.
Makefile template for the compilation of source files written in assembly language or C language for MIPS simulator environment:
ARCH=mips-elf CC=$(ARCH)-gcc AS=$(ARCH)-as LD=$(ARCH)-ld OBJCOPY=$(ARCH)-objcopy CFLAGS += -ggdb -O1 AFLAGS += -ggdb LDFLAGS += -ggdb LDFLAGS += -nostdlib -nodefaultlibs -nostartfiles LDFLAGS += -Wl,-Ttext,0x1000 -Wl,-Tdata,0x2000 all:default .PHONY:clean %.srec:% $(OBJCOPY) -O srec $< $@ %.o:%.S $(CC) -D__ASSEMBLY__ $(AFLAGS) -c $< -o $@ %.s:%.c $(CC) $(CFLAGS) $(CPPFLAGS) -S $< -o $@ %.o:%.c $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ # default output default:change_me.srec # executable file:object file.o change_me:change_me.o $(CC) $(LDFLAGS) $^ -o $@ # all generated that would be cleaned clean: rm -f change_me change_me.o change_me.out change_me.srec
Makefile consists from definitions (assignment of values to variables) and rules. The rules start by a line which defines dependency of rule target(s) on the dependencies listed after the colon. The dependencies are names of files or abstracts commands which as to be (make) available before the commands following the first rule line can create required results. File names can be complete names or their base part can be substituted by character “%” which allows specifying rule for a whole class of transformations from one compilation stage to another. Even more complete template for the compilation of assembler and C source files to MIPS target platform with an automatic building of dependencies on header files can be found in directory /opt/apo/qtmips_template on the computers in the laboratory.
/opt/apo/qtmips_template
A compilation is invoked by a command make (the make has to be invoked in the directory where Makefile and program source code are located). Make generates multiple output files. The file without an extension is used for execution in QtMips environment. The process of compilation translated the compilation unit (one source wile together with includes header files for a simple case) into object files (.o) in relocatable form. Object files are then collected by the linker which resolves address references between compilation units and locates code to the final addresses. It is necessary to equip the actual sequences of machine instructions by the envelope which specifies where to fill references during .o files linking during the final placement on the specified addresses. Even instruction and data in the final executable form usually require some information for operating systems where they should be loaded/mapped in the memory or process address-space. The ELF (Executable and Linkable Format) is used to store these metadata in our case and generally on most of the modern systems.
Next command can be used to find addresses of the final location of variables and data entries after linking
mips-elf-nm program
The list of sections and their locations can be listed by
mips-elf-objdump --headers program
List final machine code after translation with corresponding source lines
mips-elf-objdump --source program
nop
vec_a
vec_b
vec_c
vec_c[0] = vec_a[0] + vec_b[0]
gcc -E assembler.S -o preprocessed-pro-mips.s can be used for preprocessing.
gcc -E assembler.S -o preprocessed-pro-mips.s