Mill Computing, Inc. Forums The Mill Tools Simulators I was at it again.

  • Author
  • Thomas D
    Post count: 18
    #3515 |

    I’m not sure if I should call this genAsm or conAsm. ConAsm is targeted to a specific machine, but the specific machine I’m targeting is a very generic machine. Then again, I am not trying to duplicate genAsm.

    I started this three weeks ago and am unhappy with myself over where I’ve gotten with it.
    Now, I guess I am fishing for criticism. Does the “Hello World” program look as you would expect it to? I realize that the approach I have taken is pretty bad under the covers, but the goal is to get something that algorithms can be tested on that is close to the language you can see in the videos. Hopefully, the code appears straight forward as to how one would implement their own opcodes. It’s a starting point, I hope.

  • Ivan Godard
    Post count: 594

    In case this is helpful to contrast your own work, here is the Mill tool chain assembler output for Hello World on the Copper configuration. It is two bundles here because Copper is slot-limited on the flow side; on Silver or Gold it is the same instructions but only one bundle.

    Reminder: Mill conAsm is really C++; this file is compileable by Clang/LLVM given appropriate #include’s.

    s8:~/mill/build/testsuite$ cat hello*asm
    #include    "absParse.hh"
    int main(int argc, char* argv[]) {
            getOpt(argc, argv);
    Section( .data )  {
    Section( .rodata )  {
        const char* __x_str =
            "Hello, World!\12\0";
        assembler::defData("__x_str", private$, 0, over(__x_str, 15));
    Section( .bss )  {
    Section( .text ) assembler::defFunc("memcpy", unknown);
    Section( .text ) assembler::defFunc("memmove", unknown);
    Section( .text ) assembler::defFunc("memset", unknown);
    Section( .text ) assembler::defFunc("printf", external);
    Section( .text ) assembler::defFunc("main", external);
        lea(cpp, gl("__x_str")) %1 ^13;
                    // V%1
        rd(w(0)) %0 ^12,
            // L6C2F0=/home/ivan/mill/testsuite/hello.c
        call0("printf", b1 %1) ^14,                                 // L5C2F0
        retn(b0 %0) ^15;                                            // L6C2F0
                    // V%0 ^%1 ^%0
    Section( .init_array )  {
    Section( .fini_array )  {
            return 0;
  • Thomas D
    Post count: 18

    This is so much more complicated than I imagined. I guess it stems from the maturity of what you are doing vs what I am doing. I am writing a toy program to execute toy programs on a thought experiment of a processor. This has all the warts of the real deal. Did you overload the ‘%’ and ‘^’ operators? Is it wise or prudent to ask?

    One nagging question: you said that it is only one bundle, but I see data dependencies within the bundle. The call0 uses the result of the lea, no? Am I missing something? Is it that the logical belt state changes at the end of each phase? That’s going to change what I was doing.

    • Ivan Godard
      Post count: 594

      Yes, the ‘%’ and ‘^’ operators are overloaded, as is the ‘,’ operator. C++ lends itself fairly well to the creation of Application Specific Languages such as our conAsm assembler format.

      Mill bundle execution extends over three physical cycles on which seven phases are overlain. Each op has a spec which tells in which phase each argument is evaluated (which may differ for different args), and which phase it drops its results. If the drop phase of one op is earlier than the eval phase of another then data can be passed among ops in the same bundle; that’s what’s happening in the lea->call dependency you noted.

      The listing includes a comment for each bundle that shows the belt events that happen during the cycle that contains the opPhase of that bundle. These comments are to help the reader understand what is going on, because so much is happening all at once. For example, the comment on the bundle with the call is “// V%0 ^%1 ^%0”. This says that (in that cycle) %0 was dropped (“V”) and %0 and %1 were evaluated (“^”). Looking at the ops, you see that V%0 came from the rd(), while ^%1 went to the call0() and ^%0 to the retn(). The items in the comment are in time order, so a ^%n can see and use a V%n to its left or in the comments of earlier bundles. Hence the retn can use the result of the rd, and so on.

      As for the rest, the “section” stuff is organization material for the linker, and is irrelevant if you are not doing separate compilation. The “main” at the top and “return” at the bottom is to make the whole thing into an executable C++ program. In the assemble step in the tool chain, that C++ program is compiled and executed. The execution builds an internal representation of the program and processes it to build ELF binaries or the source which when compiled is the simulator for the program. We’re working on going direct from specializer to binary, making the assembler step optional, but that’s not up yet.

      BTW, it’s always wise and prudent to ask 🙂 Most companies seem to feel that it’s not prudent to answer, but we differ.

You must be logged in to reply to this topic.