rust-embedded / cortex-m-semihosting Goto Github PK
View Code? Open in Web Editor NEWSemihosting for ARM Cortex-M processors
License: Apache License 2.0
Semihosting for ARM Cortex-M processors
License: Apache License 2.0
I'm not expert with embedded, but why is semihosting so slow? Single characters appear on the host stdio taking some 100s of ms for each one. I know that semihosting is slow in general, but I hadn't any issue using it from Ada which uses the following implementation:
package body System.Semihosting is
type SH_Word is new Interfaces.Unsigned_32;
function To_SH_Word is new Ada.Unchecked_Conversion
(Source => System.Address, Target => SH_Word);
function Generic_SH_Call (R0, R1 : SH_Word) return SH_Word;
-- Handles the low-level part of semihosting, setting the registers and
-- executing a breakpoint instruction.
subtype Syscall is SH_Word;
SYS_WRITEC : constant Syscall := 16#03#;
SYS_WRITE0 : constant Syscall := 16#04#;
SYS_READC : constant Syscall := 16#07#;
-- Output buffer
-- Because most of the time required for semihosting is not consumed for
-- the data itself but rather in the handling of breakpoint and
-- communication between the target and debugger, sending one byte costs
-- almost as much time as sending a buffer of multiple bytes.
--
-- For this reason, we use an output buffer for the semihosting Put
-- functions. The buffer is flushed when full or when a line feed or NUL
-- character is transmitted.
Buffer_Size : constant := 128;
type Buffer_Range is range 1 .. Buffer_Size;
Buffer : array (Buffer_Range) of Unsigned_8;
Buffer_Index : Buffer_Range := Buffer_Range'First;
procedure Flush;
-- Send the content of the buffer with semihosting WRITE0 call
---------------------
-- Generic_SH_Call --
---------------------
function Generic_SH_Call (R0, R1 : SH_Word) return SH_Word is
Ret : SH_Word;
begin
Asm ("mov r0, %1" & ASCII.LF & ASCII.HT &
"mov r1, %2" & ASCII.LF & ASCII.HT &
"bkpt #0xAB" & ASCII.LF & ASCII.HT &
"mov %0, r0",
Outputs => (SH_Word'Asm_Output ("=r", Ret)),
Inputs => (SH_Word'Asm_Input ("r", R0),
SH_Word'Asm_Input ("r", R1)),
Volatile => True,
Clobber => ("r1, r0"));
return Ret;
end Generic_SH_Call;
-----------
-- Flush --
-----------
procedure Flush is
Unref : SH_Word;
pragma Unreferenced (Unref);
begin
if Buffer_Index /= Buffer'First then
-- Set null-termination
Buffer (Buffer_Index) := 0;
-- Send the buffer with a semihosting call
Unref := Generic_SH_Call (SYS_WRITE0, To_SH_Word (Buffer'Address));
-- Reset buffer index
Buffer_Index := Buffer'First;
end if;
end Flush;
---------
-- Put --
---------
procedure Put (Item : Character) is
Unref : SH_Word;
pragma Unreferenced (Unref);
C : Character with Volatile;
-- Use a volatile variable to avoid compiler's optimization
begin
if Item = ASCII.NUL then
-- The WRITE0 semihosting call that we use to send the output buffer
-- expects a null terminated C string. Therefore it is not possible
-- to have an ASCII.NUL character in the middle of the buffer as this
-- would truncate the buffer.
--
-- For this reason the ASCII.NUL character is sent separately with a
-- WRITEC semihosting call.
-- Flush the current buffer
Flush;
-- Send the ASCII.NUL with a WRITEC semihosting call
C := Item;
Unref := Generic_SH_Call (SYS_WRITEC, To_SH_Word (C'Address));
else
Buffer (Buffer_Index) := Character'Pos (Item);
Buffer_Index := Buffer_Index + 1;
-- Flush the buffer when it is full or if the character is a line
-- feed.
if Buffer_Index = Buffer'Last or else Item = ASCII.LF then
Flush;
end if;
end if;
end Put;
---------
-- Put --
---------
procedure Put (Item : String) is
begin
for Index in Item'Range loop
Put (Item (Index));
end loop;
end Put;
---------
-- Get --
---------
procedure Get (Item : out Character) is
Ret : SH_Word;
begin
Ret := Generic_SH_Call (SYS_READC, 0);
Item := Character'Val (Ret);
end Get;
end System.Semihosting;
I know that just pasting the Ada implementation may not be helpful, but unfortunately I'm myself a rookie at both languages and embedded programming (although the code is quite natural to read). I just checked this lib implementation and it already uses a buffer, so I don't what might be the issue. The asm instructions seem also to be the similar although Ada does something more with the registers, but don't trust me.
If syscall!
fails and returns -1 inside write_all
, it crashes because the write_all loop still tries to calculate the new buffer offset. Maybe syscall!
should return isize
(or c_int
?). Gdb seems to report back -1 if a hostio call fails (just like libc does on the host).
For some reason all writes to stdout fail for me (syscall returning -1), while stderr works fine. (SAM3X, black magic probe 1.6, gdb 7.10.1, rustc nightly 2017-05-01)
The crate panic_semihosting
still crashes with JLink.
cortex-m-semihosting never initializes stdin, stdout, and stderr with open()
. As such, it never hears from gdb that the file descriptors being used for those are not the traditional 0/1/2 (in my case, gdb is giving out 1/2/3), resulting in the wrong stream being used.
This works for me:
let _stdin = unsafe{ syscall!(OPEN, ":tt".as_bytes().as_ptr(), 0, 3) };
let _stdout = unsafe{ syscall!(OPEN, ":tt".as_bytes().as_ptr(), 4, 3) };
let _stderr = unsafe{ syscall!(OPEN, ":tt".as_bytes().as_ptr(), 8, 3) };
let buffer = "Hello, world!\n";
unsafe{ syscall!(WRITE, _stdout, buffer.as_ptr(), buffer.len()) };
We need something akin to newlib's initialise_monitor_handles
to actually send the OPEN syscalls and record the file descriptors in use.
i am following rust tutorial https://rust-embedded.github.io/bookshelf/book/start/hardware.html to start with embedded programming.
my hardware is NUCLEOF334R8 with STM32F334R8T6
i am running into problem with debugging against openocd when running simple "Hello world".
gdb> target remote :3333
results into this error:
cortex_m_semihosting::hio::HStderr::write_all ( self=0x80002eb, buffer=...) at /home/soni/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-semihosting-0.3.1/src/hio.rs:48 48 open(":tt\0", nr::open::W_APPEND).map(|fd| HStderr { fd }
your help would be appreciated
thanks
The hprint{ln}
macros currently all return a Result
to the caller. There are two reasons for why this is not ideal:
print{ln}
macros in libstd, which return nothing (()
).Err
. This is not the case as semihosting calls will just crash the application when no debugger is attached (it would be great to fix that regardless).Hello,
While going trough tutorial I’ve encountered the following problems:
The second one is quite hostile for beginners.
I have run into the issue that #35 was meant to fix.
Environment:
Example: https://github.com/nickray/lpc55s6x-hal/blob/master/examples/semihosting.rs
(gdb) c
Continuing.
uuid = [panicked at '
Program received signal SIGTRAP, Trace/breakpoint trap.
0x000024ae in __bkpt ()
For now, the fix in that pull request fixed it:
(gdb) c
Continuing.
uuid = [c8ded018, 55025d23, 906643bc, 3e3c4a61]
Using openocd is not an option, as it currently has no support for the chip AFAIK.
Hi everyone,
I am following this tutotial: https://rust-embedded.github.io/book/start/hardware.html
but for stm32l475.
First of all, I changed the memory.x for stm32l475:
/* Linker script for the STM32L475VG*/
MEMORY
{
/* NOTE 1 K = 1 KiBi = 1024 bytes */
FLASH : ORIGIN = 0x08000000, LENGTH = 1000K /*1MBYTE*/
RAM : ORIGIN = 0x20000000, LENGTH = 128K /*128KBYTE*/
}
The source code of the program which I want to run on stm32 chip is:
hello.rs
use cortex_m_rt::entry;
use cortex_m_semihosting::{hprintln};
#[entry]
fn main() -> ! {
hprintln!("Hello, world!").unwrap();
loop {}
}
Then, I run these commands:
For compilation:
cargo run --example hello
For openocd:
openocd -f interface/stlink-v2-1.cfg -f target/stm32l4x.cfg
In another terminal, I run the gdb program and commands for flush etc.:
$ gdb-multiarch -q target/thumbv7em-none-eabi/debug/examples/hello
$ (gdb) target remote :3333
$ (gdb) load
$ (gdb) monitor arm semihosting enable
$ semihosting is enabled
$ (gdb) continue
Continuing.
^ the program stucks at this point.
Afterwards, I terminated the program, and Ι reopened a new gdb process.
Ι gave the same commands until the command "monitor arm semihosting enable".
At this point, I set breakpoint to the main function.
$ break main
$ jump main
Line 33 is not in `HardFault_'. Jump anyway? (y or n) y
Continuing at 0x8000708.
Note: automatically using hardware breakpoints for read-only addresses.
Breakpoint 1, main () at examples/hello.rs:33
$ continue
$Continuing...
Again, the program stucks at this point.
I pressed Ctrl-C and it printed out the command that the program execution is blocked:
0x080008f0 in cortex_m_semihosting::hio::hstdout () at /home/tasosxak/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-semihosting-0.3.2/src/hio.rs:53
53 open(":tt\0", nr::open::W_TRUNC).map(|fd| HStdout { fd })
OS : Ubuntu 18.04.1 LTS
rustc version : rustc 1.34.0-nightly (146aa60f3 2019-02-18)
cargo .config file:
[target.thumbv7em-none-eabi]
//noth
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
//noth
rustflags = ["-C", "link-arg=-Tlink.x"]
[build]
target = "thumbv7em-none-eabi"
I've disabled the semihosting demo for thumbv8m-main.none-eabi in rust-embedded/cortex-m-rt#182 until we can push out a release for this crate.
After changing my project to use lld, the semi hosting calls don't go through it hangs on the __syscall(_nr, _arg)
in lib.rs:150
. I switched back to gcc to confirm they still work.
Flags for rust-lld
rustflags = ["-C", "link-arg=-Tlink.x"]
Flags for GCC
rustflags = [
"-C", "link-arg=-Tlink.x",
"-C", "linker=arm-none-eabi-ld",
"-Z", "linker-flavor=ld",
"-Z", "thinlto=no",
]
Edit:
I should mention I am only using panic-semihosting, I haven't tested normal semihosting yet.
You could use just
tail -f /tmp/openocd.log
At least in the presence of multiple cores sharing the same set of static
s. Problematic code:
cortex-m-semihosting/src/export.rs
Lines 9 to 19 in 7a961f0
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.