rust-embedded / wg

Coordination repository of the embedded devices Working Group
1.86k stars 95 forks source link

Raw string runtime error with target riscv32i-unknown-none-elf #750

Closed universal-git closed 2 months ago

universal-git commented 2 months ago

Target: riscv32i-unknown-none-elf, target-feature=+m, no_std Raw strings in the rust code would compile for all lengths but they show garbage values at runtime. Cases:

  1. Strings with small length e.g. len<16 would compile and run fine for below case: let buff = b" Github brings\r\n"; ...,but for the below code, it takes garbage: let buff = b" Github brings us all together\r\n";

  2. push_str: here, the len constraint reduces even more. e.g. num_str.push_str(" Git\r\n"); would compile and run, but, num_str.push_str(" Github.com\r\n"); ...would not.

Is there any known solution?

jonathanpallant commented 2 months ago

Would it be possible to set up a godbolt link that demonstrates the issue?

jonathanpallant commented 2 months ago

This seems fine, for example:

https://godbolt.org/z/q8oK9jv17

universal-git commented 2 months ago

Tried here, https://godbolt.org/z/oTTo1GPnG , this looks alright. But, let me share my local one:

  1. Code from the project: let buff = b"Github brings us all together"; let mut num_buff: [u8; 30] = [0;30]; //to test output together with string transformtobytes(loop_num as usize, &mut num_buff); //usize to decimal ascii bytes num_buff[0] = '\r' as u8; numbuff[1] = '\n' as u8; let mut ser = Serial::new(periph.uart0); //serial let = embedded_io::Write::write(&mut ser, &numbuff).unwrap(); //serial write let = embedded_io::Write::write(&mut ser, buff).unwrap(); this gives uart output: 174#�#,�7 m#� #,� 175#�#,�7 m#� #,� 176#�#,�7 m#� #,� 177#�#,�7 m#� #,� 178#�#,�7 m#� #,� 179#�#,�7 m#� #,� etc..

whereas, if buff is set to b"Github", output is: 1Github 2Github 3Github 4Github 5Github etc..

Also sharing the output objdump of both the builds. Git__together.txt Github.txt In this asm, but each string length generates different result.

key to look into the asm objdumps: each block before "fence" writes a byte to uart: 200278: 13 05 00 00 li a0, 0 20027c: 83 25 4b 11 lw a1, 276(s6) 200280: 93 f5 05 06 andi a1, a1, 96 200284: e3 8c 05 fe beqz a1, 0x20027c <_main+0x1ac> 200288: b3 85 ad 00 add a1, s11, a0 20028c: 83 c5 05 00 lbu a1, 0(a1) 200290: 23 20 bb 10 sw a1, 256(s6) 200294: 0f 00 f0 0f fence

In the long string case, you can notice that it is not writing all the bytes.

universal-git commented 2 months ago

I've done the above with target=riscv32i-unknown-none-elf, +m using "rustc 1.77.2 (25ef9e3d8 2024-04-09)" opt-level=3

  1. Meanwhile, inspecting the linker if I've done any mistake. Problem in accessing .data section may be another local possibility.

Tried with nightly but no different results. -end-

jonathanpallant commented 2 months ago

Can you post the source code that reproduces this issue? I feel it's unlikely to be the target, as I think that target gets used by a lot of Espressif chips.

universal-git commented 2 months ago

Sure, here pls:

https://github.com/universal-git/riscv_write/tree/main

I tried studying the asm dump further, it loops and tries to load the string data from memory, probably the fetch address fails. study notes:

  1. address loaded to s0: 0x200fb80
  2. len of the long string loaded to s1=0x1a
  3. The byte write loop: 2002b0: 93 fa 0a 06 andi s5, s5, 0x60 2002b4: e3 9c ea fe bne s5, a4, 0x2002ac <_main+0x294> 2002b8: b3 0a 44 01 add s5, s0, s4 2002bc: 83 ca 0a 00 lbu s5, 0x0(s5) 2002c0: 23 a0 55 11 sw s5, 0x100(a1) 2002c4: 0f 00 f0 0f fence 2002c8: 13 0a 1a 00 addi s4, s4, 0x1 2002cc: 83 aa 45 11 lw s5, 0x114(a1) 2002d0: 13 fb 0a 02 andi s6, s5, 0x20 2002d4: e3 0c 0b fe beqz s6, 0x2002cc <_main+0x2b4> 2002d8: 93 fa fa 0f andi s5, s5, 0xff 2002dc: e3 1a 9a fc bne s4, s1, 0x2002b0 <_main+0x298>

Attaching a copy of the asm obj-dump. reproduceable.txt

Meanwhile, trying possibilities on the linker. -end-

universal-git commented 2 months ago

The short strings write because their bytes are written directly from registers, not fetched from data stack. The problem is clearly accessing the data stack. Possible reasons:

  1. Wrong linker used or
    1. The interpreter not taking right address of the data location. -end-
ia0 commented 2 months ago

It looks to me that you don't copy data from flash to RAM at startup. Is there a reason to not use the riscv-rt crate? It would handle this for you and fix your linker script too (which I'm surprised works given you don't declare the flash memory region, so maybe it's a different problem).

universal-git commented 2 months ago

In another version, I do copy flash to RAM at startup. Tried riscv_rt initially, it didnot work for the board. Moreover, I was into building it from scratch, so made it myself. The program works because it is loaded in UART test mode. After much trials and tests, I realised that the problem is due to SPI flash, which does not load onto RAM with the usual method. Closing this thread as it is not related to the target.