ARM BKPT <imm8> equivalent on RISC-V

831 views
Skip to first unread message

Liviu Ionescu

unread,
Nov 28, 2017, 7:33:27 PM11/28/17
to RISC-V ISA Dev, Krste Asanovic, Andrew Waterman, Yunsup Lee, Megan Wachs, Drew Barbier
I'm starting a new thread, since the related discussion in the debug list apparently was not very well understood.

According to the current ISA specs, there is a single instruction, EBREAK, that breaks execution to a debugger.


Other architectures have multiple such instructions, for example the ARM BKPT has an 8-bit immediate value. Value #0 is the default breakpoint instruction, all other values are available for various specific needs, for example for semihosting, a technique supported by many debuggers (like J-Link, OpenOCD, QEMU, etc) that allows to forward some of the POSIX calls to the debugger, helping write unit tests.


The question is, in the absence of multiple EBREAKs, how to define a sequence of instructions that include the RISC-V unique EBREAK, but also allows to safely pass an additional value to the debugger?


Megan suggested to reserve some values for the `mscratch` register, and have the debugger test these values; assuming this CSR is used to store a pointer, small values non multiple of 4, like 1,2,3,5,6,7,... might be used for this purpose.

Currently only one additional EBREAK is needed, for semihosting, so, if I have to choose, I would take 7.


Any other suggestions?


Thank you,

Liviu



Andrew Waterman

unread,
Nov 28, 2017, 7:48:09 PM11/28/17
to Liviu Ionescu, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
I don’t support adding additional EBREAKs, as placing odd-valued magic numbers in mscratch seems sufficient. There is an additional DBI (debug binary interface) constraint on mscratch, that it must be initialized on boot and in normal operation only hold aligned pointers or 0. But that is vastly preferable to an ISA modification.

Bruce Hoult

unread,
Nov 28, 2017, 8:43:07 PM11/28/17
to Liviu Ionescu, RISC-V ISA Dev, Krste Asanovic, Andrew Waterman, Yunsup Lee, Megan Wachs, Drew Barbier
What is the reason for not using the same ECALL interface for semihosting as for running under an actual *nix OS?




--
You received this message because you are subscribed to the Google Groups "RISC-V ISA Dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to isa-dev+unsubscribe@groups.riscv.org.
To post to this group, send email to isa...@groups.riscv.org.
Visit this group at https://groups.google.com/a/groups.riscv.org/group/isa-dev/.
To view this discussion on the web visit https://groups.google.com/a/groups.riscv.org/d/msgid/isa-dev/5CEC97CF-A405-46F0-A873-20A646265C14%40livius.net.

Michael Clark

unread,
Nov 28, 2017, 11:41:31 PM11/28/17
to Bruce Hoult, Liviu Ionescu, RISC-V ISA Dev, Krste Asanovic, Andrew Waterman, Yunsup Lee, Megan Wachs, Drew Barbier


> On 29/11/2017, at 2:43 PM, Bruce Hoult <br...@hoult.org> wrote:
>
> What is the reason for not using the same ECALL interface for semihosting as for running under an actual *nix OS?

I don’t know. That was my suggestion. Get the debugger to hook mtvec and have the handler route via the debug transport or otherwise handle locally.

It’s an elegant solution because it allows one to maintain binary compatibility.

AFAICT the intent is redirecting POSIX calls so it’s not exactly a case of bare-metal e.g. like bbl, linux or any other low level code that doesn’t actually make POSIX calls.

> On Wed, Nov 29, 2017 at 3:33 AM, Liviu Ionescu <i...@livius.net> wrote:
> I'm starting a new thread, since the related discussion in the debug list apparently was not very well understood.
>
> According to the current ISA specs, there is a single instruction, EBREAK, that breaks execution to a debugger.
>
>
> Other architectures have multiple such instructions, for example the ARM BKPT has an 8-bit immediate value. Value #0 is the default breakpoint instruction, all other values are available for various specific needs, for example for semihosting, a technique supported by many debuggers (like J-Link, OpenOCD, QEMU, etc) that allows to forward some of the POSIX calls to the debugger, helping write unit tests.
>
>
> The question is, in the absence of multiple EBREAKs, how to define a sequence of instructions that include the RISC-V unique EBREAK, but also allows to safely pass an additional value to the debugger?
>
>
> Megan suggested to reserve some values for the `mscratch` register, and have the debugger test these values; assuming this CSR is used to store a pointer, small values non multiple of 4, like 1,2,3,5,6,7,... might be used for this purpose.
>
> Currently only one additional EBREAK is needed, for semihosting, so, if I have to choose, I would take 7.
>
>
> Any other suggestions?
>
>
> Thank you,
>
> Liviu
>
>
>
> --
> You received this message because you are subscribed to the Google Groups "RISC-V ISA Dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to isa-dev+u...@groups.riscv.org.
> --
> You received this message because you are subscribed to the Google Groups "RISC-V ISA Dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to isa-dev+u...@groups.riscv.org.
> To post to this group, send email to isa...@groups.riscv.org.
> Visit this group at https://groups.google.com/a/groups.riscv.org/group/isa-dev/.
> To view this discussion on the web visit https://groups.google.com/a/groups.riscv.org/d/msgid/isa-dev/CAMU%2BEkwnbB818ppuqeGLcOiMzJokkK9ot_R2MKZ6v29vJs%2BjcA%40mail.gmail.com.

Liviu Ionescu

unread,
Nov 29, 2017, 3:53:31 AM11/29/17
to Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee


> On 29 Nov 2017, at 02:47, Andrew Waterman <and...@sifive.com> wrote:
>
> I don’t support adding additional EBREAKs, as placing odd-valued magic numbers in mscratch seems sufficient. There is an additional DBI (debug binary interface) constraint on mscratch, that it must be initialized on boot and in normal operation only hold aligned pointers or 0. But that is vastly preferable to an ISA modification.

yes, this constraint of holding aligned pointers and the set of allowed magic numbers must be clearly documented, with the additional consequence that mscratch must be cleared at reset.

I foresee a small problem with using the mscratch for magic numbers, the need to place the code in an interrupt critical section, i.e. disable interrupts, swap mscratch, ebreak, restore mscratch, enable interrupts. probably no longer reasonable for an inline function.

otherwise we risk entering an interrupt while mscratch does not hold an aligned pointer.

