-
Notifications
You must be signed in to change notification settings - Fork 139
Assignment 1
** Note: this assignment is finished.** |
---|
In this assignment you will implement the main interfaces of the functional memory. They include initialization from a MIPS executable file in the ELF format, reading/writing data and others.
For this assignment you should create a fork of our repo as it is described in this wiki page and work only there.
- You should work in forked repository. See Git&GitHub cheat sheet for instructions.
- In forked repository create branch task_1:
git branch task_1
. See Pro Git about branching in Git - Switch to your new branch with
git checkout task_1
command. Your Assignment 1 should be done in this branch. - Use
assert
macro fromassert.h
to handle wrong parameters and to protect your code from hidden errors. For more information aboutassert
usage visit this page. - You have to follow our code guidelines. Don't violate them!
- Set tabs as 4 spaces in your text editor.
Don't send pull requests to main repository - just contact project mentors via e-mail when you finish task |
---|
The class ElfSection
implements functionality to parse a MIPS executable file, download and store content of all their sections. The class is defined in trunk/func_sim/elf_parser/elf_parser.h
.
Note: the ELF parser has already implemented. You don't need to change anything in it. |
---|
Functionality for parsing ELF executable files of the MIPS architecture is already implemented. To create a vector of objects of ElfSection
class that contain the data of all the section in the binary, you have to invoke the following static function:
static void getAllElfSections( const char* elf_file_name,
vector<ElfSection>& sections_array /*used as output*/);
with the following parameters:
-
elf_file_name
- name of a MIPS executable file (e.g.mips_bin_exmpl.out
). You can find examples of such files intrunk/tests/samples/
or even create your own files (for more information seeREADME.txt
in that folder). -
sections_array
- STL vector that is used as output container (don't worry you will be able to complete this task even if you don't know STL).
You can see how it works in trunk/func_sim/elf_parser/main.cpp
. This file implements elf_parser
program that downloads and prints all the sections from the executable file given as the only parameter (e.g. ./elf_parser mips_bin_exmpl.out
). The output will be something like the following:
Dump ELF section ".reginfo"
size = 24 Bytes
start_addr = 0x400094
Content:
0x400094: 000c0000
....
0x4000a8: 70814100
Dump ELF section ".text"
size = 16 Bytes
start_addr = 0x4000b0
Content:
0x4000b0: 41000b3c
0x4000b4: cc006b25
0x4000b8: 04006a8d
....
Dump ELF section ".data"
size = 192 Bytes
start_addr = 0x4100c0
Content:
0x4100c0: 00010203
0x4100c4: 04050607
0x4100c8: 08090000
0x4100cc: 07000000
0x4100d0: 0b000000
0x4100d4: 0d000000
....
To build elf_parser
just type make
in trunk/func_sim/elf_parser/
.
Compare the output of this program with the data printed by the standard mips-objdump
(mips-objdump -D mips_bin_exmpl.out
). You can see that each byte has the same value, but bytes in a words are mirrored. It occurs, because mips-objdump
prints each word as a value of 4 Bytes from left to right (the least significant byte on the left), but elf_parser
prints each word per byte from right to the left.
The functional memory is an abstraction that present how the memory is defined in the ISA. In the other word, it is how a programmer sees the memory. Note that functional memory does not reflect the organization of the memory in the real machine.
In short, the functional memory is a sequence of bytes. Each of them has unique address and can be read and written using it.
The FuncMemory
class implements a functional memory model. It provides interfaces to parse an ELF formatted executable file, download and store content of ELF sections, read and write data. The class is defined in trunk/func_sim/func_memory/func_memory.h
.
Note: all interfaces of the FuncMemory class are empty. You have to implement them. |
---|
The typical address size for the MIPS architecture is equal to 32 bits, i.e. the memory size is 2^32 = 4 GB. It is clear that such amount of memory cannot be assigned in the simulator. Moreover, an usual program uses significantly less memory.
In order to not allocate the whole required bytes at once, but still be able to read or write by any address, the functional memory is organized as a hierarchy.
Note: that is done only because of the limitation of our program model and does not reflect the real memory organization. |
---|
There are three levels in our implementation of the functional memory:
- The lowest level is a page. It is a block of bytes that keeps the real data.
- The middle level is a set of pages. It is an array of pointers to all pages that includes in the set.
- The highest level is an array of sets.
The hierarchy is shown on the following picture:
NULL
means that this set
or page
has not been written yet and the memory for it is not allocated in the simulator.
The values of set number
, page number
and offset
can be extracted directly from the memory address. The following picture shows the address separating:
If the three level hierarchy is too complex for you, it is possible to implement a simpler hierarchy described in this section. Note that in this case you will receive lower mark, but it is better than nothing.
In the simplified version the functional memory is just an static array of bytes (i.e. no dynamic memory allocation). In this case the size of your memory will be small and can be able to process only a limited range of memory address. For our work, it will be enough to process only addresses from the following range: [0x3FFFF0; 41FFF0]
. Therefore, the array will contain 0x20000 bytes. As it is not a very larger number, it can be allocated at once statically.
Note: if you decide to choose this option you should not care about address separation into set , page and offset . |
---|
Note: the constructor of the FuncMemory class is empty and to implement them is a part of the first assignment. Use ElfParser class to do that. |
---|
An object of FuncMemory
class is initialized taking the information from the binary ELF file. To create an object of FuncMemory
class and download context of ELF sections, you have to invoke its constructor:
FuncMemory ( const char* executable_file_name,
uint64 addr_size = 32,
uint64 page_num_size = 10,
uint64 offset_size = 12);
with the following parameters:
-
elf_file_name
- name of a MIPS executable file (e.g.mips_bin_exmpl.out
). You can find examples of such files intrunk/tests/samples/
or even create your own files (for more information seeREADME.txt
in that folder). -
addr_size
- the size of the memory address (number of bits). -
page_num_size
- the number of bits in the memory address that represent the page number inside the set. -
offset_size
- the number of bits in the memory address that represent the byte number inside the page.
You can see how it should work in trunk/func_sim/func_memory/main.cpp
. This file implements func_memory
program that downloads sections from an executable file given as the only parameter (e.g. ./elf_parser mips_bin_exmpl.out
) and prints the contents of the functional memory. To build func_memory
just type make
in trunk/func_sim/func_memory/
.
Note: the read interfaces are empty and to implement them is a part of the first assignment |
---|
Class FuncMemory
contains only one method to read the data of the stored section:
uint64 read( uint64 addr, unsigned short num_of_bytes = 4) const;
It returns a value stored in the byte sequence, which length is equal to num_of_bytes
and the address of the first byte of which is equal to addr
.
For example, if the functional memory has the following content (the order of bits into the bytes are from left to right):
0x400090 0x400091 0x400092 <-- addresses of bytes
| | |
... | 0001 0011 | 0011 1000 | 1111 0010 | 1101 0000 | ...
Then read( 0x400091, 2)
return the value of subsequence | 0011 1000 | 1111 0010 |
, which is equal to 0x4f1c
.
This method just return the start address of .text
and has the following declaration:
uint64 startPC() const;
It is used to know where the first instruction of the program is located.
It is similar to the read
despite the fact that the data is not read, but written by the specified address. The method has the following interface:
void write( uint64 value, uint64 addr, unsigned short num_of_bytes = 4);
This method is used to display the contents of the functional memory. It should print the information in the format like dump
method of the ElfParser
class does.
The Google C++ Testing Framework is used in our project to check the quality of our code. The classes ElfParser
and FuncMemory
have already been covered by unit tests, which are located in trunk/func_sim/elf_parser/unit_test.cpp
and trunk/func_sim/func_memory/unit_test.cpp
correspondently. To run the tests just type make test
. It builds the tests and run them. The test log will be printed and you will see passed and failed (if they are) tests.
For example, for ElfParser
the output will be something like:
alexandr.titov@helena:~/mipt-mips/func_sim/elf_parser$ make test
Running ./unit_test
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from Elf_parser_init
[ RUN ] Elf_parser_init.Process_Wrong_Args_Of_Constr
[ OK ] Elf_parser_init.Process_Wrong_Args_Of_Constr (4 ms)
[----------] 1 test from Elf_parser_init (4 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (5 ms total)
[ PASSED ] 1 test.
Unit testing for the moduler ELF parser passed SUCCESSFULLY!
Phrase Unit testing for the moduler ELF parser passed SUCCESSFULLY!
in the end means that everything was ok and your changes have passed all tests.
Note that in the beginning the unit tests for FuncMemory
class will not pass as its interfaces are not implemented. You will see something like this:
alexandr.titov@helena:~/mipt-mips/func_sim/func_memory$ make test
Running ./unit_test
[==========] Running 5 tests from 2 test cases.
[----------] Global test environment set-up.
[----------] 1 test from Func_memory_init
[ RUN ] Func_memory_init.Process_Wrong_Args_Of_Constr
unit_test.cpp:28: Failure
Death test: FuncMemory func_mem( wrong_file_name)
Result: failed to die.
Error msg:
[ DEATH ]
[ FAILED ] Func_memory_init.Process_Wrong_Args_Of_Constr (6 ms)
[----------] 1 test from Func_memory_init (6 ms total)
[----------] 4 tests from Func_memory
[ RUN ] Func_memory.StartPC_Method_Test
unit_test.cpp:35: Failure
Value of: 0x4000b0
Actual: 4194480
Expected: func_mem.startPC()
Which is: 140736709710576
[ FAILED ] Func_memory.StartPC_Method_Test (1 ms)
[ RUN ] Func_memory.Read_Method_Test
unit_test.cpp:47: Failure
Value of: right_ret
Actual: 50462976
Expected: func_mem.read( dataSectAddr)
Which is: 0
[ FAILED ] Func_memory.Read_Method_Test (0 ms)
[ RUN ] Func_memory.Write_Read_Initialized_Mem_Test
unit_test.cpp:81: Failure
Value of: right_ret
Actual: 50462977
Expected: func_mem.read( data_sect_addr)
Which is: 0
[ FAILED ] Func_memory.Write_Read_Initialized_Mem_Test (1 ms)
[ RUN ] Func_memory.Write_Read_Not_Initialized_Mem_Test
unit_test.cpp:107: Failure
Value of: right_ret
Actual: 256
Expected: func_mem.read( write_addr, sizeof( uint16))
Which is: 0
[ FAILED ] Func_memory.Write_Read_Not_Initialized_Mem_Test (1 ms)
[----------] 4 tests from Func_memory (3 ms total)
[----------] Global test environment tear-down
[==========] 5 tests from 2 test cases ran. (10 ms total)
[ PASSED ] 0 tests.
[ FAILED ] 5 tests, listed below:
[ FAILED ] Func_memory_init.Process_Wrong_Args_Of_Constr
[ FAILED ] Func_memory.StartPC_Method_Test
[ FAILED ] Func_memory.Read_Method_Test
[ FAILED ] Func_memory.Write_Read_Initialized_Mem_Test
[ FAILED ] Func_memory.Write_Read_Not_Initialized_Mem_Test
5 FAILED TESTS
make: *** [test] Error 1
However, when you complete them the testing has to pass successfully and you will see something like:
alexandr.titov@helena:~/mipt-mips/func_sim/func_memory$ make test
Running ./unit_test
[==========] Running 5 tests from 2 test cases.
[----------] Global test environment set-up.
[----------] 1 test from Func_memory_init
[ RUN ] Func_memory_init.Process_Wrong_Args_Of_Constr
[ OK ] Func_memory_init.Process_Wrong_Args_Of_Constr (9 ms)
[----------] 1 test from Func_memory_init (9 ms total)
[----------] 4 tests from Func_memory
[ RUN ] Func_memory.StartPC_Method_Test
[ OK ] Func_memory.StartPC_Method_Test (1 ms)
[ RUN ] Func_memory.Read_Method_Test
[ OK ] Func_memory.Read_Method_Test (927 ms)
[ RUN ] Func_memory.Write_Read_Initialized_Mem_Test
[ OK ] Func_memory.Write_Read_Initialized_Mem_Test (225 ms)
[ RUN ] Func_memory.Write_Read_Not_Initialized_Mem_Test
[ OK ] Func_memory.Write_Read_Not_Initialized_Mem_Test (0 ms)
[----------] 4 tests from Func_memory (1154 ms total)
[----------] Global test environment tear-down
[==========] 5 tests from 2 test cases ran. (1163 ms total)
[ PASSED ] 5 tests.
Unit testing for the moduler functional memory passed SUCCESSFULLY!
Otherwise, you did something wrong or your code has an error.
MIPT-V / MIPT-MIPS — Cycle-accurate pre-silicon simulation.