Combination of auipc and sign-extended 12 bit offsets

768 views
Skip to first unread message

Pohle, Erik

unread,
Jan 20, 2017, 4:27:31 AM1/20/17
to sw-...@groups.riscv.org
Hello RISC-V,

we are a student team and are currently developing an assembly simulator (similar to an IDE) which supports some of the basic RISC-V modules. It is planned to be used mainly for educational purposes, especially for one specific lecture for undergraduates.

We want to implement the RV32I RISC-V pseudo instructions, but on doing so we ran into a problem.
Considering the following example: we want to load a word located at memory address 0x1fff ffff into register x1.
According to the pseudo instruction table (cf. RISC-V User-Level Specification 2.1, p. 112, table 21.1) in the specification this can be done the following way:

auipc rd, symbol[31:12]
lw rd, symbol[11:0](rd)

which we would interpret as

auipc x1, 0x1ffff
lw x1, 0xfff(x1)

According to the user-level specification, "AUIPC forms a 32-bit offset from the 20-bit U-immediate, filling in the lowest 12 bits with zeros, adds this offset to the pc, then places the result in register rd." (RISC-V User-Level Specification 2.1, p. 14)

So after the execution of auipc (assuming that the pc was 0), x1 holds the value 0x1ffff000.
Now, when executing lw, "the effective byte address is obtained by adding register rs1 to the sign-extended 12-bit offset." (RISC-V User-Level Specification 2.1, p. 18) So when we sign-extend 0xfff, it transforms to 0xffff ffff. Then we add it to x1 and get 0x1fff efff which is not the address we wanted to access the memory at.

So the question is: what did we miss? How is the pseudo instruction lw (as an example) supposed to work? Similar problems occur with lui and addi.

Thank you for your effort in advance!

Sincerely,


Erik Pohle,

Technical University of Munich, in a project for computer science chair "Computer Technology and Computer Organization"

https://github.com/TUM-LRR/era-gp-sim



Stefan O'Rear

unread,
Jan 20, 2017, 4:32:13 AM1/20/17
to Pohle, Erik, sw-...@groups.riscv.org
On Fri, Jan 20, 2017 at 1:27 AM, Pohle, Erik <erik....@tum.de> wrote:
> So the question is: what did we miss? How is the pseudo instruction lw (as
> an example) supposed to work? Similar problems occur with lui and addi.

The "symbol[31:12]" is not to be taken 100% literally.

auipc x1, 0x20000
addi x1, x1, -1

( I think you can get there by adding 1 if bit 11 is 1 )

-s

Alex Bradbury

unread,
Jan 20, 2017, 4:38:45 AM1/20/17
to Stefan O'Rear, Pohle, Erik, sw-...@groups.riscv.org
On 20 January 2017 at 09:32, Stefan O'Rear <sor...@gmail.com> wrote:
> On Fri, Jan 20, 2017 at 1:27 AM, Pohle, Erik <erik....@tum.de> wrote:
>> So the question is: what did we miss? How is the pseudo instruction lw (as
>> an example) supposed to work? Similar problems occur with lui and addi.
>
> The "symbol[31:12]" is not to be taken 100% literally.
>
> auipc x1, 0x20000
> addi x1, x1, -1

Yes, perhaps it would be better if the translation used %hi(foo) and %lo(foo).

As Stefan, says, adding 1 to the high twenty bits if bit 11 is 1 is
sufficient. e.g. ((Val+0x800) >> 12) & 0xfffff.

For instance:
.equ addr, 0xdeadbeef
lui t0, %hi(addr)
lw ra, %lo(addr)(t0)

Should be assembled to:
lui t0, 912092
lw ra, -273(t0)

Best,

Alex

Bruce Hoult

unread,
Jan 20, 2017, 5:42:13 AM1/20/17
to Pohle, Erik, sw-...@groups.riscv.org
Two things:

- if you want an absolute address then you want lui not auipc

- as the offset in lw is signed, if the low bits look like a negative number then you need to add 4096 to what ends up in the temporary register i.e. add 1 to the high 20 bits.

--
You received this message because you are subscribed to the Google Groups "RISC-V SW Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sw-dev+unsubscribe@groups.riscv.org.
To post to this group, send email to sw-...@groups.riscv.org.
Visit this group at https://groups.google.com/a/groups.riscv.org/group/sw-dev/.
To view this discussion on the web visit https://groups.google.com/a/groups.riscv.org/d/msgid/sw-dev/1484904455132.46875%40tum.de.

Pohle, Erik

unread,
Jan 20, 2017, 8:58:30 AM1/20/17
to Bruce Hoult, sw-...@groups.riscv.org

Thank you guys for your quick responses!