things get pretty messy, only to compensate in software what could have been an EBREAK with at least a 2-4 bits immediate value field. :-(



regards,

Liviu

Andrew Waterman

unread,
Nov 29, 2017, 4:13:13 AM11/29/17
to Liviu Ionescu, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
On Wed, Nov 29, 2017 at 12:53 AM, Liviu Ionescu <i...@livius.net> wrote:
>
>
>> On 29 Nov 2017, at 02:47, Andrew Waterman <and...@sifive.com> wrote:
>>
>> I don’t support adding additional EBREAKs, as placing odd-valued magic numbers in mscratch seems sufficient. There is an additional DBI (debug binary interface) constraint on mscratch, that it must be initialized on boot and in normal operation only hold aligned pointers or 0. But that is vastly preferable to an ISA modification.
>
> yes, this constraint of holding aligned pointers and the set of allowed magic numbers must be clearly documented, with the additional consequence that mscratch must be cleared at reset.
>
> I foresee a small problem with using the mscratch for magic numbers, the need to place the code in an interrupt critical section, i.e. disable interrupts, swap mscratch, ebreak, restore mscratch, enable interrupts. probably no longer reasonable for an inline function.
>
> otherwise we risk entering an interrupt while mscratch does not hold an aligned pointer.

Right - this needs to be done with interrupts disabled.

>
> things get pretty messy, only to compensate in software what could have been an EBREAK with at least a 2-4 bits immediate value field. :-(

I can see your point, but changing the ISA is all the more messy.

>
>
>
> regards,
>
> Liviu
>

Liviu Ionescu

unread,
Nov 29, 2017, 4:27:21 AM11/29/17
to Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee


> On 29 Nov 2017, at 11:12, Andrew Waterman <and...@sifive.com> wrote:
>
> On Wed, Nov 29, 2017 at 12:53 AM, Liviu Ionescu <i...@livius.net> wrote:
>>
>>
>>> On 29 Nov 2017, at 02:47, Andrew Waterman <and...@sifive.com> wrote:
>>>
>>> I don’t support adding additional EBREAKs, as placing odd-valued magic numbers in mscratch seems sufficient. There is an additional DBI (debug binary interface) constraint on mscratch, that it must be initialized on boot and in normal operation only hold aligned pointers or 0. But that is vastly preferable to an ISA modification.
>>
>> yes, this constraint of holding aligned pointers and the set of allowed magic numbers must be clearly documented, with the additional consequence that mscratch must be cleared at reset.
>>
>> I foresee a small problem with using the mscratch for magic numbers, the need to place the code in an interrupt critical section, i.e. disable interrupts, swap mscratch, ebreak, restore mscratch, enable interrupts. probably no longer reasonable for an inline function.
>>
>> otherwise we risk entering an interrupt while mscratch does not hold an aligned pointer.
>
> Right - this needs to be done with interrupts disabled.

Ok.

Megan, can you suggest a new implementation for the `call_host()` function?

>
>>
>> things get pretty messy, only to compensate in software what could have been an EBREAK with at least a 2-4 bits immediate value field. :-(
>
> I can see your point, but changing the ISA is all the more messy.

I can see your point too, probably for this specific case we can live with an elaborated workaround, but, more generally, does it mean that if, in time, more issues with the current (tight) ISA will be discovered, there will never be new versions to fix them, and the required software workarounds to compensate for the problems will become more and more elaborate?


Regards,

Liviu


Alex Marshall

unread,
Nov 29, 2017, 6:56:18 PM11/29/17
to Liviu Ionescu, Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
> things get pretty messy, only to compensate in software what could have
> been an EBREAK with at least a 2-4 bits immediate value field. :-(
>
I believe there is actually a rather large number of unused bits in the EBREAK instruction encoding that could have been used to include an immediate field. Theoretically it should be possible to slightly hack up your assembler to allow you to fill those bits, if your RISC-V implementation supports ignoring them (that is, doesn't trap on filled 'reserved' bits).

Why this wasn't the original way EBREAK was designed, I can't say. I also may be incorrect about the reserved bits existing, maybe I've read the encodings incorrectly.

Thanks,
Alex


-----------------------------------------------------------------------------------
This email message is for the sole use of the intended recipient(s) and may contain
confidential information. Any unauthorized review, use, disclosure or distribution
is prohibited. If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
-----------------------------------------------------------------------------------

Jacob Bachmeyer

unread,
Nov 29, 2017, 7:15:44 PM11/29/17
to Liviu Ionescu, Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
Liviu Ionescu wrote:
>> On 29 Nov 2017, at 11:12, Andrew Waterman <and...@sifive.com> wrote:
>>
>> On Wed, Nov 29, 2017 at 12:53 AM, Liviu Ionescu <i...@livius.net> wrote:
>>
>>>> On 29 Nov 2017, at 02:47, Andrew Waterman <and...@sifive.com> wrote:
>>>>
>>>> I don’t support adding additional EBREAKs, as placing odd-valued magic numbers in mscratch seems sufficient. There is an additional DBI (debug binary interface) constraint on mscratch, that it must be initialized on boot and in normal operation only hold aligned pointers or 0. But that is vastly preferable to an ISA modification.
>>>>
>>> yes, this constraint of holding aligned pointers and the set of allowed magic numbers must be clearly documented, with the additional consequence that mscratch must be cleared at reset.
>>>
>>> I foresee a small problem with using the mscratch for magic numbers, the need to place the code in an interrupt critical section, i.e. disable interrupts, swap mscratch, ebreak, restore mscratch, enable interrupts. probably no longer reasonable for an inline function.
>>>
>>> otherwise we risk entering an interrupt while mscratch does not hold an aligned pointer.
>>>
>> Right - this needs to be done with interrupts disabled.
>>
>
> Ok.
>
> Megan, can you suggest a new implementation for the `call_host()` function?
>

Why not just use ECALL for 'call_host()'? In a semi-hosted system, the
"magic" link to the host is the environment, after all.


-- Jacob

Bruce Hoult

unread,
Nov 29, 2017, 7:15:45 PM11/29/17
to Alex Marshall, Liviu Ionescu, Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
ECALL and EBREAK are SYSTEM opcode (bits 0..6), with 000 in funct3. That's enough to uniquely identify them (at present) as nothing else uses that combination.

Then rs1 and rdst are specified as both 00000 and imm12 as 000000000000 for ECALL and 000000000001 for EBREAK.

Plenty of room to do something with a few more bits of imm12 while leaving rs1 and rdst nonzero for future expansion.

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

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

Liviu Ionescu

unread,
Nov 29, 2017, 7:31:09 PM11/29/17
to jcb6...@gmail.com, Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee


> On 30 Nov 2017, at 02:15, Jacob Bachmeyer <jcb6...@gmail.com> wrote:
>
>> Megan, can you suggest a new implementation for the `call_host()` function?
>>
>
> Why not just use ECALL for 'call_host()'? In a semi-hosted system, the "magic" link to the host is the environment, after all.

Jacob,

please take a HiFive1 board, write two small programs one with an EBREAK and one with an ECALL, and run them in a debugger.

when you hit EBREAK, the debugger will halt and you'll see that the PC is exactly at the EBREAK instruction. a semihosted debugger will examine the EBREAK and if some specific condition is met (like mscratch is 0x7), will process the request and continue execution after the EBREAK.

when you hit ECALL, the FE310-G000 device will most probably crash, or at best will remain in a loop in the trap handler, if you cared to install one. the debugger has absolutely no idea about this, you have to manually halt execution and examine the PC.


regards,

Liviu


Liviu Ionescu

unread,
Nov 29, 2017, 7:37:57 PM11/29/17
to Bruce Hoult, Alex Marshall, Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee


> On 30 Nov 2017, at 02:15, Bruce Hoult <br...@hoult.org> wrote:
>
> ECALL and EBREAK are SYSTEM opcode (bits 0..6), with 000 in funct3. That's enough to uniquely identify them (at present) as nothing else uses that combination.
>
> Then rs1 and rdst are specified as both 00000 and imm12 as 000000000000 for ECALL and 000000000001 for EBREAK.
>
> Plenty of room to do something with a few more bits of imm12 while leaving rs1 and rdst nonzero for future expansion.

sure, if there are any free bits in the current encoding, it would be great to reserve some for an extended EBREAK that has an immediate value (ARM Cortex-M uses 8 bits, but I guess 2-3 bits would probably be enough).


regards,

Liviu

Jacob Bachmeyer

unread,
Nov 29, 2017, 10:25:43 PM11/29/17
to Liviu Ionescu, Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
There is no way for the debugger to intercept ECALL? That sounds like
an oversight in the HiFive chip, not an architectural shortcoming.


-- Jacob

Jacob Bachmeyer

unread,
Nov 29, 2017, 10:32:20 PM11/29/17
to Liviu Ionescu, Bruce Hoult, Alex Marshall, Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
We can easily get 10 bits using the rs1 and rd fields, or XLEN bits by
using rs1 to refer to an actual register holding a function code. The
immediate field in SYSTEM/PRIV is listed as "funct12", so it seems to be
intended as a 12-bit function code.

Using rs1 to select a register holding a parameter is a smaller change
to the ISA than giving magic semantics to mscratch. Further, EBREAK is
available in all modes, while mscratch is accessible only in M-mode.


-- Jacob

Liviu Ionescu

unread,
Nov 30, 2017, 2:18:44 AM11/30/17
to Jacob Bachmeyer, Bruce Hoult, Alex Marshall, Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee


> On 30 Nov 2017, at 05:32, Jacob Bachmeyer <jcb6...@gmail.com> wrote:
>
>> sure, if there are any free bits in the current encoding, it would be great to reserve some for an extended EBREAK that has an immediate value (ARM Cortex-M uses 8 bits, but I guess 2-3 bits would probably be enough).
>>
>
> We can easily get 10 bits using the rs1 and rd fields, or XLEN bits by using rs1 to refer to an actual register holding a function code. The immediate field in SYSTEM/PRIV is listed as "funct12", so it seems to be intended as a 12-bit function code.

yes, for the future, if Andrew agrees, I welcome such a solution (the mscratch magic is not a solution, it is a workaround), but for now, as long as the specs do not mention it, I doubt it is a safe choice.

> Using rs1 to select a register holding a parameter is a smaller change to the ISA than giving magic semantics to mscratch. Further, EBREAK is available in all modes, while mscratch is accessible only in M-mode.

maybe I'm short sighted, but why would I use semihosting in any other mode but M?


Liviu

Andrew Waterman

unread,
Nov 30, 2017, 2:45:26 AM11/30/17
to Liviu Ionescu, Alex Marshall, Bruce Hoult, Drew Barbier, Jacob Bachmeyer, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
On Wed, Nov 29, 2017 at 11:18 PM Liviu Ionescu <i...@livius.net> wrote:


> On 30 Nov 2017, at 05:32, Jacob Bachmeyer <jcb6...@gmail.com> wrote:
>
>> sure, if there are any free bits in the current encoding, it would be great to reserve some for an extended EBREAK that has an immediate value (ARM Cortex-M uses 8 bits, but I guess 2-3 bits would probably be enough).
>>
>
> We can easily get 10 bits using the rs1 and rd fields, or XLEN bits by using rs1 to refer to an actual register holding a function code.  The immediate field in SYSTEM/PRIV is listed as "funct12", so it seems to be intended as a 12-bit function code.

yes, for the future, if Andrew agrees, I welcome such a solution (the mscratch magic is not a solution, it is a workaround), but for now, as long as the specs do not mention it, I doubt it is a safe choice.

It’s not my decision; it’s just against my advisement.

Liviu Ionescu

unread,
Nov 30, 2017, 3:31:13 AM11/30/17
to Andrew Waterman, Alex Marshall, Bruce Hoult, Drew Barbier, Jacob Bachmeyer, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee


> On 30 Nov 2017, at 09:45, Andrew Waterman <and...@sifive.com> wrote:
>
>
> yes, for the future, if Andrew agrees, I welcome such a solution (the mscratch magic is not a solution, it is a workaround), but for now, as long as the specs do not mention it, I doubt it is a safe choice.
>
> It’s not my decision; it’s just against my advisement.

as Jacob noticed,

> mscratch is accessible only in M-mode

so, for other use cases (more elaborate than my M-mode semihosting), the mscratch workaround might not be available.

perhaps a short study on how other architectures use multiple BRK instructions would be useful. Cortex-M reserves an 8-bit value, but personally I used only the semihosting one (but I used it heavily). x86 also has an 8-bit value for INT, but they use it for slightly different purposes.

if anyone else has more experience with BRK use on other architectures, please share.


regards,

Liviu

Cesar Eduardo Barros

unread,
Nov 30, 2017, 6:05:17 AM11/30/17
to jcb6...@gmail.com, Liviu Ionescu, Bruce Hoult, Alex Marshall, Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
Em 30-11-2017 01:32, Jacob Bachmeyer escreveu:
> Liviu Ionescu wrote:
>>> On 30 Nov 2017, at 02:15, Bruce Hoult <br...@hoult.org> wrote:
>>>
>>> ECALL and EBREAK are SYSTEM opcode (bits 0..6), with 000 in funct3.
>>> That's enough to uniquely identify them (at present) as nothing else
>>> uses that combination.
>>>
>>> Then rs1 and rdst are specified as both 00000 and imm12 as
>>> 000000000000 for ECALL and 000000000001 for EBREAK.
>>>
>>> Plenty of room to do something with a few more bits of imm12 while
>>> leaving rs1 and rdst nonzero for future expansion.
>>
>> sure, if there are any free bits in the current encoding, it would be
>> great to reserve some for an extended EBREAK that has an immediate
>> value (ARM Cortex-M uses 8 bits, but I guess 2-3 bits would probably
>> be enough).
>
> We can easily get 10 bits using the rs1 and rd fields, or XLEN bits by
> using rs1 to refer to an actual register holding a function code.  The
> immediate field in SYSTEM/PRIV is listed as "funct12", so it seems to be
> intended as a 12-bit function code.

One of the coolest properties of the design of the base RISC-V ISA is
that the major opcode (bits 0-6) is enough to determine the instruction
format, and that it is enough to determine which registers should be
read (rs1, rs2, rs3) and written (rd), without decoding anything else.
Let's not lose that property. Immediate mode CSR instructions might do a
useless read of a register on a naive implementation, since they reuse
rs1 as an immediate, but that's not a problem; reusing rd for an
immediate, on the other hand, would require special-casing to suppress
the register write.

The SYSTEM opcode is I-type, and both ECALL and EBREAK have rd=x0; let's
keep it that way, unless the instruction is actually writing to a register.

>
> Using rs1 to select a register holding a parameter is a smaller change
> to the ISA than giving magic semantics to mscratch.  Further,  EBREAK is
> available in all modes, while mscratch is accessible only in M-mode.

Since SYSTEM is I-type, and a naive implementation might already be
reading from rs1, I agree that it's a small change to the ISA. Using rs1
as an immediate is also a small change to the ISA, given that there's
precedent with the CSR immediate instructions, and a naive
implementation can simply discard the eagerly read rs1 register.

You could even do both, using separate funct12 values for "EBREAK
reading from rs1" and "EBREAK treating rs1 as an immediate".

--
Cesar Eduardo Barros
ces...@cesarb.eti.br

Liviu Ionescu

unread,
Nov 30, 2017, 6:47:02 AM11/30/17
to Cesar Eduardo Barros, Jacob Bachmeyer, Bruce Hoult, Alex Marshall, Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee


> On 30 Nov 2017, at 13:04, Cesar Eduardo Barros <ces...@cesarb.eti.br> wrote:
>
> One of the coolest properties of the design of the base RISC-V ISA is that the major opcode (bits 0-6) is enough to determine the instruction format, and that it is enough to determine which registers should be read (rs1, rs2, rs3) and written (rd), without decoding anything else. Let's not lose that property.

agree.

> You could even do both, using separate funct12 values for "EBREAK reading from rs1" and "EBREAK treating rs1 as an immediate".

great!

this means we have at least two options to choose from.

from my point of view, a short immediate would be enough. however, without being an ISA guru, using register fields for immediate values doesn't look very nice.

---

if I understand the encoding right, from the imm[11:0] field, only two values are used, 000000000000 for ECALL and 000000000001 for EBREAK.

perhaps we can use 00000000xxx1 for EBREAK, similar to what Jacob suggested, and extend the syntax of EBREAK to EBREAK #N.

if so, I would reserve EBREAK #7 for semihosting.

I guess this is a minor change to ISA, which does not break any compatibility, and would not consume too much of the encoding space. and I guess it'll not make any problems to compress, there are 5+1 bits for the imm field, more than enough.

and, not relying on mscratch, the extended EBREAKs are available in all modes, not only to M.


regards,

Liviu

Bruce Hoult

unread,
Nov 30, 2017, 7:46:56 AM11/30/17
to Liviu Ionescu, Andrew Waterman, Alex Marshall, Drew Barbier, Jacob Bachmeyer, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
Most architectures, including x86 and Thumb1, that have a literal argument to INT/SVC/BRK/whatever seem to top out at 8 bits.

But ARM has 24 bits! Aarch64 has individual instructions (SVC, MHV, SMC) to make system calls to interrupt levels 1/2/3 or to make calls to debugger at interrupt levels 1/2/3 (DCPS1, DCPS2, DCPS3) as well as BRK (self hosted) and HLT. All have a 16 bit immediate.

If you look at...

https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h

.. standard Linux system call numbers currently go up to 292 plus some deprecated or nonstandard ones starting at 1024.

Almost every Linux architecture passes the syscall number in a register.

IBM s390(x) can pass system calls less than 256 in an immediate in the svc instruction, but if the number if greater than 256 them it falls back to using svc 0 with the syscall number in register r1.

ARM OABI uses an immediate in the SVC instruction which as noted has a 24 bit immediate in ARM32 mode but only 8 bit in Thumb1. Thumb2 doesn't add any larger SVC immediate and there is no register fallback. So OABI can't be used with modern Linux.

ARM EABI uses SVC 0 and passes the system call number in r7.

i386 uses INT 0 with the syscall number in eax.
x86_64 uses the syscall instruction with the syscall number in rax


I think it's nice to be able to use a single instruction with the syscall number in an immediate, but you need to make it big enough! Which means at least 9 bits today for standard Linux syscalls, or 11 bits for deprecated ones. Maybe 12 bits would be future-proof.

Copying the s390 fallback to a register would be a good idea. In that case an 8 bit immediate might be enough as the syscalls above 255 look like they're probably rarely used.

Being able to put the syscall number in a register as well as in the immediate would also vastly simplify code to dynamically call different system calls. For example wrappers for logging or whatever. If you can't use a register then you're faced with either a huge switch or else runtime code generation. Ugly. 


Liviu

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

kr...@berkeley.edu

unread,
Nov 30, 2017, 9:05:32 AM11/30/17
to Bruce Hoult, Liviu Ionescu, Andrew Waterman, Alex Marshall, Drew Barbier, Jacob Bachmeyer, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee

Thanks Bruce,

I think you've just argued very convincingly for passing the syscall
number in a register.

Krste
| email to isa-dev+u...@groups.riscv.org.
| To post to this group, send email to isa...@groups.riscv.org.
| Visit this group at https://groups.google.com/a/groups.riscv.org/group/isa-
| dev/.
| To view this discussion on the web visit https://groups.google.com/a/
| groups.riscv.org/d/msgid/isa-dev/183F2952-832B-405A-BF45-
| 7D03570DD6D2%40livius.net.


| --
| You received this message because you are subscribed to the Google Groups
| "RISC-V ISA Dev" group.
| To unsubscribe from this group and stop receiving emails from it, send an email
| to isa-dev+u...@groups.riscv.org.
| To post to this group, send email to isa...@groups.riscv.org.
| Visit this group at https://groups.google.com/a/groups.riscv.org/group/isa-dev/
| .
| To view this discussion on the web visit https://groups.google.com/a/
| groups.riscv.org/d/msgid/isa-dev/
| CAMU%2BEkxbQ44BVHCW7w4_t-MKeRR5L%2Bha3Xo01X8EKC2ee2NFDg%40mail.gmail.com.

Liviu Ionescu

unread,
Nov 30, 2017, 9:43:42 AM11/30/17
to Bruce Hoult, Andrew Waterman, Alex Marshall, Drew Barbier, Jacob Bachmeyer, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee


> On 30 Nov 2017, at 14:46, Bruce Hoult <br...@hoult.org> wrote:
>
> Most architectures, including x86 and Thumb1, that have a literal argument to INT/SVC/BRK/whatever seem to top out at 8 bits. ... But ARM has 24 bits! ... I think it's nice to be able to use a single instruction with the syscall number in an immediate, but you need to make it big enough! ...

thank you, Bruce.

I was also used to see system calls with literal arguments, and I was surprised that the RISC-V ISA took the minimalistic approach and define a single ECALL and a single EBREAK, delegating everything to software (as if transistors were expensive and software cheap).

but, at least for ECALL, it is possible to pass the syscall number in a register, while for EBREAK it is not.

ECALLs with literal arguments might be useful to support multiple ABIs, or multiple ABI versions.

even for small embedded devices it is common to use different SVCs, for various internal RTOS functions (see ARM RTX, for example).


but not ECALLs are my main concern, I'd be happy to have a few more EBREAKs, to no longer need the mscratch workaround...


regards,

Liviu






Michael Chapman

unread,
Nov 30, 2017, 9:54:32 AM11/30/17
to isa...@groups.riscv.org
EBREAK is just a request from the software for the debugger to intervene.

If you sometimes want to pass parameters, then create a specific
function for that, place all the parameters in registers and put a lable
on the EBREAK instruction.  If you want to emulate system calls in the
debugger, then all you need is for the SYSCALL handler for ECALL to call
that function.

The debugger can examine the label on the EBREAK in the ELF executable
being debugged to determine the service requested be it a SW breakpoint
(no special label), SYSCALL emulation (special label such as
$dbg_syscall) or some other service. Indeed, if you really want 256
different kinds of EBREAKs, then just use 256 different special labels!

There is no point in encoding stuff in instructions in HW on the target
device which does not need to be there.

Megan Wachs

unread,
Nov 30, 2017, 10:02:08 AM11/30/17
to Michael Chapman, RISC-V ISA Dev
Right, now that I know that debuggers like OpenOCD can get this information from GDB, I'd prefer the ELF-labeling solution to changing the ISA or relying on values in mscratch (apologies to those who are on both threads, I think we have the same discussion going on in two places).

Megan

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



--
Megan A. Wachs
Engineer | SiFive, Inc 
1875 South Grant Street
Suite 600
San Mateo, CA 94402

Liviu Ionescu

unread,
Nov 30, 2017, 10:51:10 AM11/30/17
to Bruce Hoult, Krste Asanovic, Andrew Waterman, Alex Marshall, Drew Barbier, Jacob Bachmeyer, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee


> On 30 Nov 2017, at 17:30, Bruce Hoult <br...@hoult.org> wrote:
>
> Passing in a register is definitely the simplest and most future-proof. ... So, it's pretty clear for syscalls.

how do you suggest to handle multiple ABIs?

with multiple ECALLs it is easy, you assign one for each ABI, and which may have completely different syscall numbers, registers, etc, and all work in parallel.

> But what about debugger traps? If you're going to insert breakpoints by replacing user instructions (rather than using hardware breakpoint address registers) then you'd like to replace as few bytes of them and as few instructions as possible.

if you really need to replace multiple user instructions with long magic sequences simply because the ISA does not support multiple BRKs, probably you selected the wrong ISA.

> Ideally you'd want a 16 bit debugger trap instruction with a few bits of literal, so you could replace a single RVC instruction. Or if the instruction is a bigger one, replace it with a 16 bit debugger trap instruction and one or more 16 bit NOPs.

that's correct.

> PLUS .. where do you find a register to put the constant for the debugger? ... Hence the suggestion to use odd-valued magic numbers in mscratch for this communication. But that also needs several instructions .. and looks like m mode only.

yes. also ugly and limited.

for the existing embedded RISC-V devices, which are M-mode only, that would not be a problem, but future devices might also take the ARM path, which is promoting dual mode embedded devices, with lots of security improvements (see large Cortex-M devices, especially M8), and in this case the mscratch workaround cannot be used. :-(


regards,

Liviu

Andrew Waterman

unread,
Nov 30, 2017, 12:22:14 PM11/30/17
to Liviu Ionescu, Alex Marshall, Bruce Hoult, Drew Barbier, Jacob Bachmeyer, Krste Asanovic, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
On Thu, Nov 30, 2017 at 7:51 AM Liviu Ionescu <i...@livius.net> wrote:


> On 30 Nov 2017, at 17:30, Bruce Hoult <br...@hoult.org> wrote:
>
> Passing in a register is definitely the simplest and most future-proof. ... So, it's pretty clear for syscalls.

how do you suggest to handle multiple ABIs?

with multiple ECALLs it is easy, you assign one for each ABI, and which may have completely different syscall numbers, registers, etc, and all work in parallel.

> But what about debugger traps? If you're going to insert breakpoints by replacing user instructions (rather than using hardware breakpoint address registers) then you'd like to replace as few bytes of them and as few instructions as possible.

if you really need to replace multiple user instructions with long magic sequences simply because the ISA does not support multiple BRKs, probably you selected the wrong ISA.

> Ideally you'd want a 16 bit debugger trap instruction with a few bits of literal, so you could replace a single RVC instruction. Or if the instruction is a bigger one, replace it with a 16 bit debugger trap instruction and one or more 16 bit NOPs.

that's correct.

> PLUS .. where do you find a register to put the constant for the debugger? ... Hence the suggestion to use odd-valued magic numbers in mscratch for this communication. But that also needs several instructions .. and looks like m mode only.

yes. also ugly and limited.

A fixed-size immediate is limiting. What we’ve done is to introduce a level of indirection to avoid this limitation, which at the same time avoids changing the ISA for a pretty dumb reason.



for the existing embedded RISC-V devices, which are M-mode only, that would not be a problem, but future devices might also take the ARM path, which is promoting dual mode embedded devices, with lots of security improvements (see large Cortex-M devices, especially M8), and in this case the mscratch workaround cannot be used. :-(

There will always be some register available to indicate the purpose of the request. The combination of the opcode, active ABI, and originating privilege mode will—by design—suffice to indicate where to look.




regards,

Liviu

Liviu Ionescu

unread,
Nov 30, 2017, 12:39:11 PM11/30/17
to Andrew Waterman, Alex Marshall, Bruce Hoult, Drew Barbier, Jacob Bachmeyer, Krste Asanovic, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee


> On 30 Nov 2017, at 19:21, Andrew Waterman <and...@sifive.com> wrote:
>
> ... mscratch, M-mode only ... There will always be some register available to indicate the purpose of the request. The combination of the opcode, active ABI, and originating privilege mode will—by design—suffice to indicate where to look.

with the mscratch ruled out for being M-mode only, what other options do we have?

is there any other register, available in all modes, that is guaranteed to **not** have a certain range of values, so we can use it to temporarily store the magic?

otherwise the single EBRAK alone is not enough, it can be placed by the debugger as a breakpoint at any location.


regards,

Liviu

Andrew Waterman

unread,
Nov 30, 2017, 12:44:37 PM11/30/17
to Liviu Ionescu, Alex Marshall, Bruce Hoult, Drew Barbier, Jacob Bachmeyer, Krste Asanovic, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
I was talking about ECALL more than EBREAK.

U-mode doesn’t (shouldn’t) need to know about semihosting. There are
existing ABIs for U-mode to request services of greater privilege modes.




regards,

Liviu

Liviu Ionescu

unread,
Nov 30, 2017, 1:35:47 PM11/30/17
to Andrew Waterman, Alex Marshall, Bruce Hoult, Drew Barbier, Jacob Bachmeyer, Krste Asanovic, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee


> On 30 Nov 2017, at 19:44, Andrew Waterman <and...@sifive.com> wrote:
>
>
> U-mode doesn’t (shouldn’t) need to know about semihosting. There are
> existing ABIs for U-mode to request services of greater privilege modes.

Not necessarily.

I don't know the details of the U-mode in the context of embedded RISC-V devices, but in the Cortex-M world the so-called 'user mode' has nothing to do with a kernel, ABIs, or things like this, it only restricts access to some registers, and as such, breaking to the debugger shouldn't be a problem.

Actually, when asked about semihosting, most ARM users will reply that it is a method to display printf() messages. Yes, it is, but not only; semihosting has two separate calls to output bytes/strings on a 'debug channel', which indeed can be used for trace::printf() messages during regular debug sessions; in addition, it also has a larger set of calls for fully semihosted applications.

I see no reason why semihosting calls not be available in both machine and user mode, how else could I write unit tests that run in user mode?

Please note that during semihosting calls, the application is simply halted, regardless of its mode, since the core executes a regular BRK.


Regards,

Liviu





Michael Chapman

unread,
Nov 30, 2017, 1:43:30 PM11/30/17
to isa...@groups.riscv.org

Cores which support compressed mode already have two EBREAK
instructions. The debugger will used the compressed one for SW
breakpoint. The other one can be used for semihosting and any other
interaction required with the debugger.

For cores without compressed mode, OpenOCD will know whether it placed a
EBREAK there or not. If it placed it there then it is a SW breakpoint.
Otherwise it is a semihosting call to the debugger. Also the EBREAK
could be followed by a word which is an illegal instruction pattern
which is extremely unlike to occur in any application - i.e. would not
ever be the next instruction after a SW breakpoint to make this distinction.


On 30-Nov-17 18:39, Liviu Ionescu wrote:
> ...

Tommy Thorn

unread,
Nov 30, 2017, 2:47:27 PM11/30/17
to Liviu Ionescu, Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
Just catching up to an old discussion, but it seems to me that there's another
option that is simpler: put the magic number is the instruction word following
the EBREAK, optionally we should use an ADDI x0, x0, MAGIC if we want
to keep it sane.

The code/debugger that handles the EBREAK obviously have the address of
the EBREAK instruction and can inspect the code stream.

Tommy


On Wed, Nov 29, 2017 at 12:53 AM, Liviu Ionescu <i...@livius.net> wrote:


> On 29 Nov 2017, at 02:47, Andrew Waterman <and...@sifive.com> wrote:
>
> I don’t support adding additional EBREAKs, as placing odd-valued magic numbers in mscratch seems sufficient. There is an additional DBI (debug binary interface) constraint on mscratch, that it must be initialized on boot and in normal operation only hold aligned pointers or 0. But that is vastly preferable to an ISA modification.

yes, this constraint of holding aligned pointers and the set of allowed magic numbers must be clearly documented, with the additional consequence that mscratch must be cleared at reset.

I foresee a small problem with using the mscratch for magic numbers, the need to place the code in an interrupt critical section, i.e. disable interrupts, swap mscratch, ebreak, restore mscratch, enable interrupts. probably no longer reasonable for an inline function.

otherwise we risk entering an interrupt while mscratch does not hold an aligned pointer.

things get pretty messy, only to compensate in software what could have been an EBREAK with at least a 2-4 bits immediate value field. :-(



regards,

Liviu

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

Liviu Ionescu

unread,
Nov 30, 2017, 2:54:16 PM11/30/17
to Tommy Thorn, Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee


> On 30 Nov 2017, at 21:47, Tommy Thorn <tommy...@esperantotech.com> wrote:
>
> ... put the magic number is the instruction word following
> the EBREAK, optionally we should use an ADDI x0, x0, MAGIC if we want
> to keep it sane.

if `ADDI x0, x0, MAGIC` is a legal instruction, it can appear in a program, and if the debugger places a breakpoint to the instruction just before it, it'll look like a semihosting call.

we need a solution that, in a legal program, **guarantees** that it only matches semihosting calls, and never matches breakpoints placed by the debugger.


regards,

Liviu




Michael Chapman

unread,
Nov 30, 2017, 3:17:14 PM11/30/17
to isa...@groups.riscv.org

Breakpoints are placed by the debugger/openOCD. They know both where
they are. Therefore an EBREAK at some other location is a semihosting call.

Michael Chapman

unread,
Nov 30, 2017, 3:31:30 PM11/30/17
to Liviu Ionescu, isa...@groups.riscv.org

You should use a semihosting call (to print out the PC and a message)
for that!


On 30-Nov-17 21:28, Liviu Ionescu wrote:
>
>> On 30 Nov 2017, at 22:20, Michael Chapman <michael.c...@gmail.com> wrote:
>>
>>
>> Breakpoints are placed by the debugger/openOCD. They know both where
>> they are. Therefore an EBREAK at some other location is a semihosting call.
> Not necessarily, for Debug build configurations I manually add BREAK instructions instead (actually before) infinite loops (like in asserts, unused trap handlers, or should-not-reach-this places); it is much more convenient to have the debugger halt and immediately show you the location than entering an infinite loop, waiting for a while, start wondering what happened and finally manually halt execution.
>
> Regards,
>
> Liviu
>
>
>
> ---
> This email has been checked for viruses by AVG.
> http://www.avg.com
>

Jacob Bachmeyer

unread,
Nov 30, 2017, 11:45:29 PM11/30/17
to Liviu Ionescu, Bruce Hoult, Alex Marshall, Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
Liviu Ionescu wrote:
>> On 30 Nov 2017, at 05:32, Jacob Bachmeyer <jcb6...@gmail.com> wrote:
>>
>>
>>> sure, if there are any free bits in the current encoding, it would be great to reserve some for an extended EBREAK that has an immediate value (ARM Cortex-M uses 8 bits, but I guess 2-3 bits would probably be enough).
>>>
>>>
>> We can easily get 10 bits using the rs1 and rd fields, or XLEN bits by using rs1 to refer to an actual register holding a function code. The immediate field in SYSTEM/PRIV is listed as "funct12", so it seems to be intended as a 12-bit function code.
>>
>
> yes, for the future, if Andrew agrees, I welcome such a solution (the mscratch magic is not a solution, it is a workaround), but for now, as long as the specs do not mention it, I doubt it is a safe choice.
>

To clarify, the 12-bit "funct12" code is part of the opcode -- one of
those functions is EBREAK. That field is *not* available for EBREAK
<imm>. Using the rs1 field in EBREAK to provide "EBREAK <reg>" and a
semihost call number in that register is probably a better option.

>> Using rs1 to select a register holding a parameter is a smaller change to the ISA than giving magic semantics to mscratch. Further, EBREAK is available in all modes, while mscratch is accessible only in M-mode.
>>
>
> maybe I'm short sighted, but why would I use semihosting in any other mode but M?
>

Exactly the reason I do not like the mscratch workaround -- EBREAK is
available in all modes, but you are suggesting a use specific to
M-mode. Interestingly, ECALL is also available in all modes and is
expressly intended for performing calls to an environment. Why not use
ECALL for your semihosting platform? (If nothing else, your M-mode trap
handler can recognize an M-mode ECALL and execute an EBREAK at a
specific, known address. The semihosting interface recognizes an EBREAK
at *that* address as a semihost call, handles it, and resumes execution,
while passing other EBREAKs to the user's debugger. This is really a
workaround for insufficient debugging support -- the debugger should be
able to intercept an M-mode ECALL directly.)



-- Jacob

Jacob Bachmeyer

unread,
Nov 30, 2017, 11:54:03 PM11/30/17
to Bruce Hoult, Krste Asanovic, Liviu Ionescu, Andrew Waterman, Alex Marshall, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
Bruce Hoult wrote:
> But what about debugger traps? If you're going to insert breakpoints
> by replacing user instructions (rather than using hardware breakpoint
> address registers) then you'd like to replace as few bytes of them and
> as few instructions as possible. And you still (as Liviu argues) may
> want at least a few bits of argument/literal to be passed to the debugger.

The debugger also has a trick not available for syscalls -- the debugger
gets the address of the EBREAK that was executed and the debugger knows
*why* it put an EBREAK at *that* address.

> Ideally you'd want a 16 bit debugger trap instruction with a few bits
> of literal, so you could replace a single RVC instruction. Or if the
> instruction is a bigger one, replace it with a 16 bit debugger trap
> instruction and one or more 16 bit NOPs.

RVC currently has C.EBREAK.

> Having to put in 8 bytes of replacement instructions to call the
> debugger is ugly. One 4 byte instruction to load a code into a
> register, and another to actually trap. That's potentially up to four
> user instructions being lifted out to be emulated or whatever.
>
> PLUS .. where do you find a register to put the constant for the
> debugger? With syscall at least the compiler knows about it and you
> can register-allocate around it. But with a debugger breakpoint you
> need to work with arbitrary code, which may well be already using the
> register you want to pass the constant in.
>
> Hence the suggestion to use odd-valued magic numbers in mscratch for
> this communication. But that also needs several instructions .. and
> looks like m mode only.

This is why I do not like using EBREAK for this: EBREAK is supposed to
be a debugging breakpoint, while ECALL is supposed to be an environment
call. All of these problems are stemming from trying to abuse EBREAK
for ECALL's purpose.



-- Jacob

Jacob Bachmeyer

unread,
Dec 1, 2017, 12:02:02 AM12/1/17
to Liviu Ionescu, Cesar Eduardo Barros, Bruce Hoult, Alex Marshall, Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
Liviu Ionescu wrote:
> if I understand the encoding right, from the imm[11:0] field, only two
> values are used, 000000000000 for ECALL and 000000000001 for EBREAK.

This is true for the user ISA; the privileged ISA defines xRET and WFI
in this space as well, using a few more values. (Not actually a problem
for EBREAK <smallimm>, but why not just read rs1, or use the rs1 field
itself as a small immediate?)


-- Jacob

Liviu Ionescu

unread,
Dec 1, 2017, 2:12:53 AM12/1/17
to jcb6...@gmail.com, Bruce Hoult, Krste Asanovic, Andrew Waterman, Alex Marshall, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee


> On 1 Dec 2017, at 06:53, Jacob Bachmeyer <jcb6...@gmail.com> wrote:
>
> This is why I do not like using EBREAK for this: EBREAK is supposed to be a debugging breakpoint, while ECALL is supposed to be an environment call. All of these problems are stemming from trying to abuse EBREAK for ECALL's purpose.

nope. this is exactly how semihosting was designed to work, by intentionally triggering breakpoint events.

> Exactly the reason I do not like the mscratch workaround -- EBREAK is available in all modes, but you are suggesting a use specific to M-mode.

that was a mistake, sorry, at a second thought I reconsidered, the mscratch workaround is not usable, some semihosting calls, like SYS_WRITEC and SYS_WRITE0, should be available in all modes, as an independent `trace::printf()` output stream.

then I realised that in the future there may be embedded devices with both U and M modes, exactly like current Cortex-M devices, and, I'll need a method to write tests for applications running on them, so it became obvious that semihosting must be available for these dual-mode configurations too.

so the mscratch proposal was dismissed.

the current proposal is to use a sequence including guaranteed illegal instructions, like 'uncompressed EBREAK, uncompressed all zero, uncompressed all one`, that should be safe enough for the 'debugger' to identify semihosting calls.

> This is true for the user ISA; the privileged ISA defines xRET and WFI in this space as well, using a few more values. (Not actually a problem for EBREAK <smallimm>, but why not just read rs1, or use the rs1 field itself as a small immediate?)

`EBREAK <reg>` is also ok, if available. and there is no need to actually pass any value in the register, the call can be identified by the register number alone. (for example `EBREAK x7` == 'semihosting').


regards,

Liviu




Bruce Hoult

unread,
Dec 1, 2017, 2:45:50 AM12/1/17
to Liviu Ionescu, Jacob Bachmeyer, Krste Asanovic, Andrew Waterman, Alex Marshall, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
This seems silly. If you're going to put illegal instructions in the program, why not just put an illegal instruction that's almost exactly like an EBREAK except with some of the fields that are supposed to be zero non-zero? RS1 if you want. Or other bits in the imm12.

That will trap with a cause of "illegal instruction", you can examine it, find it's an enhanced EBREAK, and do what you want with it.

You may need to revise that in the future if more instructions are defined using those encodings, but on any given CPU you (and the debugger) know what instructions you have.

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

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

Liviu Ionescu

unread,
Dec 1, 2017, 3:31:37 AM12/1/17
to Bruce Hoult, Jacob Bachmeyer, Krste Asanovic, Andrew Waterman, Alex Marshall, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee


> On 1 Dec 2017, at 09:45, Bruce Hoult <br...@hoult.org> wrote:
>
> ... why not just put an illegal instruction that's almost exactly like an EBREAK except with some of the fields that are supposed to be zero non-zero? RS1 if you want. Or other bits in the imm12.
> That will trap with a cause of "illegal instruction", you can examine it, find it's an enhanced EBREAK, and do what you want with it.

'do what you want with it'? I don't want/need to do anything, I don't want to trap any exception, and I don't want the program running on the device to be required to do anything, all I need is a standard method to break to the debugger, and on RISC-V the one and only such method is an EBREAK.

things are very simple, I don't know how to better explain them to you. :-(

placing illegal instructions after EBREAK is probably not the most elegant solution, but so far was the safest method. (mscratch is M-mode only, getting the runtime address of a symbol requires a lot of effort in all semihosting enabled debuggers).

one disadvantage of placing illegal instructions after EBREAK is that when running such code on a non-semihosting enabled debugger, if the debugger is instructed to retrun normally after EBREAK, the core will crash with illegal instructions, but generally trying to run semihosting applications in a non-semihosting enabled debugger makes little sense.

---

I think it becomes more and more obvious that, in order to avoid such ugly hacks, we need a proper ISA solution.


taking a look at the Table 6.1 in the privileged ISA manual, to be in line with the other instructions, it seems we can have 5 bits of immediate value, similar to the rs2 field in the SFENCE instruction.

there are many free encodings left, the next one would be:

0001010 xxxxx 00000 000 00000 1110011

or we can use the current EBREAK encoding, but with a non zero value for rs1:

0000000 00001 rrrrr 000 00000 1110011

Andrew, any thoughts on this?


regards,

Liviu

Michael Clark

unread,
Dec 1, 2017, 4:42:20 AM12/1/17
to Liviu Ionescu, Bruce Hoult, Krste Asanovic, Andrew Waterman, Alex Marshall, Drew Barbier, Jacob Bachmeyer, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee

> On 1/12/2017, at 4:51 AM, Liviu Ionescu <i...@livius.net> wrote:
>
> how do you suggest to handle multiple ABIs?

The current wisdom is that the success of an ISA is inversely proportional to the number of ABIs; as one progressively becomes weighed down by supporting all of the ABI variants in compilers and associated libraries e.g dealing with all of the brokenness and incompatibility that multiple ABI variants create.

Successful ISAs have had a reducing number of ABIs as their success has increased (armv8, amd64). Failed ISAs have on the other hand have had an increasing number of ABIs (MIPS, an old ISA is still making ABI breaks, most recently their NaN encoding).

If one has a 10 trillion yen market cap one, then one might have the resources to support several ABIs however its likely that arm is now only supporting a smaller number of ABIs compared to the early days, a small amount of armv6, with mostly armv7 and armv8 little endian. In fact the number of supported ABIs has reduced in recent times. Hardfloat is standard on armv8 (2011).

arm on servers is exclusively armv8.x-a (2011), little endian, hard float.

Apple for example has stopped providing OS updates for armv7 devices. iOS 11 only runs on armv8. In armv8, all of the main armv7 extensions are mandatory (Thumb-2, NEON, VFPv4).

Arm embedded development today is most likely to be at least armv7. There might still be some soft float with no NEON or VFP in highly embedded applications.

x86-64 has 2 major ABIs, Windows PE/COFF and System V on Linux/*BSD. The BSDs share an ABI with Linux (calling conventions) but have different syscalls and syscall numbers. The BSDs are of course a little less popular and more fragmented when it comes to ABIs when compared to Linux variants which are all binary compatible (in large part due to the LSB ISO standardisation effort which covers symbols and symbol versions in all of the core libraries).

Power has moved to a 64-Bit ELF V2 ABI which is little endian. The V1 ABI and big endian is now legacy. SPARC as we know is disappearing as of this year.

Apple uses the upper bits in the syscall number register on both x86 and arm to control dispatch to either BSD or Mach kernels. Only the lower 12 bits are used for the syscall number. Same ABI (calling convention), different syscall numbers. The Darwin x86-64 syscall ABI is very similar to System V with the only difference being that condition codes are used to indicate error returns vs negative return values in %rax. It is essentially System V syscall ABI with the addition of condition codes.

I like the idea of an mtvec ECALL handler that calls EBREAK... ILLEGAL. It however can be added later to re-introduce binary compact. For U500 series this would allow semi-hosting for existing Linux binaries. I don’t think it would be too expensive in comparison to the cycle count of the syscall itself. With this approach, someone could send you a binary without source and you could use it in a semi-hosting setup.

I’d suggest being parsimonious when considering adding additional ABI variants :-D

Cesar Eduardo Barros

unread,
Dec 1, 2017, 4:48:20 AM12/1/17
to Liviu Ionescu, Bruce Hoult, Jacob Bachmeyer, Krste Asanovic, Andrew Waterman, Alex Marshall, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
Em 01-12-2017 06:31, Liviu Ionescu escreveu:
>
>
>> On 1 Dec 2017, at 09:45, Bruce Hoult <br...@hoult.org> wrote:
>>
>> ... why not just put an illegal instruction that's almost exactly like an EBREAK except with some of the fields that are supposed to be zero non-zero? RS1 if you want. Or other bits in the imm12.
>> That will trap with a cause of "illegal instruction", you can examine it, find it's an enhanced EBREAK, and do what you want with it.
>
> placing illegal instructions after EBREAK is probably not the most elegant solution, but so far was the safest method. (mscratch is M-mode only, getting the runtime address of a symbol requires a lot of effort in all semihosting enabled debuggers).

What Bruce is proposing is not putting an invalid instruction after
EBREAK, but replacing the EBREAK itself with an invalid instruction. The
debugger can trap invalid instructions as well as it can trap breakpoints.

The encoding of the invalid instruction could even be the suggested
"EBREAK with rs1 set".

Michael Clark

unread,
Dec 1, 2017, 5:42:29 AM12/1/17
to jcb6...@gmail.com, Liviu Ionescu, Bruce Hoult, Alex Marshall, Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
I like this idea but i’m not sure if the current version of the debug spec can cause debug traps on ECALL like it can with EBREAK. It would be a nice addition, while not an ISA change, it might be a Debug spec change which is also problematic for existing silicon.

It can otherwise be added with a small amount of complexity in OpenOCD by trap patching mtvec, assuming the debugger has some RAM for the trap patch code containing EBREAK, e.g. so it can continue to forward other traps to an existing trap handler (if present). The trap interceptor could also be put in boot section of the XIP flash on the HiFive1 where there is plenty of room.

Trap patching was a common way to intercept system calls on the M68K with MacOS <= 9

Liviu Ionescu

unread,
Dec 1, 2017, 10:39:30 AM12/1/17
to jcb6...@gmail.com, Bruce Hoult, Alex Marshall, Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee


> On 1 Dec 2017, at 06:45, Jacob Bachmeyer <jcb6...@gmail.com> wrote:
>
> ... execute an EBREAK at a specific, known address. The semihosting interface recognizes an EBREAK at *that* address as a semihost call, handles it, and resumes execution,

I'm very curious to learn how the semihosting interface knows what this address is. (substitute 'semihosting interface' by OpenOCD, QEMU, J-Link GDB Server).


Liviu

Michael Clark

unread,
Dec 1, 2017, 1:31:09 PM12/1/17
to Bruce Hoult, Liviu Ionescu, Jacob Bachmeyer, Krste Asanovic, Andrew Waterman, Alex Marshall, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
There is a difference between the canonical illegal instructions or some other marker sequence after the ebreak vs as yet unused opcode space. Using unused opcode space is essentially a de facto change in the ISA.

I think ebreak with sentinel instructions might be the way to go, and potentially we can put an ecall wrapper in XIP non volatile memory at some point in the future. ABI compat is not as big of an issue on the FE310 as it is on the U54MC. One could perhaps sidestep calling this a syscall ABI, rather it is a semi-hosting call ABI. If one wants to deploy binaries conforming to a well known ABI then one can install a trap handling stub to dispatch to a host, however we can add this later.
To unsubscribe from this group and stop receiving emails from it, send an email to isa-dev+u...@groups.riscv.org.

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

Bruce Hoult

unread,
Dec 1, 2017, 1:55:12 PM12/1/17
to Krste Asanovic, Liviu Ionescu, Andrew Waterman, Alex Marshall, Drew Barbier, Jacob Bachmeyer, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
Yes, but see the at the end of the post for debugger calls.

Passing in a register is definitely the simplest and most future-proof.

It's important that the register is not one normally used for argument passing, so that C stubs wrapping system calls can just stuff the system call number into that register without shuflfing the arguments. Using the last argument passing register can be ok if system calls don't have many arguments (I seem to recall they max out at 4?)

I don't much like every call site needing an extra instruction to load the syscall number into the register. But .. in most toolchains there is only one instance of each syscall and user code calls a wrapper rather than it being inlined.

On the receiving end in the kernel a register is definitely simpler. If the system call number is encoded in a literal in the ECALL instruction then either:

1) hardware needs to extract the syscall number into a CSR, or

2) hardware needs to save the faulting opcode into a CSR and then kernel code needs to mask out the syscall number, or

3) kernel code needs to get the address of the faulting instruction from a CSR (NOT the return address! That would be OK if instructions are fixed length, but not with C extension or future longer instructions). And then the kernel code needs to load the instruction from user space and mask out the syscall number.

These are all very ugly, needing more instructions than if the caller simply put the syscall number into a register, and possibly also requiring extra hardware and CSRs.

So, it's pretty clear for syscalls.


But what about debugger traps? If you're going to insert breakpoints by replacing user instructions (rather than using hardware breakpoint address registers) then you'd like to replace as few bytes of them and as few instructions as possible. And you still (as Liviu argues) may want at least a few bits of argument/literal to be passed to the debugger.

Ideally you'd want a 16 bit debugger trap instruction with a few bits of literal, so you could replace a single RVC instruction. Or if the instruction is a bigger one, replace it with a 16 bit debugger trap instruction and one or more 16 bit NOPs.

Having to put in 8 bytes of replacement instructions to call the debugger is ugly. One 4 byte instruction to load a code into a register, and another to actually trap. That's potentially up to four user instructions being lifted out to be emulated or whatever.

PLUS .. where do you find a register to put the constant for the debugger? With syscall at least the compiler knows about it and you can register-allocate around it. But with a debugger breakpoint you need to work with arbitrary code, which may well be already using the register you want to pass the constant in.

Hence the suggestion to use odd-valued magic numbers in mscratch for this communication. But that also needs several instructions .. and looks like m mode only.

|     To post to this group, send email to isa...@groups.riscv.org.
|     Visit this group at https://groups.google.com/a/groups.riscv.org/group/isa-
|     dev/.
|     To view this discussion on the web visit https://groups.google.com/a/
|     groups.riscv.org/d/msgid/isa-dev/183F2952-832B-405A-BF45-
|     7D03570DD6D2%40livius.net.


| --
| You received this message because you are subscribed to the Google Groups
| "RISC-V ISA Dev" group.
| To unsubscribe from this group and stop receiving emails from it, send an email

| To post to this group, send email to isa...@groups.riscv.org.
| Visit this group at https://groups.google.com/a/groups.riscv.org/group/isa-dev/
| .
| To view this discussion on the web visit https://groups.google.com/a/
| groups.riscv.org/d/msgid/isa-dev/
| CAMU%2BEkxbQ44BVHCW7w4_t-MKeRR5L%2Bha3Xo01X8EKC2ee2NFDg%40mail.gmail.com.

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

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

Liviu Ionescu

unread,
Dec 1, 2017, 1:55:12 PM12/1/17
to Michael Chapman, isa...@groups.riscv.org


> On 30 Nov 2017, at 22:20, Michael Chapman <michael.c...@gmail.com> wrote:
>
>
> Breakpoints are placed by the debugger/openOCD. They know both where
> they are. Therefore an EBREAK at some other location is a semihosting call.

Liviu Ionescu

unread,
Dec 1, 2017, 1:55:12 PM12/1/17
to Michael Chapman, isa...@groups.riscv.org


> On 30 Nov 2017, at 22:35, Michael Chapman <michael.c...@gmail.com> wrote:
>
>
> You should use a semihosting call (to print out the PC and a message)
> for that!

:-)

I do, but support for `trace::printf()` is optional, i.e. I may have a `trace::printf()` in my code, but the user may decide not to implement the method `trace::write()`, and the default weak `trace::write()` does nothing, so no messages are shown.

and even if the message is shown, it is still convenient to have the debugger halt.

so my code usually looks like:

```c
void do_something()
{
should_not_return();

trace::printf("should_not_return() actually returned\n");

#if defined(DEBUG)
os::arch::brk();
#endif
while(true) {
os::arch::wfi();
}
}
```

and, BTW, on Cortex-M devices, semihosting is not the only trace channel, in my code I provide implementations for `trace::write()` to forward the trace stream to:

- semihosting
- ARM ITM (the recommended ARM solution; when using common J-Link probes, speeds up to 6 Mbps are common)
- SEGGER RTT, a proprietary SEGGER solution, significantly much faster (tens of Mbps)

currently, on the available RISC-V test boards, the only available trace channel is an FTDI UART, at the fantastic speed of 115200 bps. not exactly encouraging...


regards,

Liviu

Jacob Bachmeyer

unread,
Dec 1, 2017, 9:34:14 PM12/1/17
to Liviu Ionescu, Bruce Hoult, Alex Marshall, Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee
Configuration? Reading it from ELF headers or symbol table? I am
assuming that tethered semihosting involves *loading* the program from
the host, so the loader can simply tell the semihosting debugger that
the semihost EBREAK is at address X. (There is not much reason to store
the program in NVM if it cannot run without a host interface connected.)

Another option that others have mentioned is to use EBREAK followed by
32-bit zero followed by a semihost call packet (format determined by
semihosting tools in use) in the instruction stream. 32-bit zero is
guaranteed to be an invalid instruction in RISC-V, while all ones is
actually an infinitely long instruction, so 32 ones are only guaranteed
invalid on implementations that do not have instructions longer than 32
bits.


-- Jacob

Liviu Ionescu

unread,
Dec 2, 2017, 4:19:33 AM12/2/17
to jcb6...@gmail.com, Bruce Hoult, Alex Marshall, Andrew Waterman, Drew Barbier, Krste Asanovic, Megan Wachs, RISC-V ISA Dev, Yunsup Lee


> On 2 Dec 2017, at 04:34, Jacob Bachmeyer <jcb6...@gmail.com> wrote:
>
> Reading it from ELF headers or symbol table?

ah, ok. please let me know when existing tools (especially commercial ones, like J-Link) will implement this.

> EBREAK followed by 32-bit zero followed by a semihost call packet

yes, this is the final workaround we decided to use, to compensate for the lack of multiple EBREAK instructions.


regards,

Liviu

Rishiyur Nikhil

unread,
Dec 2, 2017, 1:30:29 PM12/2/17
to Liviu Ionescu, RISC-V ISA Dev
>  Jacob said:
>    This is why I do not like using EBREAK for this: EBREAK is
>    supposed to be a debugging breakpoint, while ECALL is supposed to
>    be an environment call.  All of these problems are stemming from
>    trying to abuse EBREAK for ECALL's purpose.

>  Liviu said:
>    nope. this is exactly how semihosting was designed to work, by
>    intentionally triggering breakpoint events.

Liviu, I don't follow this.  I agree with Jacob's comment that the
functionality certainly sounds like special kind of environment call,
so why this certainty that it must be a break?  Note: I'm a newbie to
the concept of semi-hosting, so perhaps I'm missing some historical
development?

Nikhil


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

Andrew Waterman

unread,
Dec 2, 2017, 1:53:08 PM12/2/17
to Rishiyur Nikhil, Liviu Ionescu, RISC-V ISA Dev
The main reason is that semihosting calls are usually serviced by the debugger, and EBREAK is an existing mechanism that signals the debugger. By contrast, ECALL does not signal the debugger; it just generates an exception locally.

To unsubscribe from this group and stop receiving emails from it, send an email to isa-dev+u...@groups.riscv.org.

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

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

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

Liviu Ionescu

unread,
Dec 2, 2017, 4:36:44 PM12/2/17
to Andrew Waterman, Rishiyur Nikhil, RISC-V ISA Dev


> On 2 Dec 2017, at 20:52, Andrew Waterman <and...@sifive.com> wrote:
>
> The main reason is that semihosting calls are usually serviced by the debugger, and EBREAK is an existing mechanism that signals the debugger. By contrast, ECALL does not signal the debugger; it just generates an exception locally.

well said.

perhaps this will help all understand how semihosting works, and why it mandates an EBREAK, since my explanations were constantly disputed.

thank you Andrew.

Liviu

Jacob Bachmeyer

unread,
Dec 2, 2017, 4:40:21 PM12/2/17
to Andrew Waterman, Rishiyur Nikhil, Liviu Ionescu, RISC-V ISA Dev
Andrew Waterman wrote:
> The main reason is that semihosting calls are usually serviced by the
> debugger, and EBREAK is an existing mechanism that signals the
> debugger. By contrast, ECALL does not signal the debugger; it just
> generates an exception locally.

But in full environments (think "workstation") EBREAK must also generate
an exception locally, since the debugger is another process and
debugging is actually mediated through the supervisor. (Or through a
hypervisor or even monitor when debugging a supervisor.)


-- Jacob

Liviu Ionescu

unread,
Dec 2, 2017, 6:09:47 PM12/2/17
to jcb6...@gmail.com, Andrew Waterman, Rishiyur Nikhil, RISC-V ISA Dev
The typical configuration for using semihosting is:

- a host, usually a general purpose computer
- a semihosting enabled tool, usually part of a debugging environment
- a JTAG probe
- a physical target board, usually a bare-metal embedded device, but not necessarily

The common flow is:

- the host starts a process which loads an application in target's memory, via the JTAG connection
- the host instructs the target to start
- the target starts from RESET and during the startup sequence, via the semihosting call SYS_GET_CMDLINE gets a string with the command line
- the target startup parses this string and calls the target main(argc, argv)
- the target uses this info and runs, usually a unit test
- the target application returns either success or failure
- this return code is passed to the host
- the host semihosting process returns this exit code to the calling process


Optionally the target application may:

- read a file from the host, to get test data input
- write a file to the host, to leave a detailed test results
- write to the host STDOUT or STDERR
- get current time
- a few more other things, sometimes needed in an unit test


A fictional call would look like

```bash
run_semihosting --image e31arty-test.elf --device e31arty -- arg0 --iterations 10 --output test.xml
code=$?
if [ $code -eq 0 ]
then
echo "passed"
else
echo "failed"
fi
```

The options after the `--` are passed to the target as command line.

A functionally equivalent configuration is with an emulator running exactly the same target application.

Here is an actual code from one of my projects that run a sequence of tests, and exits if one test fails:

```
set -euo pipefail
# ...
list=( \
"test-cmsis-rtos-valid-release" \
"test-rtos-release" \
"test-mutex-stress-release" \
"test-cmsis-rtos-valid-debug" \
"test-rtos-debug" \
)

for f in "${list[@]}"
do
echo
echo run $f
# run executable
qemu-system-gnuarmeclipse \
--verbose --board STM32F4-Discovery --image $f/$f.elf --nographic \
-d unimp,guest_errors --semihosting-config enable=on,target=native --semihosting-cmdline test
done
```

(The full script is available from https://github.com/micro-os-plus/eclipse-test-projects/blob/master/f4discovery-tests-micro-os-plus/scripts/run-qemu.sh; the full script that runs the tests on Travis is https://github.com/micro-os-plus/eclipse-test-projects/blob/master/f4discovery-tests-micro-os-plus/scripts/travis.sh)


A simplified usage of semihosting is to forward only trace message (like `trace::printf()` in µOS++) to the host console during a regular debugging session. This behaviour is implemented by most debugging tools that act like a GDB server (J-Link GDB Server, OpenOCD, QEMU, etc).


---

As Andrew also mentioned, semihosting is based on a BREAK instruction signalling to the debugger, and this is a design choice, not an accident.

On architectures that support multiple BREAK instructions, BREAK #0 is used by the debugger to place breakpoints in RAM, and a separate BREAK #N is used for semihosting. The debugger halts on all BREAK instructions encountered, but if it identifies the semihosting BREAK, it performs the semihosting call and continues execution. While the debugger performs the semihosting call, the target is halted, as for any BREAK.

Since RISC-V has a single EBREAK, the simple method of identifying the BREAK by an immediate value encoded in the instruction is not possible.

After several iterations, we identified a workaround, to follow the single EBREAK by an all-zero illegal instruction and a second word with an immediate value to identify the EBREAK as a semihosting call (or another protocol).

It is not perfect, but it looks safe enough to avoid false positives.

Hopefully future ISA versions will include more EBREAK instructions, and this workaround will no longer be necessary.


Please note that nowhere in this story we were talking about supervisors, hypervisors, kernels, virtual memory, unix processes, portable binaries, etc; the ELF images mentioned are not portable applications but bare-metal applications, specific to a given physical board (in RISC-V case this usually means M-mode applications, but this does not exclude dual mode, U+M-mode applications).

This is my experience with semihosting on Cortex-M devices, and this is how I plan to use it for RISC-V devices too, both for `trace::printf()` messages and for fully semihosted unit tests running locally on physical boards connected via JTAG and/or on emulated boards, via QEMU.


Of course, your mileage may vary, so if others have better ideas, I'm open to suggestion.

But before making proposals, please read this message carefully, since I'll probably silently ignore all messages not related to the $subject and to its use with semihosting for bare-metal embedded devices.


Regards,

Liviu

Jacob Bachmeyer

unread,
Dec 2, 2017, 7:01:36 PM12/2/17
to Liviu Ionescu, Andrew Waterman, Rishiyur Nikhil, RISC-V ISA Dev
Liviu Ionescu wrote:
>> On 2 Dec 2017, at 23:40, Jacob Bachmeyer <jcb6...@gmail.com> wrote:
>>
>> Andrew Waterman wrote:
>>
>>> The main reason is that semihosting calls are usually serviced by the debugger, and EBREAK is an existing mechanism that signals the debugger. By contrast, ECALL does not signal the debugger; it just generates an exception locally.
>>>
>> But in full environments (think "workstation") EBREAK must also generate an exception locally, since the debugger is another process and debugging is actually mediated through the supervisor. (Or through a hypervisor or even monitor when debugging a supervisor.)
>>
>
> The typical configuration for using semihosting is:
>
> - a host, usually a general purpose computer
> - a semihosting enabled tool, usually part of a debugging environment
> - a JTAG probe
> - a physical target board, usually a bare-metal embedded device, but not necessarily
>
> The common flow is:
>
> - the host starts a process which loads an application in target's memory, via the JTAG connection
> - the host instructs the target to start
> - the target starts from RESET and during the startup sequence, via the semihosting call SYS_GET_CMDLINE gets a string with the command line
> - the target startup parses this string and calls the target main(argc, argv)
> - the target uses this info and runs, usually a unit test
> - the target application returns either success or failure
> - this return code is passed to the host
> - the host semihosting process returns this exit code to the calling process
>
> [...]
>
> As Andrew also mentioned, semihosting is based on a BREAK instruction signalling to the debugger, and this is a design choice, not an accident.
>
> On architectures that support multiple BREAK instructions, BREAK #0 is used by the debugger to place breakpoints in RAM, and a separate BREAK #N is used for semihosting. The debugger halts on all BREAK instructions encountered, but if it identifies the semihosting BREAK, it performs the semihosting call and continues execution. While the debugger performs the semihosting call, the target is halted, as for any BREAK.
>
> Since RISC-V has a single EBREAK, the simple method of identifying the BREAK by an immediate value encoded in the instruction is not possible.
>

The simple method of identifying semi-host calls by the address of the
EBREAK is possible, though, and does not require changing the ISA.
"Because ARM does it this way" is a very poor argument -- RISC-V is not ARM.

> After several iterations, we identified a workaround, to follow the single EBREAK by an all-zero illegal instruction and a second word with an immediate value to identify the EBREAK as a semihosting call (or another protocol).
>
> It is not perfect, but it looks safe enough to avoid false positives.
>
> Hopefully future ISA versions will include more EBREAK instructions, and this workaround will no longer be necessary.
>

I still argue that one EBREAK is sufficient in the ISA. The debugging
and semi-host interface software should be able to tell the difference
by the address of the instruction. The debugger most definitely *knows*
where it has placed breakpoints.

> Please note that nowhere in this story we were talking about supervisors, hypervisors, kernels, virtual memory, unix processes, portable binaries, etc; the ELF images mentioned are not portable applications but bare-metal applications, specific to a given physical board (in RISC-V case this usually means M-mode applications, but this does not exclude dual mode, U+M-mode applications).
>

ELF is still ELF even when statically-linked and "preloaded" to fit the
target's memory map and can carry metadata such as a list of EBREAK
addresses that are intended as semi-host calls, possibly including
*which* call is to be performed when a given EBREAK is executed. The
host has sufficient resources to handle reading a list of ?epc values
that indicate a semi-host call, and looking up ?epc when a breakpoint is
hit.

> This is my experience with semihosting on Cortex-M devices, and this is how I plan to use it for RISC-V devices too, both for `trace::printf()` messages and for fully semihosted unit tests running locally on physical boards connected via JTAG and/or on emulated boards, via QEMU.
>
>
> Of course, your mileage may vary, so if others have better ideas, I'm open to suggestion.
>
> But before making proposals, please read this message carefully, since I'll probably silently ignore all messages not related to the $subject and to its use with semihosting for bare-metal embedded devices.

The RISC-V ISA supports many environments, some of which are bare-metal,
some of which are varying degrees of virtualized, some of which are
semi-hosted, most of which are free-standing. Perhaps a new SHCALL
instruction should be added? Or SHCALL #N as an assembler shorthand for
"EBREAK/[literal 32-bit zero]/[literal 32-bit N]" if we do not want to
standardize metadata for semi-hosting?



-- Jacob

Christopher Celio

unread,
Dec 2, 2017, 7:13:38 PM12/2/17
to jcb6...@gmail.com, Liviu Ionescu, Andrew Waterman, Rishiyur Nikhil, RISC-V ISA Dev
 if it identifies the semihosting BREAK, it performs the semihosting call and continues execution.

Pardon my complete ignorance on this topic, but if you're semihosting a printf, how does the debugger know what to print out? Having an EBREAK with an immediate code for "printf" doesn't seem sufficient. 

I presume it needs to read target memory at some pre-negotiated address? Can that not store your extra EBREAK info?

-Chris


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

Liviu Ionescu

unread,
Dec 2, 2017, 7:54:57 PM12/2/17
to Christopher Celio, jcb6...@gmail.com, Andrew Waterman, Rishiyur Nikhil, RISC-V ISA Dev


> On 3 Dec 2017, at 02:13, Christopher Celio <ce...@eecs.berkeley.edu> wrote:
>
>>> if it identifies the semihosting BREAK, it performs the semihosting call and continues execution.
>
> ... if you're semihosting a printf, how does the debugger know what to print out? Having an EBREAK with an immediate code for "printf" doesn't seem sufficient.

if a semihosting EBREAK is identified, then it behaves like a call to a function like

```c
int
call_host(int reason, void* data)
```

in other words, it expects two input values, one integer with a call number and a pointer to something, specific to each call, and returns a status code.

implementing trace::printf() requires some processing, usually you need to process it first into a local buffer, then pass the buffer to the host.

for debug messages, the semihosting calls allow to print either one character or a zero terminated string.

on ARM, the reason is passed in RO, the pointer in R1.

from the ARM manual http://infocenter.arm.com/help/topic/com.arm.doc.dui0471g/DUI0471G_developing_for_arm_processors.pdf

---

8.28 SYS_WRITEC (0x03)

Writes a character byte, pointed to by R1, to the debug channel. When executed under an ARM debugger, the character appears on the host debugger console.

8.28.1 Entry

On entry, R1 contains a pointer to the character.

8.28.2 Return

None. Register R0 is corrupted.


8.29 SYS_WRITE0 (0x04)

Writes a null-terminated string to the debug channel. When executed under an ARM debugger, the characters appear on the host debugger console.

8.29.1 Entry

On entry, R1 contains a pointer to the first byte of the string.

8.29.2 Return

None. Register R0 is corrupted.

---


For an example how the full semihosting calls can be integrated in an application, please take a look at my implementation in µOS++:

https://github.com/micro-os-plus/micro-os-plus-iii/blob/xpack/src/semihosting/c-syscalls-semihosting.cpp

> I presume it needs to read target memory at some pre-negotiated address?

yes, it generally needs to read, sometimes write, the target memory.

> Can that not store your extra EBREAK info?

not really.

the difficulty is how to differentiate a debugger breakpoint, which can be placed either by the debugger or explicitly by the user, anywhere in the application memory space, from a semihosting break, which can also be placed anywhere.

for ARM, there are no limitations on how many times the `BRK` instruction can be used in an application, so the usual `call_host()` implementation is a `static inline` that includes the `BRK #N` assembly instruction. in a file like the `c-syscalls-semihosting.cpp` above, the `BRK #N` instruction is included in lots of places. also the files that implement `trace::write()`, the function used by `trace::printf()`, also includes `call_host()` several times.

of course other architectures may decide to limit the number of occurrences for some of the `BRK` calls, their location, or place other constraints, but I would not recommend this.

this concludes the semihosting class...


regards,

Liviu

Bruce Hoult

unread,
Dec 2, 2017, 9:06:45 PM12/2/17
to Liviu Ionescu, Andrew Waterman, RISC-V ISA Dev, Rishiyur Nikhil, jcb6...@gmail.com
No doubt I’m a very stupid person, but it seems to me that:

1) such a debugger (with or without a semihosting facility) can not be completely CPU-type independent and will contain specific knowledge of RISC-V instructions and conventions.

2) such a debugger will also install handlers for illegal instruction and bus errors (etc), not only BRK.

I therefore fail to see why some of the CPU-specific code can not examine the instruction causing an illegal instruction trap and determine that it is almost an EBREAK, but with non-zero RS1.


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

Liviu Ionescu

unread,
Dec 2, 2017, 9:31:55 PM12/2/17
to Bruce Hoult, Andrew Waterman, RISC-V ISA Dev, Rishiyur Nikhil, jcb6...@gmail.com


> On 3 Dec 2017, at 04:06, Bruce Hoult <br...@hoult.org> wrote:
>
> 2) such a debugger will also install handlers for illegal instruction and bus errors (etc), not only BRK.

not the debuggers I know in the Cortex-M world, and I have my doubts it can be done.

even in debug mode, all exceptions, including illegal instruction and bus errors, are handled by the device itself, if the application installed the appropriate handlers.

the only exception is BRK.

if the core is in debug mode, so it is connected via a JTAG or SWD probe to a debugger, encountering a BRK will signal the debugger, exactly as reaching a hardware breakpoint does.

if the core is not in debug mode, i.e. it comes out of reset and no debug probe is attached to the core, then encountering a BRK will trigger a local debug exception, as all other exceptions.

this presents an interesting opportunity, that I took advantage off in the projects generated by my GNU MCU Eclipse templates.

one common mistake when using semihosting to display the `trace::printf()` messages is trying to run the same application standalone, without the JTAG connected, or the debugger active.

if no special precautions are taken, this obviously triggers a debug exception, and the board hangs.

thus, my projects install a debug exception handler, that inspects the cause of the break, and simply ignores the semihosting calls, returning to the application. of course the trace messages are not displayed, but at least the application no longer hangs.


so, to answer your question, it is not the debugger which installs handlers for various exceptions, it is exactly the opposite, the application handles them all and even installs a handler for the debugging exception.


name it primitive, not appropriate for running a kernel, not appropriate for virtualisation, actually for nothing fancy, but this is how it works, it is very simple and serves its purpose very well.


regards,

Liviu






Liviu Ionescu

unread,
Dec 2, 2017, 9:47:47 PM12/2/17
to jcb6...@gmail.com, Andrew Waterman, Rishiyur Nikhil, RISC-V ISA Dev


> On 3 Dec 2017, at 02:01, Jacob Bachmeyer <jcb6...@gmail.com> wrote:
>
> ... RISC-V is not ARM.

It certainly is not.

From the bare-metal embedded software developer point of view, who spent several years in the Cortex-M world, where special care was taken in the design phase to make things as easy to use as possible (no need for assembly code, everything is available in C/C++, everything is memory mapped, so no CSRs, all system registers and peripherals are easily accessible by programmers and debuggers, the interrupts are nested and prioritised, there is a separate interrupts stack, the interrupt handlers can be written directly in C, the context switching required to support RTOSes is extremely easy to implement, etc), unfortunately RISC-V is one generation behind... :-(

Sure, RISC-V probably has the coolest instruction set, and probably the fastest and most compact code. but... how does this make the embedded software guy more productive, if he/she has to compensate in assembly when the hardware guys decided to save some transistors and oversimplify the hardware?

For Linux systems probably none of the above criteria are important, since a small team of super smart guys implement the several hundred assembly lines required by the kernel, and then millions of happy users interact with the RISC-V core only at high level, most probably in user space, and do not have to deal with low-level issues.

On the other hand, in the embedded world, each developer should be a small guru, and handle all the low-level detail alone, so if we expect to also have millions of happy embedded RISC-V developers, the design should be focused on making the architecture more friendlier for them, not on saving transistors.


The team that developed the ISA obviously enjoyed their work, and this can be seen in the elegant design. For the Linux market, this is probably enough for success.

However, if the RISC-V team will ever want to compete in the bare-metal embedded space, and dent ARMs market share, it should ensure that the embedded software developers also enjoy their work, by making the architecture more friendlier to them; unfortunately we're not yet there...

I already suggested to consider a small working group to analyse the current situation and make suggestions on how to improve the design for embedded devices, but until now the interest was very limited...


Regards,

Liviu








Jacob Bachmeyer

unread,
Dec 2, 2017, 11:16:42 PM12/2/17
to Liviu Ionescu, Andrew Waterman, Rishiyur Nikhil, RISC-V ISA Dev
Liviu Ionescu wrote:
>> On 3 Dec 2017, at 02:01, Jacob Bachmeyer <jcb6...@gmail.com> wrote:
>>
>> ... RISC-V is not ARM.
>>
>
> It certainly is not.
>
> >From the bare-metal embedded software developer point of view, who spent several years in the Cortex-M world, where special care was taken in the design phase to make things as easy to use as possible (no need for assembly code, everything is available in C/C++, everything is memory mapped, so no CSRs, all system registers and peripherals are easily accessible by programmers and debuggers, the interrupts are nested and prioritised, there is a separate interrupts stack, the interrupt handlers can be written directly in C, the context switching required to support RTOSes is extremely easy to implement, etc), unfortunately RISC-V is one generation behind... :-(
>

I disagree with "one generation behind": RISC-V simply optimizes for
different goals than ARM chose.

> Sure, RISC-V probably has the coolest instruction set, and probably the fastest and most compact code. but... how does this make the embedded software guy more productive, if he/she has to compensate in assembly when the hardware guys decided to save some transistors and oversimplify the hardware?
>

I would not call it oversimplification: RISC as a concept was always
intended more as a compiler target than a convenient assembler. RISC-V
stays true to that even at the cost of (some) software complexity, while
ARM has added even some very complex instructions for assembler
convenience and generally looks surprisingly like a CISC architecture in
some ways.

> The team that developed the ISA obviously enjoyed their work, and this can be seen in the elegant design. For the Linux market, this is probably enough for success.
>
> However, if the RISC-V team will ever want to compete in the bare-metal embedded space, and dent ARMs market share, it should ensure that the embedded software developers also enjoy their work, by making the architecture more friendlier to them; unfortunately we're not yet there...
>

There are two converging forces that I expect to push RISC-V into the
embedded market against the established ARM: first, the bare-metal
embedded market as a category is giving way to embedded Linux in many
areas; second, RISC-V implementations are expected to have favorable
pricing compared to ARM implementations. Either ARM cuts their
royalties dramatically in the face of this competition, or their
architecture will inevitably be relegated to various niches that can
justify the higher prices. I expect that mass market devices will move
to RISC-V (and implementations of varying quality -- I expect some real
turkeys to be produced) simply on price alone.

Adding features specifically for the embedded market to what is supposed
to be an all-purpose ISA seems short-sighted to me -- especially when
those features can be effectively emulated using slightly improved
development tools. (The toolchain knows where the semihost calls are;
all it needs to do is collect that information and write it into a table
in the final program ELF image. The semihosting runtime then looks up
EBREAKs in the table.) For comparison, ARM has very much focused on the
embedded market, and I believe that this may have had effects on their
architecture that are still hampering their entry into the server
market. In contrast, the only reason I know that RISC-V is starting at
the embedded end is that embedded systems use simpler chips, and we (the
RISC-V community) are planning to expand from simple microcontrollers
upwards as implementation experience is gained.


-- Jacob

Michael Clark

unread,
Dec 2, 2017, 11:18:09 PM12/2/17
to Liviu Ionescu, Jacob Bachmeyer, Andrew Waterman, Nikhil Rishiyur, RISC-V ISA Dev
I think there has been a little misunderstanding because of the mention of “syscall” and ABI. The semi-hosting “hostcalls” are very unlike any modern POSIX-like syscall ABI so there is no problem with maintaining any sort of compatibility. The POSIX interfaces are SYS_read/SYS_write and they take a file descriptor and use SYS_fcntl to control buffering and blocking / non-blocking IO. The semi-hosting “hostcalls" seem to me to be distinct.

I think the M-mode programability issues can be addressed both by compiler additions e.g. __attribute__((interrupt( irq ))) ; and also the creation of a M mode HAL. There is a lot of useful M-mode code inside of riscv-pk/machine and unfortunately a reasonable proportion of it targets environments with supervisors. I think it could be factored into a tiny M-mode CRT that is even smaller than newlib nano and could run on M-mode only environments (but not preclude M-mode code for environments with S-mode).

I’m interested in the idea of defining an MBI (Machine mode Binary Interface) much the the SBI (Supervisor Binary Interface). In fact it would be more like an API than an ABI (while symbol stability will be important many of the functions may be #define macros). Given it is M-mode only, there is no privilege separation necessary so C linkage can be used vs ECALL. C++ wrappers could easily be provided; as one typically does when providing a C++ interface, doing so on top of a core C API so that C++ is not mandatory.

A small assembly file that handles trap entry/exit which would be part of an MBI CRT and would be the only assembly required (much like riscv-pk’s mentry.s). One could allow an entire M-mode program to be written in C. I’ve already created a simple C interface to register a C function as a trap handler. No assembly required.

I can share something when I get further along. I’m writing a probe utility to compare hardware vs emulator privileged mode functions.

Bruce Hoult

unread,
Dec 3, 2017, 11:39:31 AM12/3/17
to Liviu Ionescu, Andrew Waterman, RISC-V ISA Dev, Rishiyur Nikhil, jcb6...@gmail.com
No doubt it is handy that on Cortex M the hardware saves the four volatile integer registers, meaning you can call a standard ABI C function and if it needs more it will save them itself.

For that to work on RISC-V, the hardware would have to save FIFTEEN integer registers — eight argument, and seven temporary. Plus twenty FP registers, if present.

I don’t think you’d like what that would do to interrupt latency. I’m sure it would make you say bad things about RISC-V for embrdded use.

Much better to be able to annotate interrupt handler functions to use a different calling convention where they save only and exactly the registers they use. Such a function could also call other functions with the same calling convention. But if they call a regular function … gonna have to save all those 15 (plus 20 FP) registers.


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

Liviu Ionescu

unread,
Dec 3, 2017, 5:31:41 PM12/3/17
to Bruce Hoult, Andrew Waterman, RISC-V ISA Dev, Rishiyur Nikhil, jcb6...@gmail.com


> On 3 Dec 2017, at 18:39, Bruce Hoult <br...@hoult.org> wrote:
>
> ... you can call a standard ABI C function and if it needs more it will save them itself. ... For that to work on RISC-V, the hardware would have to save FIFTEEN integer registers — eight argument, and seven temporary. ... I don’t think you’d like what that would do to interrupt latency. I’m sure it would make you say bad things about RISC-V for embrdded use.

the current recommended way to handle interrupts (FP regs not supported):

- https://github.com/sifive/freedom-e-sdk/blob/master/bsp/env/entry.S
- https://github.com/sifive/freedom-e-sdk/blob/master/bsp/env/ventry.S


regards,

liviu

kr...@berkeley.edu

unread,
Dec 28, 2017, 11:55:55 AM12/28/17
to RISC-V ISA Dev

I just wanted to catch up on this thread and add some background, some
discussion at the 7th workshop, and an improved proposal.

Summary first:

- Use EBREAK + 16-bit zero + 16-bit tag as current workaround to lack
of EBREAK immediate (effectively making a new 64-bit EBREAK encoding).

- Add "undelegate" feature to debug spec so ECALL (and other
exceptions/interrupts) can trap to debugger.

- Move eventually to using ECALL for semihosting-like applications
where debug hardware supports "undelegate".

- Use labeled ELF for EBREAK "assert failure" use cases.


Longer discussion:

First, some history and rationale. When we were deciding how to
encode EBREAK for RISC-V, we decided against having an immediate field
for the following reasons:

- A debugger knows the addresses at which it has inserted breakpoints.

- When the debugger is notified that a hart has stopped at a
breakpoint, it needs to read the pc at least.

- A debugger-side hashtable indexed by the pc can uncover all the
necessary information about that breakpoint without having to go
back and read the EBREAK instruction bits from target memory, and
without being constrained by the size of the EBREAK immediate field.

- A compressed EBREAK should work the same as an uncompressed EBREAK,
and encoding space is more critical in compressed code.

- More generally, we viewed EBREAKs as instructions that are poked
into compiled code from outside, not instructions that are compiled
in to a binary, though it's clear software breakpoints are being
used in this way for other architectures.

We were not thinking of semihosting as an debugger/EBREAK application,
though our proxy kernel approach for providing unix-like system calls
on bare metal hardware (including M-mode only) is very similar in
structure.

As several have pointed out, semihosting should really be handled via
ECALL. Multiplexing multiple ABIs onto ECALL just requires managing
the syscall number namespace (instead of managing the ECALL immediate
number namespace). The missing piece is that the current RISC-V debug
standard does not enter the debugger on an ECALL, and requiring that a
handler with EBREAK is installed at mtvec is apparently too intrusive
in some situations.

At the 7th workshop debug group meeting, we discussed these issues and
proposed adding a general "undelegate" feature to allow the debugger
to intervene on any exception (or interrupt). Andes mentioned they
already had this facility. I'd recommend the debug task group work on
adding this facility as an addition to the spec.

The current proposal for semihosting (or any other debugger-supported
environment call) of using an EBREAK instruction followed by a
guaranteed illegal instruction and a unique number makes sense as a
workaround for existing hardware. To reduce code size and transfer
bandwidth, I'd suggest always using a 16-bit illegal instruction
(16-bits of zero), followed by an arbitrary 16-bit number encoding the
"tag". The compressed zero instruction is always illegal, even if
compressed is not supported. The 16-bit tag should never be executed
as an instruction (if this is a concern, then we can overload a HINT
instruction to provide an immediate in an encoding that is a NOP -
this doesn't preclude use of same HINT for another purpose).

The other uses of EBREAK (flagging bad execution paths in Unix kernel
etc.), seem better handled by labeled ELF, e.g.,

interrupts_not_disabled: ebreak

Given the above, there's still no real incentive to change the ISA to
try and add immediate bits to EBREAK, and existing hardware and
cores could be incompatible. The workaround with EBREAK + 16-bit zero
+ 16-bit tag effectively adds a new 64-bit EBREAK instruction encoding
without changing anything.

The change to the debug spec to make ECALL able to be trapped by the
debugger is much less intrusive to the software ecosystem and adds a
lot more functionality, eventually including semihosting.

Krste

kr...@berkeley.edu

unread,
Dec 28, 2017, 12:13:01 PM12/28/17
to RISC-V ISA Dev

>>>>> On Fri, 29 Dec 2017 01:55:47 +0900, kr...@berkeley.edu said:
| The current proposal for semihosting (or any other debugger-supported
| environment call) of using an EBREAK instruction followed by a
| guaranteed illegal instruction and a unique number makes sense as a
| workaround for existing hardware. To reduce code size and transfer
| bandwidth, I'd suggest always using a 16-bit illegal instruction
| (16-bits of zero), followed by an arbitrary 16-bit number encoding the
| "tag". The compressed zero instruction is always illegal, even if
| compressed is not supported. The 16-bit tag should never be executed
| as an instruction (if this is a concern, then we can overload a HINT
| instruction to provide an immediate in an encoding that is a NOP -
| this doesn't preclude use of same HINT for another purpose).

After sending email, realized that a good reason to repurpose a HINT
with a small immediate (e.g., C.NOP imm!=0) is that the disassembly
would be readable, assuming we picked a reasonable way of rendering
hint numbers in disassembly.

Krste

Liviu Ionescu

unread,
Dec 29, 2017, 4:22:09 AM12/29/17
to kr...@berkeley.edu, RISC-V ISA Dev, Drew Barbier


> On 28 Dec 2017, at 18:55, kr...@berkeley.edu wrote:
>
>
> I just wanted to catch up on this thread and add some background, some
> discussion at the 7th workshop, and an improved proposal.

Thank you Krste, I appreciate your help in finding a solution for semihosting.

>
> Summary first:
>
> - Use EBREAK + 16-bit zero + 16-bit tag as current workaround to lack
> of EBREAK immediate (effectively making a new 64-bit EBREAK encoding).

This solution is available immediately and, from my point of view, it is ok.

> - Add "undelegate" feature to debug spec so ECALL (and other
> exceptions/interrupts) can trap to debugger.
>
> - Move eventually to using ECALL for semihosting-like applications
> where debug hardware supports "undelegate".

As these require changing the debug specs, hardware reimplementations, and reserving numbers in the Linux system ABI, it seems too far-fetched for my urgent needs.

And I'm not even sure that this solution is in the spirit of the original semihosting design, which was a completely separated channel, using only standard debug resources.

> - Use labeled ELF for EBREAK "assert failure" use cases.

As semihosting for running unit tests is different from running debug sessions, generally semihosting should work with non-elf binaries too. Not to mention that even when using ELF, it is not reasonable to expect **all** existing tools implementing semihosting to be able to identify labels, since this is usually done by GDB, not the GDB server.

> The change to the debug spec to make ECALL able to be trapped by the
> debugger is much less intrusive to the software ecosystem and adds a
> lot more functionality, eventually including semihosting.

I agree that trapping ECALLs by the debugger might be useful in some applications, but I doubt that semihosting itself should take this path.

For now, I suggest we stick with the EBREAK + 16-bit zero + 16-bit tag solution, with 7 as the value reserved for semihosting.

If SiFive will agree, in January I'll probably try to implement it in OpenOCD (since I need semihosting during the development of the bare-metal template RTOS projects) and let you know the results.



Regards,

Liviu

kr...@berkeley.edu

unread,
Dec 29, 2017, 10:46:44 AM12/29/17
to Liviu Ionescu, RISC-V ISA Dev, Drew Barbier

>>>>> On Fri, 29 Dec 2017 11:22:04 +0200, Liviu Ionescu <i...@livius.net> said:
|| On 28 Dec 2017, at 18:55, kr...@berkeley.edu wrote:
|| Summary first:
|| - Use EBREAK + 16-bit zero + 16-bit tag as current workaround to lack
|| of EBREAK immediate (effectively making a new 64-bit EBREAK encoding).

| This solution is available immediately and, from my point of view, it is ok.

To refine this further, I propose we use C.NOP non-zero immediate
"hints" to encode the EBREAK tag to support readable disassembly (I'm
assuming the disassembler will decode C instructions even if the file
was compiled for not-C processors). This gives a 6-bit immediate
field to encode EBREAK uses - and I'm fine with using value 7 for
semihosting. I think this explanation should live in the debug spec.
Note, this does not "use up" the C.NOP HINT, as it is still available
for whatever HINT purpose.

|| - Add "undelegate" feature to debug spec so ECALL (and other
|| exceptions/interrupts) can trap to debugger.
||
|| - Move eventually to using ECALL for semihosting-like applications
|| where debug hardware supports "undelegate".

| As these require changing the debug specs, hardware
| reimplementations, and reserving numbers in the Linux system ABI, it
| seems too far-fetched for my urgent needs.

Sure, this will take some time to settle, and existing hardware
implementations will not be able to use the ECALL convention unless
EBREAK-ing handlers are installed at mtvec, so the EBREAK convention
will be required for a long time.

| And I'm not even sure that this solution is in the spirit of the
| original semihosting design, which was a completely separated
| channel, using only standard debug resources.

I want to separate out two goals here.

1) Supporting I/O via the debugger interface.

2) Having two (or more) independent I/O streams to an embedded device
under development.

For goal 1), debugger-ECALL would be fine, and would support more use
cases than EBREAK, by allowing the same I/O ABI to be supported by
many more outer execution environments than just the debugger (as
Michael Clark pointed out). Semihosting is one ABI, but there are
other existing ABIs that are useful even on M-mode-only hardware, like
the proxy-kernel version of the unix ABI for bare metal processes or
test environment ABIs, and ECALL allows things other than the debugger
environment to support the same ABI.

In other words, while you've rightly been complaining that ECALL does
not support semihosting via the debugger interface under current
RISC-V debug spec (unless mtvec handler can be installed), the other
side of the coin is that the EBREAK model doesn't work well with every
other possible outer environment that could implement the same ABI.
EBREAK, as used in semihosting, is an explicit call to a particular
outer execution environment (the debugger), whereas ECALL is a more
generic call to whatever is the current outer execution environment.
Once we have ECALL able to connect with the debugger environment also,
it will be quite compelling to move over.

For goal 2), during development, you'll probably want to multiplex
warning/tracing message streams from many different hardware/software
components in addition to the application I/O over one or more
physical links (JTAG, UARTs, etc.). You'd ideally like to change which
streams are active, and over which links they're sending data (using
the debugger). Your hack to ignore EBREAK semihosting calls when the
debugger isn't present is actually an instance of this. You probably
don't want the debugger intercepting every application software stream
I/O event, as it could slow execution considerably and some should be
directed to other I/O ports not the debug device I/O ports, so you
wouldn't want to use either EBREAK or debugger-ECALL in every ABI I/O
call. Instead, you'd have a standard ECALL ABI whose implementation
on the target as an ECALL handler would know how to route streams to
given I/O channels, including the debugger interface using EBREAK.
This might all sound complex, but isn't really much more code than
needed to support a single semihosting I/O interface if coded right.

|| - Use labeled ELF for EBREAK "assert failure" use cases.

| As semihosting for running unit tests is different from running
| debug sessions, generally semihosting should work with non-elf
| binaries too. Not to mention that even when using ELF, it is not
| reasonable to expect **all** existing tools implementing semihosting
| to be able to identify labels, since this is usually done by GDB,
| not the GDB server.

I was thinking of a different use case for EBREAK here, to help
diagnose bugs in target software. Once execution stops at a
compiled-in EBREAK representing a known error condition, the
programmer can find out if there was a label at that address to help
figure out what went wrong. This is unrelated to semihosting, as will
happen without a debugger attached, and I was not proposing that
semihosting used labeled ELF.

|| The change to the debug spec to make ECALL able to be trapped by the
|| debugger is much less intrusive to the software ecosystem and adds a
|| lot more functionality, eventually including semihosting.

| I agree that trapping ECALLs by the debugger might be useful in some applications, but I doubt that semihosting itself should take this path.

| For now, I suggest we stick with the EBREAK + 16-bit zero + 16-bit
| tag solution, with 7 as the value reserved for semihosting.

Sure.

Regards,
Krste

Liviu Ionescu

unread,
Dec 29, 2017, 11:04:09 AM12/29/17
to kr...@berkeley.edu, RISC-V ISA Dev, Drew Barbier


> On 29 Dec 2017, at 17:46, kr...@berkeley.edu wrote:
>
> I propose we use C.NOP non-zero immediate
> "hints" to encode the EBREAK tag to support readable disassembly (I'm
> assuming the disassembler will decode C instructions even if the file
> was compiled for not-C processors). This gives a 6-bit immediate
> field to encode EBREAK uses - and I'm fine with using value 7 for
> semihosting. I think this explanation should live in the debug spec.
> Note, this does not "use up" the C.NOP HINT, as it is still available
> for whatever HINT purpose.

Sure, once we agreed on the principle (EBREAK+NOP), I'll use exactly the syntax you suggest, assuming it is supported by the current assembler/disassembler.

And yes, the explanation should definitely be in the debug specs.

Can anyone in the debugger working group suggest the additions to the specs? please be sure you
include an example with the exact syntax to be used in a program.


Thank you,

Liviu

Liviu Ionescu

unread,
Dec 29, 2017, 11:52:23 AM12/29/17
to kr...@berkeley.edu, RISC-V ISA Dev, Drew Barbier


> On 29 Dec 2017, at 17:46, kr...@berkeley.edu wrote:
>
> To refine this further, I propose we use C.NOP non-zero immediate
> "hints" to encode the EBREAK tag to support readable disassembly

On second thought, although readable disassembly would be nice, I'm afraid C.NOP would be less reliable than 16-bit zero + 16-bit tag.

The main weakness of using some additional encoding after the single EBREAK is false positives. If the hinted C.NOP is a legal instruction, it may appear anywhere in the program. If so, if the debugger happens to place a breakpoint on the instruction before the C.NOP, depending on how smart the debugger is, it might erroneously consider the break a semihosting call.

The 16-bit zero, being a guaranteed illegal instruction, is less likely to appear after a debugger placed breakpoint; even so, the probability is not 0, since an assembly programmer can always define a sequence of zeros after a jump, for example, and placing a breakpoint on the jump may also be interpreted as a semihosting call.


Regards,

Liviu




Jim Wilson

unread,
Dec 29, 2017, 12:05:57 PM12/29/17
to kr...@berkeley.edu, Liviu Ionescu, RISC-V ISA Dev, Drew Barbier
On Fri, Dec 29, 2017 at 7:46 AM, <kr...@berkeley.edu> wrote:
> To refine this further, I propose we use C.NOP non-zero immediate
> "hints" to encode the EBREAK tag to support readable disassembly (I'm
> assuming the disassembler will decode C instructions even if the file
> was compiled for not-C processors).

Currently, yes. The only info the disassembler has is whether you
have a 32-bit or 64-bit target, from the ELF header size, so we only
check whether you have valid rv32 or rv64 instructions.

However, there is a proposal to add ELF attribute support to the
RISC-V port, similar to the ARM .eabi_attribute support. With this
additional info, the disassembler would know whether compressed
instructions were valid for the target or not, and it would make sense
to refuse to disassemble them when they aren't valid.

Jim

kr...@berkeley.edu

unread,
Dec 29, 2017, 7:15:00 PM12/29/17
to Liviu Ionescu, kr...@berkeley.edu, RISC-V ISA Dev, Drew Barbier

Sorry for the confusion. My proposal was that the following sequence
encodes extended EBREAKs for semihosting:

EBREAK (or C.EBREAK for RVC targets)
C.ILLEGAL (16 zero bits)
C.HINT imm6=7 (immediate of C.NOP set to 7)

So the hint replaces the 16-bit tag in earlier version.

I'm assuming there's no reason not to use a 16-bit C.EBREAK on RVC
targets, so the sequence is only 6 bytes on RVC targets.

Krste

kr...@berkeley.edu

unread,
Dec 29, 2017, 7:18:24 PM12/29/17
to Jim Wilson, kr...@berkeley.edu, Liviu Ionescu, RISC-V ISA Dev, Drew Barbier
I wonder if refusing to disassemble is a good policy? Some targets
won't support a full set of instructions from a category given by an
attribute, but might have a subset implemented. In this case, it
would be more useful to always disassemble any instruction patterns
that the disassembler knew about. Also, seeing the disassembled
instructions might help debug where the illegal instructions came
from.

Krste


| Jim

Liviu Ionescu

unread,
Dec 29, 2017, 7:31:02 PM12/29/17
to kr...@berkeley.edu, RISC-V ISA Dev, Drew Barbier


> On 30 Dec 2017, at 02:14, kr...@berkeley.edu wrote:
>
>
> Sorry for the confusion. My proposal was that the following sequence
> encodes extended EBREAKs for semihosting:
>
> EBREAK (or C.EBREAK for RVC targets)
> C.ILLEGAL (16 zero bits)
> C.HINT imm6=7 (immediate of C.NOP set to 7)
>
> So the hint replaces the 16-bit tag in earlier version.

Ah, ok, then it should not be a problem.

I'm not sure I understand the syntax of C.HINT, but I'll experiment until I get the assembler generate the desired code.

> I'm assuming there's no reason not to use a 16-bit C.EBREAK on RVC
> targets, so the sequence is only 6 bytes on RVC targets.

Actually the code size should not be a real concern. Even for my current code, which on ARM uses inline functions, that should not be a problem, there are only about 20-30 semihosting calls all (if I remove the inline and have actual function calls, probably the size will be comparable).


Thank you,

Liviu

Jim Wilson

unread,
Dec 29, 2017, 7:41:17 PM12/29/17
to kr...@berkeley.edu, Liviu Ionescu, RISC-V ISA Dev, Drew Barbier
On Fri, Dec 29, 2017 at 4:18 PM, <kr...@berkeley.edu> wrote:
> I wonder if refusing to disassemble is a good policy?

If we have multiple instructions with the same encoding, then we have
to refuse to disassemble encodings that aren't appropriate for the
target, in order to get the right one. This can be seen with
compressed instructions, where for instance c.flw (rv32) has the same
encoding as c.ld (rv64/rv128). This could also be an issue for vendor
extensions, where we could have multiple vendors re-using the same
encoding for multiple different instructions.

The disassembler does a single pass through the instruction table, and
prints the first one that matches. If there are conflicts, then we
have to be sure not to match ones that aren't correct for the target.
But if we don't match any, then yes, we end up with an instruction
that doesn't get disassembled, which isn't very useful. There are
multiple ways to solve this problem. We could have a command line
option to override the arch specified by the ELF attribute section, so
you can get a disassembly by specifying a more general architecture.
We could maybe keep track of the best almost matching instruction and
print that if none match. Or maybe we could do a second pass through
the table, using relaxed matching constraints for the second pass.
There are probably other solutions. The best solution may depend on
user preference, what encoding conflicts we end up with, and may
depend on exactly how the ELF attributes are used, which hasn't been
implemented or specified yet, so it is hard to say at the moment what
the best solution might be.

Jim

Jim Wilson

unread,
Dec 29, 2017, 7:47:04 PM12/29/17
to Liviu Ionescu, kr...@berkeley.edu, RISC-V ISA Dev, Drew Barbier
On Fri, Dec 29, 2017 at 4:30 PM, Liviu Ionescu <i...@livius.net> wrote:
>> Sorry for the confusion. My proposal was that the following sequence
>> encodes extended EBREAKs for semihosting:
>>
>> EBREAK (or C.EBREAK for RVC targets)
>> C.ILLEGAL (16 zero bits)
>> C.HINT imm6=7 (immediate of C.NOP set to 7)
>>
>> So the hint replaces the 16-bit tag in earlier version.
>
> Ah, ok, then it should not be a problem.
>
> I'm not sure I understand the syntax of C.HINT, but I'll experiment until I get the assembler generate the desired code.

c.illegal and c.hint aren't accepted by the assembler, because as far
as I know they aren't (currently) valid instructions.

The current FSF Binutils tree will accept
c.nop 7
as a valid instruction though. I added this when I was asked to add
hint support. Hints are defined in current ISA docs, but there is no
assembler syntax defined for them, so I just assumed that they used
the same syntax as the existing instructions. This can be changed if
a syntax is defined for them.

Jim

kr...@berkeley.edu

unread,
Dec 30, 2017, 9:30:27 PM12/30/17
to Jim Wilson, kr...@berkeley.edu, Liviu Ionescu, RISC-V ISA Dev, Drew Barbier

By definition, the standard extensions for each base ISA will not
conflict, so given the base (RV32E/RV32I/RV64I/RV128I), the standard
instruction encodings will be unambiguous.

Krste

kr...@berkeley.edu

unread,
Dec 30, 2017, 9:43:15 PM12/30/17
to Jim Wilson, Liviu Ionescu, kr...@berkeley.edu, RISC-V ISA Dev, Drew Barbier

I was just using these assembler opcodes as placeholders.

I think c.nop 7 is fine for now as the hint assembly syntax.

I think adding opcode c.illegal (or something similar) for 16 bits of
zero is probably a good idea.

The other illegal pattern is all 1s, which represents a 32-bit or
longer instruction. We haven't precisely specified this yet, but I
propose we make 0xffffffff (32 1s) be the other reserved illegal
instruction and give this the assembler opcode "illegal1". Note, we
could specify an even longer string of 1s be "illegal", but stopping
at 32 will make disassembly work on current targets that have ILEN=32.
This does add a minor constraint on very long instruction encodings
(>191 bits - none of which are considered frozen yet).

So maybe c.illegal should be "c.illegal0", as there is no
corresponding 32-bit illegal with all 0s.

So, "16 zeros" is "C.illegal0" and "32 ones" is "illegal1"

I'm open to better assembler opcode names,

Krste

Jacob Bachmeyer

unread,
Dec 30, 2017, 10:44:16 PM12/30/17
to kr...@berkeley.edu, Jim Wilson, Liviu Ionescu, RISC-V ISA Dev, Drew Barbier
kr...@berkeley.edu wrote:
> I was just using these assembler opcodes as placeholders.
>
> I think c.nop 7 is fine for now as the hint assembly syntax.
>

I agree; this provides support for experimentation with future hints.

> I think adding opcode c.illegal (or something similar) for 16 bits of
> zero is probably a good idea.
>

Why not just use ".dh 0" to directly place a 16-bit halfword containing
zero?

> The other illegal pattern is all 1s, which represents a 32-bit or
> longer instruction. We haven't precisely specified this yet, but I
> propose we make 0xffffffff (32 1s) be the other reserved illegal
> instruction and give this the assembler opcode "illegal1". Note, we
> could specify an even longer string of 1s be "illegal", but stopping
> at 32 will make disassembly work on current targets that have ILEN=32.
> This does add a minor constraint on very long instruction encodings
> (>191 bits - none of which are considered frozen yet).
>
> So maybe c.illegal should be "c.illegal0", as there is no
> corresponding 32-bit illegal with all 0s.
>
> So, "16 zeros" is "C.illegal0" and "32 ones" is "illegal1"
>
> I'm open to better assembler opcode names,
>

I had understood that while 32-bit zero decodes as a 16-bit instruction,
implementations lacking RVC would still read the entire 32-bit word,
since they do not support 16-bit instructions. I had also understood
that "all-ones" is actually an infinitely-long instruction, which any
implementation with a finite ILEN will eventually recognize exceeds the
longest available instruction.

Since the reason for "all zero" and "all ones" to be illegal is to
ensure that a jump to unimplemented memory will quickly trap, I suggest
either making an all-ones first parcel explicitly illegal (which will
move the threshold for "very long" instructions from 192 bits to 176
bits by making "nnn == 3'b111" illegal and "nnn == 3'b110" indicate
length greater than or equal to 176 bits with further encoding in later
parcels; following the pattern, the R-type "funct7" field would be the
next length field). Another option is to forbid 192-bit or longer
instructions from using x31 as destination, but this rather severely
compromises regularity and simply moving the threshold makes more sense.

But now we are back to wanting some illegal instruction encoding for
each valid instruction length. Could almost-all-zeros or
almost-all-ones (except for the fields that determine instruction
length) work here?


-- Jacob

Jacob Bachmeyer

unread,
Dec 30, 2017, 10:46:40 PM12/30/17
to kr...@berkeley.edu, Jim Wilson, Liviu Ionescu, RISC-V ISA Dev, Drew Barbier
kr...@berkeley.edu wrote:
> By definition, the standard extensions for each base ISA will not
> conflict, so given the base (RV32E/RV32I/RV64I/RV128I), the standard
> instruction encodings will be unambiguous.
>

As I read it, Jim Wilson's concerns are that disassemblers have some way
to support conflicting *non-standard* extensions.


-- Jacob

kr...@berkeley.edu

unread,
Dec 30, 2017, 11:32:31 PM12/30/17
to jcb6...@gmail.com, kr...@berkeley.edu, Jim Wilson, Liviu Ionescu, RISC-V ISA Dev, Drew Barbier

>>>>> On Sat, 30 Dec 2017 21:44:10 -0600, Jacob Bachmeyer <jcb6...@gmail.com> said:
| kr...@berkeley.edu wrote:
|| I think adding opcode c.illegal (or something similar) for 16 bits of
|| zero is probably a good idea.
| Why not just use ".dh 0" to directly place a 16-bit halfword containing| zero?

Want something readable on disassembly.

Krste

kr...@berkeley.edu

unread,
Dec 30, 2017, 11:34:28 PM12/30/17
to jcb6...@gmail.com, kr...@berkeley.edu, Jim Wilson, Liviu Ionescu, RISC-V ISA Dev, Drew Barbier

No, Jim was worried about C.FLW versus C.LD, which are standard
extensions for two different bases. Also, C versus non-C targets.

Krste

Jacob Bachmeyer

unread,
Dec 31, 2017, 12:05:41 AM12/31/17
to kr...@berkeley.edu, Jim Wilson, Liviu Ionescu, RISC-V ISA Dev, Drew Barbier
Could the disassembler recognize the 64-bit EBREAK/zero/C.NOP7 sequence
as a unit? Perhaps display it as "SHCALL" for "semi-host call"?


-- Jacob

kr...@berkeley.edu

unread,
Dec 31, 2017, 1:53:11 AM12/31/17
to jcb6...@gmail.com, RISC-V ISA Dev

Changing thread name and CC list for different topic.

>>>>> On Sat, 30 Dec 2017 21:44:10 -0600, Jacob Bachmeyer <jcb6...@gmail.com> said:
|| The other illegal pattern is all 1s, which represents a 32-bit or
|| longer instruction. We haven't precisely specified this yet, but I
|| propose we make 0xffffffff (32 1s) be the other reserved illegal
|| instruction and give this the assembler opcode "illegal1". Note, we
|| could specify an even longer string of 1s be "illegal", but stopping
|| at 32 will make disassembly work on current targets that have ILEN=32.
|| This does add a minor constraint on very long instruction encodings
|| (>191 bits - none of which are considered frozen yet).
||
|| So maybe c.illegal should be "c.illegal0", as there is no
|| corresponding 32-bit illegal with all 0s.
||
|| So, "16 zeros" is "C.illegal0" and "32 ones" is "illegal1"
||
|| I'm open to better assembler opcode names,

| I had understood that while 32-bit zero decodes as a 16-bit
| instruction, implementations lacking RVC would still read the entire
| 32-bit word, since they do not support 16-bit instructions.

I don't see a problem here, as any instruction with the two low bits
zero will raise an illegal instruction exception on a non-C target.

| I had also understood that "all-ones" is actually an infinitely-long
| instruction, which any implementation with a finite ILEN will
| eventually recognize exceeds the longest available instruction.

Yes, but defining a shorter finite length as illegal simplifies
disassembly/debug.

| Since the reason for "all zero" and "all ones" to be illegal is to
| ensure that a jump to unimplemented memory will quickly trap, I
| suggest either making an all-ones first parcel explicitly illegal
| (which will move the threshold for "very long" instructions from 192
| bits to 176 bits by making "nnn == 3'b111" illegal and "nnn ==
| 3'b110" indicate length greater than or equal to 176 bits with
| further encoding in later| parcels; following the pattern, the
| R-type "funct7" field would be the next length field). Another
| option is to forbid 192-bit or longer instructions from using x31 as
| destination, but this rather severely compromises regularity and
| simply moving the threshold makes more sense.

I agree making a single starting parcel (16 bits) of all ones be
illegal is probably preferable, even if it constrains longer
instruction encodings, as this is the smallest unit of instruction
fetch. Speed of trapping is irrelevant, but it is useful to be able
to have the last 16 bits of some unprogrammed instruction memory be
illegal, when unprogrammed state is all 1s.

As you say, messing with rd field will reduce regularity. Requiring
bit 15 (low bit of rs1) be zero in legal instructions is similarly
problematic. Reserving nnn=111 for single-parcel illegal instructions
is probably the least impactful choice:

xnnnxxxxx1111111 80+16*nnn-bit instructions, nnn != 11x
x110xxxxx1111111 >= 176-bit instructions
r111rrrrr1111111 reserved (rrrrrr!=111111)
1111111111111111 illegal

This does create 63 unused single-parcel encodings, but with different
length decode than standard instructions, so probably worth avoiding
their use (except as enumerated illegals maybe).

This is a change to the standard length-encoding scheme, but shouldn't
affect any implementations, as they are very unlikely to be using
176-bit or longer instruction encodings and should all be signaling
illegal instruction exceptions on longer instruction lengths already.

For (dis)assembly syntax, I'd suggest just "illegal0" and "illegal1"
for 16 0s and 16 1s respectively. Using the C. prefix for the illegal1
just seems wrong, and only using on illegal0 seems asymmetric.

| But now we are back to wanting some illegal instruction encoding for
| each valid instruction length. Could almost-all-zeros or
| almost-all-ones (except for the fields that determine instruction
| length) work here?

With single-parcel encodings for illegal bit patterns, there's no need
for longer illegal instruction bit patterns. We don't care about the
length of a non-existent instruction. A fetch unit will stop wherever
it enters a memory containing all 0s or all 1s.

Krste

Andrew Waterman

unread,
Dec 31, 2017, 4:15:50 AM12/31/17
to Jacob Bachmeyer, Krste Asanovic, Jim Wilson, Liviu Ionescu, RISC-V ISA Dev, Drew Barbier
Right now the disassembler never attempts to match instruction
sequences, and strictly uses the standard instruction-length encoding
scheme to determine how long an instruction is. So it would be a
significant change.

Additionally, it would bake this ABI into the disassembler, which
while not problematic seems a little unfortunate.

I think that disassembling it as ebreak/c.unimp/c.nop 7 stands out
pretty clearly.

>
>
> -- Jacob
>
> --
> You received this message because you are subscribed to the Google Groups
> "RISC-V ISA Dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to isa-dev+u...@groups.riscv.org.
> To post to this group, send email to isa...@groups.riscv.org.
> Visit this group at
> https://groups.google.com/a/groups.riscv.org/group/isa-dev/.
> To view this discussion on the web visit
> https://groups.google.com/a/groups.riscv.org/d/msgid/isa-dev/5A487020.5050108%40gmail.com.

Bruce Hoult

unread,
Dec 31, 2017, 4:17:50 AM12/31/17
to Krste Asanovic, Jacob Bachmeyer, RISC-V ISA Dev
On Sun, Dec 31, 2017 at 9:53 AM, <kr...@berkeley.edu> wrote:
I agree making a single starting parcel (16 bits) of all ones be
illegal is probably preferable, even if it constrains longer
instruction encodings, as this is the smallest unit of instruction
fetch.  Speed of trapping is irrelevant, but it is useful to be able
to have the last 16 bits of some unprogrammed instruction memory be
illegal, when unprogrammed state is all 1s.

As you say, messing with rd field will reduce regularity.  Requiring
bit 15 (low bit of rs1) be zero in legal instructions is similarly
problematic.  Reserving nnn=111 for single-parcel illegal instructions
is probably the least impactful choice:

xnnnxxxxx1111111 80+16*nnn-bit instructions, nnn != 11x
x110xxxxx1111111 >= 176-bit instructions
r111rrrrr1111111 reserved (rrrrrr!=111111)
1111111111111111 illegal

This does create 63 unused single-parcel encodings, but with different
length decode than standard instructions, so probably worth avoiding
their use (except as enumerated illegals maybe).

All makes sense.

Maybe you just invented an SVC.C #imm6 :-) :-)

The only .C instruction with low bits "11".

Andrew Waterman

unread,
Dec 31, 2017, 4:25:59 AM12/31/17
to Krste Asanovic, Jacob Bachmeyer, RISC-V ISA Dev
Implementations should not be required to treat the instruction
1111_1111_1111_1111 as being 16 bits long, though.

For an implementation that supports 16-bit and 32-bit instructions
only, it is more convenient to treat it as a 32-bit instruction for
purposes of instruction-fetch faults. If c.illegal1 is the last
instruction on the page (starting at address 0xFFE), and the next page
isn't mapped in, the implementation should be able to raise an
instruction page-fault exception, rather than raising an
illegal-instruction exception.
> --
> You received this message because you are subscribed to the Google Groups "RISC-V ISA Dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to isa-dev+u...@groups.riscv.org.
> To post to this group, send email to isa...@groups.riscv.org.
> Visit this group at https://groups.google.com/a/groups.riscv.org/group/isa-dev/.
> To view this discussion on the web visit https://groups.google.com/a/groups.riscv.org/d/msgid/isa-dev/23112.35150.12448.434738%40KAMacBookAir2012.local.

Bruce Hoult

unread,
Dec 31, 2017, 4:31:37 AM12/31/17
to Andrew Waterman, Krste Asanovic, Jacob Bachmeyer, RISC-V ISA Dev
But you agree it should be illegal regardless of what the next 16 bit parcel contains?

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

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

Andrew Waterman

unread,
Dec 31, 2017, 4:40:56 AM12/31/17
to Bruce Hoult, Krste Asanovic, Jacob Bachmeyer, RISC-V ISA Dev
Yup.
>> > an email to isa-dev+u...@groups.riscv.org.
>> > To post to this group, send email to isa...@groups.riscv.org.
>> > Visit this group at
>> > https://groups.google.com/a/groups.riscv.org/group/isa-dev/.
>> > To view this discussion on the web visit
>> > https://groups.google.com/a/groups.riscv.org/d/msgid/isa-dev/23112.35150.12448.434738%40KAMacBookAir2012.local.
>>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "RISC-V ISA Dev" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to isa-dev+u...@groups.riscv.org.

Krste Asanovic

unread,
Dec 31, 2017, 7:50:19 AM12/31/17
to Andrew Waterman, Jacob Bachmeyer, RISC-V ISA Dev
Agreed.

In general, instructions starting with all-ones parcel are treated as if ILEN bits long.

This also affects mtval if written on illegal instruction fault, where the other parcels should be written up to ILEN/XLEN.

Krste

Krste Asanovic

unread,
Dec 31, 2017, 8:12:35 AM12/31/17
to Andrew Waterman, Jacob Bachmeyer, RISC-V ISA Dev
Also, I guess the disassembler should treat as ILEN bits of illegal,

Krste

Andrew Waterman

unread,
Dec 31, 2017, 8:51:38 AM12/31/17
to Krste Asanovic, Jacob Bachmeyer, RISC-V ISA Dev
The disassembler doesn't know ILEN, though. For standard programs,
the disassembler could attempt to infer ILEN from the ABI, but that's
not foolproof in the presence of nonstandard extensions (nor even for
unusual standard ABIs, like soft-float calling convention + the L
extension).

Could add more ELF flags, but that's a pain, and it's ELF-specific,
and I don't think this matters very much. Better, I think, just to
unconditionally disassemble it as a 16-bit instruction.

kr...@berkeley.edu

unread,
Dec 31, 2017, 9:20:20 AM12/31/17
to Andrew Waterman, Krste Asanovic, Jacob Bachmeyer, RISC-V ISA Dev

OK, makes sense.

But maybe should be unconditionally disassembled as 32-bit instead of
16-bit, since we know ILEN is at least 32? Will give less misleading
disassembly on most targets if second parcel is not all 1s.

This is definitely a third- or fourth-order concern...

Krste

Jacob Bachmeyer

unread,
Dec 31, 2017, 6:33:19 PM12/31/17
to kr...@berkeley.edu, RISC-V ISA Dev
kr...@berkeley.edu wrote:
> Changing thread name and CC list for different topic.
>
>
> On Sat, 30 Dec 2017 21:44:10 -0600, Jacob Bachmeyer <jcb6...@gmail.com> said:
>
> || The other illegal pattern is all 1s, which represents a 32-bit or
> || longer instruction. We haven't precisely specified this yet, but I
> || propose we make 0xffffffff (32 1s) be the other reserved illegal
> || instruction and give this the assembler opcode "illegal1". Note, we
> || could specify an even longer string of 1s be "illegal", but stopping
> || at 32 will make disassembly work on current targets that have ILEN=32.
> || This does add a minor constraint on very long instruction encodings
> || (>191 bits - none of which are considered frozen yet).
> ||
> || So maybe c.illegal should be "c.illegal0", as there is no
> || corresponding 32-bit illegal with all 0s.
> ||
> || So, "16 zeros" is "C.illegal0" and "32 ones" is "illegal1"
> ||
> || I'm open to better assembler opcode names,
>
> | I had understood that while 32-bit zero decodes as a 16-bit
> | instruction, implementations lacking RVC would still read the entire
> | 32-bit word, since they do not support 16-bit instructions.
>
> I don't see a problem here, as any instruction with the two low bits
> zero will raise an illegal instruction exception on a non-C target.
>

This could be a problem on implementations that lack RVC but use the
user ISA spec suggestion (21.3 "Extensions within fixed-width 32-bit
instruction format") to put non-standard extensions in the other 3/4 of
the 32-bit encoding space.

> | I had also understood that "all-ones" is actually an infinitely-long
> | instruction, which any implementation with a finite ILEN will
> | eventually recognize exceeds the longest available instruction.
>
> Yes, but defining a shorter finite length as illegal simplifies
> disassembly/debug.
>

Agreed.

> | Since the reason for "all zero" and "all ones" to be illegal is to
> | ensure that a jump to unimplemented memory will quickly trap, I
> | suggest either making an all-ones first parcel explicitly illegal
> | (which will move the threshold for "very long" instructions from 192
> | bits to 176 bits by making "nnn == 3'b111" illegal and "nnn ==
> | 3'b110" indicate length greater than or equal to 176 bits with
> | further encoding in later parcels; following the pattern, the
> | R-type "funct7" field would be the next length field). Another
> | option is to forbid 192-bit or longer instructions from using x31 as
> | destination, but this rather severely compromises regularity and
> | simply moving the threshold makes more sense.
>
> I agree making a single starting parcel (16 bits) of all ones be
> illegal is probably preferable, even if it constrains longer
> instruction encodings, as this is the smallest unit of instruction
> fetch. Speed of trapping is irrelevant, but it is useful to be able
> to have the last 16 bits of some unprogrammed instruction memory be
> illegal, when unprogrammed state is all 1s.
>

The delay before an illegal instruction trap occurs after a jump to
unimplemented memory in cycles is irrelevant, but ensuring that no
instructions are executed in that case will make the crash dump more
useful. This is the "quickly" that I mentioned -- no instructions are
executed, unlike (for example) x86 where all-zeros is a valid instruction.

> As you say, messing with rd field will reduce regularity. Requiring
> bit 15 (low bit of rs1) be zero in legal instructions is similarly
> problematic. Reserving nnn=111 for single-parcel illegal instructions
> is probably the least impactful choice:
>
> xnnnxxxxx1111111 80+16*nnn-bit instructions, nnn != 11x
> x110xxxxx1111111 >= 176-bit instructions
> r111rrrrr1111111 reserved (rrrrrr!=111111)
> 1111111111111111 illegal
>
> This does create 63 unused single-parcel encodings, but with different
> length decode than standard instructions, so probably worth avoiding
> their use (except as enumerated illegals maybe).
>

Perhaps a "BKPT <imm6>" instruction? :-)

> For (dis)assembly syntax, I'd suggest just "illegal0" and "illegal1"
> for 16 0s and 16 1s respectively. Using the C. prefix for the illegal1
> just seems wrong, and only using on illegal0 seems asymmetric.
>

On the other hand, "illegal0" and "illegal1" would be the only mnemonics
for single-parcel instructions that lack the "C." prefix. The
disassembler could report them as simply "illegal: 0x<bits>", while ".dh
0" and ".dh 0xFFFF" could be used in assembly to place such an
"instruction". (For the semi-host calls that spawned this discussion,
those would be in an assembler macro that expands to "EBREAK/.dh 0/C.NOP
7".)

> | But now we are back to wanting some illegal instruction encoding for
> | each valid instruction length. Could almost-all-zeros or
> | almost-all-ones (except for the fields that determine instruction
> | length) work here?
>
> With single-parcel encodings for illegal bit patterns, there's no need
> for longer illegal instruction bit patterns. We don't care about the
> length of a non-existent instruction. A fetch unit will stop wherever
> it enters a memory containing all 0s or all 1s.
>

But we do care: illegal instruction traps must be resumable in RISC-V,
since no distinction is made between non-existent and emulated
instructions at the hardware level. Also, a disassembler cannot simply
stop after reaching an illegal instruction. What comes after it? The
disassembler needs an answer to that.


-- Jacob

Jacob Bachmeyer

unread,
Dec 31, 2017, 6:37:36 PM12/31/17
to Krste Asanovic, Andrew Waterman, RISC-V ISA Dev
Krste Asanovic wrote:
> In general, instructions starting with all-ones parcel are treated as if ILEN bits long.
>

This is correct even if the all-ones instruction is infinitely long:
the implementation knows it is looking at an unimplemented instruction
when it reaches ILEN and there are still more parcels to fetch. In
other words, this interpretation is backwards compatible.


-- Jacob

Jacob Bachmeyer

unread,
Dec 31, 2017, 6:44:31 PM12/31/17
to kr...@berkeley.edu, Andrew Waterman, RISC-V ISA Dev
kr...@berkeley.edu wrote:
> OK, makes sense.
>
> But maybe should be unconditionally disassembled as 32-bit instead of
> 16-bit, since we know ILEN is at least 32? Will give less misleading
> disassembly on most targets if second parcel is not all 1s.
>

If we change the standard length encoding to make an all-ones or
nearly-all-ones (excepting register fields) first parcel a 16-bit
illegal instruction, then it *is* now a 16-bit instruction and the
disassembler should treat it as such. The only question then is whether
disassembly should resume with what (unless RVC is implemented) is a
misaligned instruction or display a second 16-bit value and resume at
32-bit alignment.

> This is definitely a third- or fourth-order concern...
>

Agreed, but it is also a loose end that we should tie up.



-- Jacob

John Hauser

unread,
Jan 1, 2018, 2:15:48 AM1/1/18
to RISC-V ISA Dev
Krste wrote:
As several have pointed out, semihosting should really be handled via
ECALL.  Multiplexing multiple ABIs onto ECALL just requires managing
the syscall number namespace (instead of managing the ECALL immediate
number namespace).  The missing piece is that the current RISC-V debug
standard does not enter the debugger on an ECALL, and requiring that a
handler with EBREAK is installed at mtvec is apparently too intrusive
in some situations.

At the 7th workshop debug group meeting, we discussed these issues and
proposed adding a general "undelegate" feature to allow the debugger
to intervene on any exception (or interrupt). Andes mentioned they
already had this facility.  I'd recommend the debug task group work on
adding this facility as an addition to the spec.

You can count me as someone who thinks it's suboptimal to support semihosting (or other host-debugger calls) using either of the existing EBREAK or ECALL instructions.  Given that EBREAK is currently the only instruction that a host debugger may be able to hook, I understand that some kind of hack using EBREAK may be needed in the short term.  However, for a better, long-term solution, I'd vote against trying to carve out a subspace of ECALL for semihosting.

While the plan to "undelegate" ECALLs to the host debugger will certainly work functionally, I don't think you've thoroughly considered the impact such a technique will have on execution speed.  You're going to get a very noticeable slowdown from trapping every ECALL through the external host.  Once you've started down this path, there will be pressure next to have the debug module itself test the register values that distinguish semihosting calls from other calls, and then the interface starts becoming more baroque, and the debug module (or the processor core) becomes unnecessarily more complex.

I think a more practical alternative would be to define a dedicated DCALL instruction for the debug module to hook, with ECALL normally left alone.  When not hooked by the debug module, a DCALL would trap as illegal.  As it happens, this also gives the environment an opportunity to supply its own alternate implementation if it wants to, much like ECALL, but without intermixing semihosting calls with regular system calls.

A possible bit pattern for DCALL might be

    0000000 00000 00000 000 00001 1110011

This looks like an existing ECALL, except with rd = x1.  If I'm not mistaken, this pattern is currently illegal, and must be trapped as such; hence, that part of the behavior is nothing new.  I'm making use of the assumption that no ECALL variant will ever have a real register destination, so such an instruction will remain permanently illegal, except for its proposed use as DCALL.  Another advantage of this particular bit pattern is its close affinity with EBREAK, which, as we know, a debug module is already likely to support hooking.

Anybody who intends to complain that rd shouldn't be nonzero when we're not writing a register should consider that this very bit pattern can already exist in the instruction stream, with the required consequence of trapping as an illegal instruction.  Using this pattern for DCALL merely causes the debug module to intercept the illegal instruction trap.  No other hardware consequences are involved beyond this intercept.

Incidentally, I think a case could also be made for a DBREAK in parallel with EBREAK, same as DCALL/ECALL.  However, the need for DBREAK is much weaker than DCALL, and furthermore support for DBREAK would be incompatible with existing practice at this point.  So, while it would be more orthgonal to have both DCALL and DBREAK, I'm advocating for DCALL alone.

    - John Hauser

John Hauser

unread,
Jan 1, 2018, 2:50:10 AM1/1/18
to RISC-V ISA Dev
Krste wrote:
I agree making a single starting parcel (16 bits) of all ones be
illegal is probably preferable, even if it constrains longer
instruction encodings, as this is the smallest unit of instruction
fetch.

I confess I don't understand what problem we're trying to solve here.

For a RISC-V without the C extension, the ISA specification should say that a 32-bit instruction of all 0s is required to remain illegal regardless of any nonstandard additions to the instruction set.  Similarly, it should say that, for any RISC-V, an ILEN-size instruction of all 1s is required to be illegal regardless of any nonstandard additions to the instruction set.  I don't see a big win in requiring that a leading 16-bit parcel of all 1s counts as an illegal instruction by itself.  I see potential implementation costs in exchange for questionable gains.

Also, many illegal instructions don't have their own names, and will never have their own names, so I don't see necessarily why the illegal instruction of all 1s (or of 16 1s) needs a name.  This is a long thread, so I may have missed it:  Was somebody proposing using this bit pattern in a special way that would necessitate giving it a name in the disassembler?

    - John Hauser

kr...@berkeley.edu

unread,
Jan 1, 2018, 9:12:21 AM1/1/18
to John Hauser, RISC-V ISA Dev

>>>>> On Sun, 31 Dec 2017 23:50:10 -0800 (PST), John Hauser <jhause...@gmail.com> said:
| Krste wrote:
| I agree making a single starting parcel (16 bits) of all ones be
| illegal is probably preferable, even if it constrains longer
| instruction encodings, as this is the smallest unit of instruction
| fetch.

| I confess I don't understand what problem we're trying to solve here.

This was a tangent from the debug semihosting discussion, where the
fact that we hadn't cleanly defined what "all 1s are illegal
instructions" really means became apparent.

One real issue for hardware implementations is when they can raise an
instruction access exception (page fault or permissions fail) versus
an illegal instruction exception. It's clear we must allow
implementations to raise the access exception for any portion of an
<=ILEN instruction, before raising an illegal instruction exception.
This is true for any illegal prefix not just 16 ones or zeros. In
fact, for implementations that write instruction bits to mtval on an
illegal instruction exception, it is effectively required (at least
for the first XLEN bits if XLEN<ILEN), and maybe should be mandated
for those implementations.

| For a RISC-V without the C extension, the ISA specification should say that a
| 32-bit instruction of all 0s is required to remain illegal regardless of any
| nonstandard additions to the instruction set.

It is a bit odd for the standard to require what to do when you're
violating the standard. If an implementation doesn't support the C
extension, then any 32-bit instruction without 11 in the two LSBs is
illegal. The standard can recommend that non-compliant encodings
avoid using 32-bits of all 0. The standard does mandate that a 16-bit
instruction of all 0s is always an illegal instruction, so 32-bits of
zero are two 16-bit illegal instructions.

| Similarly, it should say that,
| for any RISC-V, an ILEN-size instruction of all 1s is required to be illegal
| regardless of any nonstandard additions to the instruction set.

The standard isn't crystal clear here, but again, at best the standard
can recommend (not require) that non-standard implementations don't
use all 1s as a valid instruction.

| I don't see a
| big win in requiring that a leading 16-bit parcel of all 1s counts as an
| illegal instruction by itself.  I see potential implementation costs in
| exchange for questionable gains.

Yes, the discussion has come around full circle. I don't think there
is actually any need to tighten to say an initial parcel of 16-bits of
all 1s is always illegal (so the current Figure 1.1 remains correct).
In practice, this portion of the encoding space will already be
illegal for any standard-conforming implementation with less then
gigantic instructions. The main clarification is to say that ILEN
bits of all 1s is always illegal (we should revise recent change to
spec to say any encodings with bits[15:0] equal to 1 are illegal -
though this only affects very long instruction encodings in practice).

| Also, many illegal instructions don't have their own names, and will never have
| their own names, so I don't see necessarily why the illegal instruction of all
| 1s (or of 16 1s) needs a name.  This is a long thread, so I may have missed
| it:  Was somebody proposing using this bit pattern in a special way that would
| necessitate giving it a name in the disassembler?

It was more that 16-bits of 0 was being used as a marker in the EBREAK
semihosting convention, and was already explicitly defined as illegal,
so seemed to warrant a name.

The disassembler does need to determine how long an instruction is,
even if it is illegal and doesn't have a name, if only to print
something useful in text disassembly and have some hope of recovering
if legal instructions are found again.

Strings of all 1s in memory correspond to illegal instructions of
length ILEN, as far as target instruction fetch unit is concerned.
For disassembler, as ILEN is not necessarily knowable from the
existing ELF tags (and might even vary for same binary library file
linked by two different ILEN targets), we can just print out as
illegal 16-bit or 32-bit instructions.

Krste

|     - John Hauser

| --
| You received this message because you are subscribed to the Google Groups
| "RISC-V ISA Dev" group.
| To unsubscribe from this group and stop receiving emails from it, send an email
| to isa-dev+u...@groups.riscv.org.
| To post to this group, send email to isa...@groups.riscv.org.
| Visit this group at https://groups.google.com/a/groups.riscv.org/group/isa-dev/
| .
| To view this discussion on the web visit https://groups.google.com/a/
| groups.riscv.org/d/msgid/isa-dev/
| 16302c76-23a9-4370-b582-628e7b3e14c6%40groups.riscv.org.
It is loading more messages.
0 new messages