On Friday, 31 July 2020 at 15:13:29 UTC, Chad Joan wrote:
THAT SAID, I think there are things to try and I hope we can get you what you want.

If you're willing to entertain more experimentation, here are my thoughts:

Thanks a lot for the suggestions and explanations. I tried them all but the only time I got different assembly code was your suggestion (3) which produced the following.

(3) Try a different type of while-loop in the D-style version:

// ideomatic D version
void write_to_host(in string msg) {
        // a fixed address to get bytes to the host via usb
        char *usb_slave = cast(char*)BaseAdr.ft232_slave;
        size_t i = 0;
        while(i < msg.length) {
                *usb_slave = msg[i++];
        }
}
_D10firmware_d13write_to_hostFAyaZv:
        addi     sp, sp, -8
        addi     r4, r0, 4096
        sw       (sp+8), r2
        sw       (sp+4), r1
        addi     r2, r0, 0
.L3:
        or       r5, r2, r0
        be     r2,r1,.L1
        lw       r3, (sp+8)
        addi     r2, r2, 1
        add      r3, r3, r5
        lbu      r3, (r3+0)
        sb       (r4+0), r3
        bi       .L3
.L1:
        addi     sp, sp, 8
        b        ra

At any rate, I don't think your code is larger or less efficient due to utf-8 decoding, because I don't see the utf-8 decoding.

Agreed, I think there's no code for autodecoding being generated.

I did some more experiments:
Trying to put pointer and length into a struct and pass that to the function. Same result, the argument ended up on the stack.

Then, I wrote the function in C and compiled it with the C-compiler of lm32-elf-gcc. It also puts the 64-bit POD structure on the stack. It seems the only arguments passed in registers are primitive data types. However, if I pass a uint64_t argument, it is registered using registers r1 and r2. So the compiler knows how to use r1 and r2 for arguments. I checked again in the lm32 manual (https://www.latticesemi.com/view_document?document_id=52077), and it says:

"As illustrated in Table 3 on page 8, the first eight function arguments are passed in registers. Any remaining arguments are passed on the stack, as
illustrated in Figure 12."

So strings and structs should be passed on the stack and this seems to be more an issue of the gcc lm32 backend than a D issue.

But I just found a workaround using a wrapper function.

void write_to_host(in string msg) {
        write_to_hostC(msg.ptr, msg.length);
}

I checked the assembly code on the caller side, and the call write_to host("Hello, World!\n") is inlined. There is only one call to write_to_hostC. This is still not nice, but I think I can live with that for now. Now I have to figure out how make the cast back from from pointer-length pair into a string. I'm sure I read that somewhere before, but forgot it and was unable to find it now on a quick google search... And since this is D: is there maybe some CTFE magic that allows to create these wrappers automatically? Somthing like

fix_stack!write_to_host("Hello, World!\n");


Good luck with your lm32/FPGA coding. That sounds like cool stuff!

I'm doing this mainly to improve my understanding of how embedded processors work, and how to write linker scripts for a given environment. Although I do have actual hardware where I can see if everything runs in the real world, I mainly use simulations. The coolest thing in my opinion is, nowadays it can be done using only open source tools (mainly ghdl and verilator, the lm32 source code is open source, too). The complete system is simulated and you can look at every logic signal in the cpu or in the ram while the program executes.

Reply via email to