views:

59

answers:

3

I have designed a MIPS I simulator using Verilator which allows me to wrap verilog code into c++. I am attempting to run a c++ program on my processor but I've reached some problems. My goal is to:

  1. write a test program in c++
  2. compile this program using a cross compiler g++ (mips-linux)
  3. take generated ELF file and disassemble it using objdump
  4. store the entire binary object dump in a text file
  5. open text file in my simulator
  6. run some text manipulating functions to isolate the HEX dump portion of the objdump
  7. load entire elf hex dump into my processor's memory (a c++ memory map containing elements keyed by their address in memory as defined by the ELF file.)
  8. run the program by setting the program counter and letting it go until exit syscall of program.

The problem would be steps 7 and 8. I have a very rudimentary understanding of the ELF file format. As far as I can tell (readelf can be used to output a program starting point) the program ounter should be set initially at the address of the beginning of the .text section. Unfortunately doing this does not result in a proper program run on my processor.

I have verified proper program execution of my processor by writing assembly programs, loading them into MIPS assembly simulators, and verifying, instruction by instruction, that the register file and generated addressing matches. What I don't understand is why I can't get even a "helloworld" program to run by writing in c++, compiling, and loading into my "memory"? I'm not particularly knowledgeable in this field. I could really use some help figuring this out.

My understanding is that .text and .data contain everything needed for my program to run. This is obviously not the case because as I traverse the .text section, my program does not execute correctly. Is there something else I need to do with the ELF file before loading it into memory?

A: 

Here is an explanation of elf that I liked, although it seems that you understand the elf issues. They also explain how g++ outputs elf files, so it might help you parse your output file (more good info in part 1 as well).

Hope some of that info helps.

Adam Shiemke
Good explanation. Easier to get through than the Wikipedia article. Thanks.
Dan Snyder
A: 

I have written a full MIPS I simulator that can load ELF binaries. You can get the source code here, maybe you'll get answers to your questions. There are also some demo programs included. The key point is to get the compiler to generate a freestanding executable that does not use any run-time library, not even the gcc's support library.

zvrba
I have also written a paper that describes in detail the workings of the simulator (including some ELF aspects). The paper abstract is here: http://www.computer.org/portal/web/csdl/doi/10.1109/ARES.2010.47Send me an email, so I can mail you the paper (I cannot publish it online due to copyright issues).
zvrba
I think that it would be a good idea for me to build my own cross compiler (instead of using the glitchy one supplied to me). Do you know of a good resource describing this process? Specifically for the mipsel-elf gcc target?
Dan Snyder
I sent you an email. Not sure if it made it through your filtering though.
Dan Snyder
Hm, it's not that hard to build a gcc cross-compiler. I deleted a VM where it lived, so I lost the configure options :-( With newer gcc versions I had to omit stack checking code and something related to mfpr library in order to get a working cross-compiler.
zvrba
Hmm, well, I have 2 options right now. Either fix my current cross compiler or build one on my own. I suppose building one would be better as I would then have an idea of how it works. (My BS ECE degree was not too focused on the concepts of linking and loading). As of now I just have a compiler that doesn't respond to ANY flags as far as I can see. Is there an easy fix for that or would it be better to just start over?
Dan Snyder
Would you mind explaining why the executable must not use any run-time libraries? My processor is capable of opening, closing, reading, and writing to files if necessary so it seems as though any run-time use of libraries should not affect whether the program runs or not. Either way it seems like I should be able to access my program within the ELF file but when I run the text section with data accessible by the processor my processor doesn't work. I've verified functionality with a gold standard so I think Im missing something...
Dan Snyder
A: 

zvrba may have hit the nail on the head, you cannot/should not call C library functions like printf in your program.

Write a simple C program to start with maybe:

const unsigned char hello[]="helloworld";

void notmain ( void )
{
   unsigned int ra;

   for(ra=0;hello[ra];ra++)
   {
       PUT32(0x1234,hello[ra]);
   }
}

and call notmain from the assembler programs that you have written and have working and link together.

PUT32 just writes some data to some address, I normally implement these in assembler, ymmv.

Choose some address other than 0x1234, my assumption is with your sim environment you can watch accesses to an address location and watch the characters. no need yet to talk to a uart and have to decode serial in a simulation when you can watch the bytes on a bus.

Elf files really are simple to parse, if you have written a simulator, reading an elf file is not a big deal. I dont bother with libraries they just make it harder. It is a few structures if you chose to use structures. I can provide you with code that will get you started if you like. An alternative is to use gnu tools to convert the elf into a binary file (mips-whatever-objcopy file.elf -O binary file.bin). If your .text and .data are not close to each other the objcopy program will make a huge file with zeros for fill between the two address spaces. For embedded you want to avoid having anything in your .data section anyway, always initialize variables in the program not ahead of time and read only tables make const so they are in .text instead of .data

is this a project for fun or work or general public consumption? I might be interested in using it someday I like the concept of verilator but its either limited or too rigid to the verilog standard and so much verilog out there wont run under it without work, so I have not gotten to really play with it.

good luck.

dwelch
The main reason why I used a map in c++ to represent my memory is because all empty memory locations are implied. If an element/key pair was established then there is an element in memory, if nothing was allotted to a specific space, when it is addressed by the simulator the return value is by default "0".
Dan Snyder
This project is for research but the simulator I am constructing isn't really proprietary. It's just a tool I'll be using to try out various super pipe-lined architectures to compensate for the reduction in performance as supply voltage is dropped to near sub-threshold levels. I'd be happy to send it to you when it's up and running.
Dan Snyder
Verilator's biggest advantage is that it's very fast. This is mainly due to the fact that it only compiles synthesizable verilog which does place a constraint on your coding freedom but because you can also write everything directly in C++ too, there's a nice sweet spot where one can take advantage of both verilog and c++. Also the founder of Verilator is very easy to get in contact with for questions so it's easy to figure out.
Dan Snyder
Also, I'm using objdump to disassemble the ELF file. I was using readelf in the past but it seems pointless to separately load each segment file by file. I just run obj dump, place disassembled output into a text file, delete everything but addressing and hex words and load away. Works well. I'll try out PUT32 and see if I can indeed access this element in memory. AS of now my compiler is stating that PUT32 is undecared. I'll see whats up with it.
Dan Snyder
PUT32 is my own function, not a C library thing, simple to implement. Esp in your environment, which is similar to where I live, you can write one program in C, note PUT32 is not in the same source file it is implemented in a separate file. You could implement PUT32 so that it reaches into the verilator simulation thorugh C++ and hits the hardware, but the program is running on the host, or compile the program for the target and use a different implementation of PUT32 for the target and run on the target in simulation
dwelch
later, if/when the target becomes silicon you can again run embedded with PUT32 implemented for embedded on that target, or implement PUT32 to make a call through the operating system or kernel driver and run the same original test/application program. Takes some practice to get a single test/app program operating system independent. I have had a lot of recent success with this approach through a chip design and into silicon
dwelch
We had professional tools, it was when the number of shared licenses were consumed and we were sitting around twiddling our thumbs is when we wished that the project, already large, would build under verilator or icarus verilog. The time to get our project to build with verilator was not worth it, esp since that was someone elses code (and we wouldnt be able to justify or save those changes) our job was chip test and board bring up (software).
dwelch