We had indeed a similar "workaround" which also involved adding 1 to the lui part if necessary. We were wondering if there might be a simpler solution but now with your confirmation we'll implement it.


Thanks again for your help.


Sincerely,

Erik


Josh Bleecher Snyder

unread,
Jan 20, 2017, 10:58:56 AM1/20/17
to Alex Bradbury, Stefan O'Rear, Pohle, Erik, sw-...@groups.riscv.org
> As Stefan, says, adding 1 to the high twenty bits if bit 11 is 1 is
> sufficient. e.g. ((Val+0x800) >> 12) & 0xfffff.

This comes up a lot. I wonder whether it's worth a well-placed
sentence in the spec.

-josh

Bruce Hoult

unread,
Jan 20, 2017, 11:26:02 AM1/20/17
to Josh Bleecher Snyder, Alex Bradbury, Stefan O'Rear, Pohle, Erik, sw-...@groups.riscv.org
It *should* be obvious, but It would not hurt!

Maybe something like:

Because the offsets for load/store and JALR are signed 12 bit with a -2048 to +2047 range, the value calculated by a preceding LUI or AUIPC should be to the closest multiple of 4 KB, whether higher or lower than the target address.

e.g. for target address t

hi(t) = ((t+0x800) & ~0xfff) >> 12; // the & is redundant because of the shift
lo(t) = t & 0xfff;

For a PC-relative address t should be the difference between the actual target and the instruction following the load/store or branch instruction.

(I'm not 100% sure of the last sentence. The documentation is also not clear on exactly what value of the PC is used -- the current instruction or the next instruction)


--
You received this message because you are subscribed to the Google Groups "RISC-V SW Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sw-dev+unsubscribe@groups.riscv.org.
To post to this group, send email to sw-...@groups.riscv.org.
Visit this group at https://groups.google.com/a/groups.riscv.org/group/sw-dev/.

Samuel Falvo II

unread,
Jan 20, 2017, 11:46:39 AM1/20/17
to Pohle, Erik, sw-...@groups.riscv.org
On Fri, Jan 20, 2017 at 1:27 AM, Pohle, Erik <erik....@tum.de> wrote:
> Then we add it to x1 and get 0x1fff efff which is not the address we wanted
> to access the memory at.
>
> So the question is: what did we miss?

Nothing. That is performing exactly as intended by the specification.

The assumption is that LUI or AUIPC (depending on your needs) are
instructions emitted by a compiler with higher-level knowledge about
how the program is laid out in memory. Thus, if it knows that bit 11
of the immediate operand is set, it'll automatically add 1 (letting it
wrap if needed) to the operand provided to LUI or AUIPC to compensate
for the sign-extension.

--
Samuel A. Falvo II

Michael Clark

unread,
Jan 20, 2017, 8:16:08 PM1/20/17
to Bruce Hoult, Josh Bleecher Snyder, Alex Bradbury, Stefan O'Rear, Pohle, Erik, sw-...@groups.riscv.org
On 21 Jan 2017, at 5:25 AM, Bruce Hoult <br...@hoult.org> wrote:

It *should* be obvious, but It would not hurt!

Maybe something like:

Because the offsets for load/store and JALR are signed 12 bit with a -2048 to +2047 range, the value calculated by a preceding LUI or AUIPC should be to the closest multiple of 4 KB, whether higher or lower than the target address.

e.g. for target address t

hi(t) = ((t+0x800) & ~0xfff) >> 12; // the & is redundant because of the shift
lo(t) = t & 0xfff;

For a PC-relative address t should be the difference between the actual target and the instruction following the load/store or branch instruction.

