Issue
I am writing an embedded C program to test a hardware IP that I have developed on an FPGA board. I was playing with the RISC-V GCC ASM syntax and found this weird issue.
This is the code that I writing in my C program. Both write_target
and base_addr
are declared as volatile uint32_t.
// write_target = 0x1F9
asm volatile (
"li %0, 0x1F9\n"
: "=r"(write_target)
);
// base_addr = 0x1040_6000
asm volatile (
"lui %0, %1"
: "=r" (base_addr)
: "i" (66566)
);
// base_addr = 0x1040_6000 + 0x0
asm volatile (
"addi %0, %1, %2\n"
: "=r"(base_addr)
: "r"(base_addr), "i"(0)
);
// mem[base_addr] = write_target
asm volatile (
"sh %0, 0(%1)\n"
: "=r"(write_target)
: "r"(base_addr)
);
When I look at the compiled dump, I see that it has been compiled to the following:
800031e6: 1f900793 li a5,505 // a5 = 0x1F9 = d'505
800031ea: f6f42423 sw a5,-152(s0) // mem[s0-152] = 505
800031ee: 104067b7 lui a5,0x10406 // a5 = 0x10406 = d'66566
800031f2: f6f42623 sw a5,-148(s0) // mem[s0-148] = 0x10406
800031f6: f6c42783 lw a5,-148(s0) // a5 = mem[s0-148]
800031fa: 00078793 mv a5,a5 // a5 = a5
800031fe: f6f42623 sw a5,-148(s0) // mem[s0-148] = 0x10406
80003202: f6c42783 lw a5,-148(s0) // a5 = mem[s0-148]
80003206: 00f79023 sh a5,0(a5) # 10406000 <_tbss_end+0x10406000>
// mem[a5] = a5
I cannot understand why this is the case. I am not very well versed with the inner workings of the RISC-V GCC cross-compiler, so I don't know what am I doing wrong. I cross-checked the syntax and to the best of my understanding it is correct. I am running with -O3 optimization flag.
I would really appreciate any guidance that you all may have.
Thanks!
Update: I am using the Newlib gcc compiler from the riscv-gnu-toolchain Git repo.
The problem I am having is that I want to store the value of write_target
into base_addr
. But the compiled code seems to store base_addr
into base_addr
.
Solution
The problem seems to be that you use an output constraint on this assembler directive:
// mem[base_addr] = write_target
asm volatile (
"sh %0, 0(%1)\n"
: "=r"(write_target)
: "r"(base_addr)
);
That's why GCC never passes write_target
to the statement.
To match your style, it would need two register input constraints instead, for write_target
and base_addr
.
It may be easier to use one asm
block without any input/output constraints, and clobbers for the hard registers you use in your assembler routine. You can embed multiple lines in one asm
statement if you embed \n
into the string. (Convention is to use \n\t
, to match the compiler-generated instruction indentation.)
Answered By - Florian Weimer Answer Checked By - Cary Denson (WPSolving Admin)