(I'm not 100% sure of the last sentence. The documentation is also not clear on exactly what value of the PC is used — the current instruction or the next instruction)

PC relative offsets are calculated relative to the PC address of the AUIPC instruction.

int offset = target_addr - auipc_inst_addr;
int hi20 = ((offset + 0x800) >> 12) << 12;
int lo12 = offset - upper;

We could potentially include the calculations in documentation for the associated ELF relocations:

R_RISCV_PCREL_HI20 U-Type (auipc)
R_RISCV_PCREL_LO12_I I-Type (lb,lbu,lh,lhu,lw,lwu,flw,fld,addi,addiw)
R_RISCV_PCREL_LO12_S S-Type (sb,sh,sw,fsw,fsd)

I am using the literal immediate values so hi20 is in bits[31:20]. The hi20 value can have the left shift removed for gas (however this is usually hidden behind %functions).

I always found it odd that LUI and AUIPC have the literal value shifted right by 12 in objdump (and gas?). By keeping the literal value in the upper 20 bits of a 32-bit decoded immediate, then simple addition and subtraction of the values just works.

On Fri, Jan 20, 2017 at 6:58 PM, Josh Bleecher Snyder <josh...@gmail.com> wrote:
> As Stefan, says, adding 1 to the high twenty bits if bit 11 is 1 is
> sufficient. e.g. ((Val+0x800) >> 12) & 0xfffff.

This comes up a lot. I wonder whether it's worth a well-placed
sentence in the spec.

-josh

--
You received this message because you are subscribed to the Google Groups "RISC-V SW Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sw-dev+unsubscribe@groups.riscv.org.
To post to this group, send email to sw-...@groups.riscv.org.
Visit this group at https://groups.google.com/a/groups.riscv.org/group/sw-dev/.
To view this discussion on the web visit https://groups.google.com/a/groups.riscv.org/d/msgid/sw-dev/CAFAcib9vKVXn%3DXyNnZ8hWxcR0njY-y4Ha4u1%2BDgXGB-KGNQrHQ%40mail.gmail.com.


--
You received this message because you are subscribed to the Google Groups "RISC-V SW Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sw-dev+un...@groups.riscv.org.

To post to this group, send email to sw-...@groups.riscv.org.
Visit this group at https://groups.google.com/a/groups.riscv.org/group/sw-dev/.

ne...@netch.kiev.ua

unread,
Jan 24, 2017, 10:24:40 AM1/24/17
to Bruce Hoult, sw-...@groups.riscv.org
hi,

Fri, Jan 20, 2017 at 19:25:59, bruce wrote about "Re: [sw-dev] Combination of auipc and sign-extended 12 bit offsets":

> For a PC-relative address t should be the difference between the actual
> target and the instruction following the load/store or branch instruction.
>
> (I'm not 100% sure of the last sentence. The documentation is also not
> clear on exactly what value of the PC is used -- the current instruction or
> the next instruction)

The "current" one.
Yep, it's better to have this explicitly mentioned in spec, because
there are too many ISAs that use a next instruction address in
PC-based addressing.


-netch-

Valentin Nechayev

unread,
Jan 24, 2017, 10:24:40 AM1/24/17
to sw-...@groups.riscv.org
Fri, Jan 20, 2017 at 19:25:59, bruce wrote about "Re: [sw-dev] Combination of auipc and sign-extended 12 bit offsets":

> For a PC-relative address t should be the difference between the actual
> target and the instruction following the load/store or branch instruction.
>
> (I'm not 100% sure of the last sentence. The documentation is also not
> clear on exactly what value of the PC is used -- the current instruction or
> the next instruction)

The "current" one. (I've detected it analyzing objdump output, in hope
binutils are correct in this.)

Valentin Nechayev

unread,
Jan 24, 2017, 10:24:40 AM1/24/17
to Pohle, Erik, sw-...@groups.riscv.org
hi,

Fri, Jan 20, 2017 at 09:27:27, erik.pohle wrote about "[sw-dev] Combination of auipc and sign-extended 12 bit offsets":

> We want to implement the RV32I RISC-V pseudo instructions, but on doing so we ran into a problem.
> Considering the following example: we want to load a word located at memory address 0x1fff ffff into register x1.
> According to the pseudo instruction table (cf. RISC-V User-Level Specification 2.1, p. 112, table 21.1) in the specification this can be done the following way:
>
> auipc rd, symbol[31:12]
> lw rd, symbol[11:0](rd)
>
> which we would interpret as
>
> auipc x1, 0x1ffff
> lw x1, 0xfff(x1)

As soon as immediate offset will be _sign_ extended,
you should feed auipc with _ceil_, not _floor_, if full offset bit 11 is 1.
(Consider this as round() after dividing by 2^12, when 0.5 is rounded
up to highest integer value.)

In that case, the correct sequence is

auipc x1, 0x20000 ; fills with 0x2000_0000
lw x1, 0xfff(x1) ; 0x2000_0000 + 0xffff_ffff == 0x1fff_ffff (modulo 2^32)


-netch-

Valentin Nechayev

unread,
Jan 24, 2017, 10:24:40 AM1/24/17
to sw-...@groups.riscv.org
(sorry for possible dups - address mess to fix)

Fri, Jan 20, 2017 at 09:27:27, erik.pohle wrote about "[sw-dev] Combination of auipc and sign-extended 12 bit offsets":

> We want to implement the RV32I RISC-V pseudo instructions, but on doing so we ran into a problem.
> Considering the following example: we want to load a word located at memory address 0x1fff ffff into register x1.
> According to the pseudo instruction table (cf. RISC-V User-Level Specification 2.1, p. 112, table 21.1) in the specification this can be done the following way:
>
> auipc rd, symbol[31:12]
> lw rd, symbol[11:0](rd)
>
> which we would interpret as
>
> auipc x1, 0x1ffff
> lw x1, 0xfff(x1)

Reply all
Reply to author
Forward
0 new messages