rust-embedded / rust-raspberrypi-os-tutorials Goto Github PK
View Code? Open in Web Editor NEW:books: Learn to write an embedded OS in Rust :crab:
License: Apache License 2.0
:books: Learn to write an embedded OS in Rust :crab:
License: Apache License 2.0
I think there's an error in reading the 64 bit value in delays::get_system_timer
. I would expect to read hi, read lo, read hi again and if different (because lo has wrapped since our first read) to read lo again (which can't now wrap because it is near zero).
In your code, if lo wraps after the if but before the read of lo, you get a bogus result.
Hi,
I'm quite new in embedded development and I had to figure out how to start all of the cores.
const CORE_1_START: *mut u32 = (0xe0) as *mut u32;
const CORE_2_START: *mut u32 = (0xe8) as *mut u32;
const CORE_3_START: *mut u32 = (0xf0) as *mut u32;
write_volatile(CORE_1_START, 0x80000);
write_volatile(CORE_2_START, 0x80000);
write_volatile(CORE_3_START, 0x80000);
Hope it helps someone, it would be nice if I could read somewhere.
(I spent hours because I read an outdated documentation)
Cheers,
Attila
In #51, the READMEs got updated. If you like, you can have a look.
Also, there is https://github.com/rust-embedded/rust-raspberrypi-OS-tutorials/blob/master/00_before_we_start/README.md now.
Best,
Andre
Hello! Thanks again for this tutorial, been learning quite a lot.
I cloned the repository and checked into one of the lessons (lesson 6) where we connect to the raspberrypi 4.
I ran the following command as instructed in the README.md
sudo gem install bundler
bundle config set path '.vendor/bundle'
bundle install
I continuously get the following error. I have attempted to install ruby dev tools etc, to no avail.
me@me /me.../rust-raspberrypi-OS-tutorials/06_drivers_gpio_uart >
bundle install
Fetching gem metadata from https://rubygems.org/.........
Resolving dependencies...
Using ast 2.4.2
Using bundler 2.2.11
Using colorize 0.8.1
Using parallel 1.20.1
Using rainbow 3.0.0
Using regexp_parser 2.0.3
Using rexml 3.2.4
Using ruby-progressbar 1.11.0
Using unicode-display_width 2.0.0
Using parser 3.0.0.0
Using rubocop-ast 1.4.1
Fetching serialport 1.3.1
Using rubocop 1.10.0
Installing serialport 1.3.1 with native extensions
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.
current directory:
/me.../rust-raspberrypi-OS-tutorials/.vendor/bundle/ruby/2.6.0/gems/serialport-1.3.1/ext/native
/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/bin/ruby -I /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0 -r
./siteconf20210222-79147-1k74z03.rb extconf.rb
checking for OS... darwin
checking for termios.h... *** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers. Check the mkmf.log file for more details. You may
need configuration options.
Provided configuration options:
--with-opt-dir
--without-opt-dir
--with-opt-include
--without-opt-include=${opt-dir}/include
--with-opt-lib
--without-opt-lib=${opt-dir}/lib
--with-make-prog
--without-make-prog
--srcdir=.
--curdir
--ruby=/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/bin/$(RUBY_BASE_NAME)
/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:467:in `try_do': The compiler failed to generate an executable file.
(RuntimeError)
You have to install development tools first.
from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:585:in `block in try_compile'
from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:534:in `with_werror'
from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:585:in `try_compile'
from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:1109:in `block in have_header'
from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:959:in `block in checking_for'
from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:361:in `block (2 levels) in postpone'
from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:331:in `open'
from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:361:in `block in postpone'
from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:331:in `open'
from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:357:in `postpone'
from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:958:in `checking_for'
from /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/ruby/2.6.0/mkmf.rb:1108:in `have_header'
from extconf.rb:10:in `<main>'
To see why this extension failed to compile, please check the mkmf.log which can be found here:
/me.../rust-raspberrypi-OS-tutorials/.vendor/bundle/ruby/2.6.0/extensions/universal-darwin-19/2.6.0/serialport-1.3.1/mkmf.log
extconf failed, exit code 1
Gem files will remain installed in
/me.../rust-raspberrypi-OS-tutorials/.vendor/bundle/ruby/2.6.0/gems/serialport-1.3.1 for
inspection.
Results logged to
/me.../rust-raspberrypi-OS-tutorials/.vendor/bundle/ruby/2.6.0/extensions/universal-darwin-19/2.6.0/serialport-1.3.1/gem_make.out
An error occurred while installing serialport (1.3.1), and Bundler cannot continue.
Make sure that `gem install serialport -v '1.3.1' --source 'https://rubygems.org/'` succeeds before bundling.
In Gemfile:
serialport
me.../rust-raspberrypi-OS-tutorials/06_drivers_gpio_uart >
Here is the output of the mkmf.log
"xcrun clang -o conftest -I/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/universal-darwin19 -I/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/backward -I/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT -g -Os -pipe -DHAVE_GCC_ATOMIC_BUILTINS -DOS_DARWIN conftest.c -L. -L/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib -L. -L/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.Internal.sdk/usr/local/lib -arch x86_64 -lruby.2.6 "
In file included from conftest.c:1:
In file included from /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby.h:33:
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/ruby.h:24:10: fatal error: 'ruby/config.h' file not found
#include "ruby/config.h"
^~~~~~~~~~~~~~~
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/ruby.h:24:10: note: did not find header 'config.h' in framework 'ruby' (loaded from '/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/System/Library/Frameworks')
1 error generated.
checked program was:
/* begin */
1: #include "ruby.h"
2:
3: int main(int argc, char **argv)
4: {
5: return 0;
6: }
/* end */
Not sure if you know what to do with such an error
OS: MacOS 10.15.7
Raspberry PI: 4
Rust ToolChain
stable-x86_64-apple-darwin
nightly-2021-01-08-x86_64-apple-darwin (override)
nightly-x86_64-apple-darwin (default)
I only have an raspberry pi 2b,I wonder if this project can run on pi 2b?
Hi,
I encountered quite a weird issue in 07_uart_chainloader. After starting make chainboot
, I never got the desired output
[MP] ✅ Connected
__ __ _ _ _ _
| \/ (_)_ _ (_) | ___ __ _ __| |
| |\/| | | ' \| | |__/ _ \/ _` / _` |
|_| |_|_|_||_|_|____\___/\__,_\__,_|
Raspberry Pi 3
[ML] Requesting binary
[MP] ⏩ Pushing 6 KiB ==========================================🦀 100% 0 KiB/s Time: 00:00:00
[ML] Loaded! Executing the payload now
[0] Booting on: Raspberry Pi 3
[1] Drivers loaded:
1. BCM GPIO
2. BCM PL011 UART
[2] Chars written: 93
[3] Echoing input now
Instead, I frequently got one of the following outputs:
[MP] ✅ Connected
[MP] ⚡ Protocol Error: Remove and insert the USB serial again
[MP] ✅ Connected
?? $Error ? ?$Error ?
[MP] ⚡ Protocol Error: Remove and insert the USB serial again
[MP] ✅ Connected
ting the payload now
[ML] Loaded! Executing the payload now
[ML] Loaded! Executing the payload now
[ML] Loaded! Executing the payload now
<repeats until I stop>
[MP] Bye 👋
[MP] ✅ Connected
?????????????????????????????????????????????????????????????????????????????????????
?????????????????????????????????????????????????????????????????????????????????????
?????????????????????????????????????????????????????????????????????????????????????
<repeats until I stop>
[MP] Bye 👋
After several hours I finally figured out how to work around the issue:
It always works if I do it like this:
make chainboot
[MP] ✅ Connected
gets displayedI don't really understand the issue in detail, but to me it seems like it's somehow connected to timings/delays. The only place in the code I could find that directly influences said timings/delays is const DELAY: usize = 2000
in src/bsp/device_driver/bcm/bcm2xxx_gpio.rs. I tried some values here and if I set it to const DELAY: usize = 20000
, it also works sometimes (but at this point I have no clue if the change really reduces my error rate, or if it just happened by chance and led me to wrong conclusions).
Do you have any idea what really causes the error? If so, I would appreciate if you could provide a little write-up what is happening on the lower levels and what causes the error I got. Since chasing this down took more than half of my day but I still didn't really understand it, I'm quite curious now 😄
Let me first thank you for this fantastic tutorial. I'm having loads of fun going through it all and the code is just beautifully organized!
I have some problems on chapter 6 (which, by excluding everything else is probably due to a broken SD card) and I had to go through every line of code and double check everything.
This is nitpicking since the result is still within ~5 % error range of the tx/rx channel which should be acceptable anyway but since this is a tutorial it might still be worth bringing up:
in bcm20xx_pl011_uart.rs
we have the PL011UartInner::init
method which sets up UART.
The calculations for the baud rate divisor given a target baud rate of 230400
is as follows: (48_000_000/16)/230400 = 13,02083
. The integer part is 13
is written to the IBRD
bitfield. However, the fractional part is written to a the 6-bit field FBRD
. Now, as I understand it, since the fractional field can only hold values from 0-63, the right thing to do is to multiply the decimal fractional by 64 (in contrast to 100): 0,0208*64 = 1,3312
which gives a nearest value of 1 instead of 2.
As you might understand I'm on the learning path here so I'm wary of making too bald claims about the correctness but I spent quite some time to check how you got the numbers you got and I leaned on this resource - 11.2.2. TM4C UART Details since I wondered what would be the right thing to do if the fractional was a value higher than 63.
I know this is very nit picky but it would be interesting to add at least the basic calculations in the comments for the code block as well since it's not easy to understand and it really does connect some dots that are hard to find otherwise.
Again, thanks for creating this great resource.
Adding some people to whom it might be of interest. You are kindly invited to review, or just silently ignore ;)
@alevy @bradjc @niklasad1 @brghena @phil-opp @ryankurte @ppannuto
Hi all,
In tock/tock#930, I started my idea of having a common register interface for both CPU and MMIO registers based on the tock register interface. Thank you very much to the tock folks for carving out a crate for it!
Today, I finally have a first peek ready for one of my RaspberryPI 3 subprojects.
Check it out at https://github.com/andre-richter/rust-raspi3-tutorial/tree/registers/09_delays
There are some distributed components involved.
In the end, you can see, for example in https://github.com/andre-richter/rust-raspi3-tutorial/blob/registers/09_delays/src/delays.rs, that MMIO and CPU accesses are side by side, using the same tock-regs API. Very very neat!
Is it possible to debug using the Serial Debug Cable rather than using a dedicated JTAG?
I have a problem when trying 06_drivers_gpio_uart.
I got following error.
$ DEV_SERIAL=/dev/tty.usbserial-1420 make miniterm
Miniterm 1.0
[MT] ⚡ Unexpected Error: #<Errno::EINVAL: Invalid argument - tcsetattr>
[MT] Bye 👋
I think the cause of the error is baud
.
The baud is depending on the platform according to the documentation.
I'm using Mac OS.
Integer from 50 to 256000, depending on platform)
https://rubydoc.info/gems/serialport/SerialPort:set_modem_params
The current value is 576000
Hi, thanks for this amazing tutorials.
For now I'm following the 06_drivers_gpio_uart. Reading the bcm2xxx_pl011_uart driver, I looked at which registers/field were not used in this tutorial, and I found a BUSY flag in the FR register. According to the BCM2711 ARM Peripherals:
UART busy. If this bit is set to 1, the UART is busy
transmitting data. This bit remains set until the complete
byte, including all the stop bits, has been sent from the shift
register.
This bit is set as soon as the transmit FIFO becomes non-
empty, regardless of whether the UART is enabled or not.
I don't know much about how UART works, so I may be mistaken, but I think that is the hardware equivalent of the calculations made in PL011UartInner::flush. So instead of waiting that the TX FIFO is empty and then waiting for some time, we could just wait for the BUSY flag to be 0.
If that is true, this allow us to make this code independent of baud rate, the number of bits to be sent and the CPU frequency; and make us sure that the UART is flushed properly.
I know that the Redox OS(1) only supports the x86-64 architecture(2), but using a Redox kernel on a Raspberry Pi Zero (w/wo WiFi) could be really interesting.
I know that a Raspberry Pi Zero has a ARM1176JZF-S (32-bit) ARMv6 CPU, but it has 512 MB of memory, and that is a LOT of memory in the embedded world, and when there is the price tag (US$ 10 in the US) for a RPI Zero ...
ad (1)
Redox OS
Hey. Firs of all, thanks for this great tutorial. Is really fun & interesting!!
As for the problems I'm experiencing:
BSP=rpi4 make
and copied kernel8.img
together with fixup4.dat
, start4.elf
(renamed to start.elf
as per README comment) and bcm2711-rpi-4-b.dtb
and config.txt
with the specified contents to the SD.sudo screen /dev/ttyUSB0 230400
. Black screen appears and then after pressing Enter
nothing happens.I tried with start4.elf
and doesn't work. Also tried to download the latest firmware and same outcome..
BSP=rpi4 make
and copied the kernel8.img
to the SD.BSP=rpi4 make chainboot
and this was the outcome:BSP=rpi4 make chainboot
Minipush 1.0
[MP] ✅ Connected
[MP] ⚡ Protocol Error: Remove and insert the USB serial again
[MP] ⏳ Waiting for /dev/ttyUSB0
[MP] ✅ Connected
[MP] ⚡ Protocol Error: Remove and insert the USB serial again
[MP] ⏳ Waiting for /dev/ttyUSB0
[MP] ✅ Connected
[MP] ⚡ Protocol Error: Remove and insert the USB serial again
[MP] ⏳ Waiting for /dev/ttyUSB0
[MP] ✅ Connected
[MP] ⚡ Protocol Error: Remove and insert the USB serial again
^C
[MP] Bye 👋
The error is very similar to what I've seen in #79 . I checked whether I had everything correct with the toolchains and targets and the rest of the config. But since I've seen new commits have been done since this issue was close, maybe one of them broke this.
If you need any kind of extra info, just ping me. Sorry that I cannot provide more, but I don't really know how to debug it.
EL
. Maybe other fields as well?additional resources
section to end of README with links for using/learning GDB.Tutorial still lives in branch JTAG
CC @naotaco if you like to help.
This is not a big problem, but I was wondering if anybody knows. The code from the repository is working without problems, but when I remove the --release flag compile and run it. Qemu loads the kernel but nothing is written to the terminal.
Is this a bug in the rust compiler?
Would that make sens to make this tutorial portable to older model like the raspi2 B+ ?
I have thoroughly enjoyed your tutorials, but I couldn't get the MiniLoad to work over the UART correctly. I spent a while debugging (and learned a ton in the process), and tracked the issue down to this section:
unsafe {
// Read the kernel byte by byte.
for i in 0..size {
*kernel_addr.offset(i as isize) = console().read_char() as u8;
// console().read_char() as u8;
}
}
(from main.rs)
If I comment out the memory assignment and just throw away each byte as I read it (the commented line) then all is well, except that obviously it doesn't actually boot the new kernel. If I leave the code as shown however, the PI 4 will hang (it will never complete this loop) and I get no information back on why. I am assuming that it is some kind of memory protection issue and perhaps it is new to the latest PI 4 version? Anyway, I don't know how to get around this and I'm hoping some brighter spark out there might. If it matters, I am cross-compiling from a Mac running Mac OS X 10.15.7 and using rustc 1.49.0-nightly (25c8c53dd 2020-10-03)
Hello,
In part 7: https://github.com/rust-embedded/rust-raspberrypi-OS-tutorials/tree/master/07_uart_chainloader
I cannot understand how the kernel can be correctly loaded, even if you set this in the linker file:
- /* Set current address to the value from which the RPi starts execution */
- . = 0x80000;
+ /* Set the link address to 32 MiB */
+ . = 0x2000000;
(Correct me if I'm mistaken) when you set .=0x2000000
the kernel will be loaded at that position in memory.
Then as soon as GPU initialization is done, the cpu will start running instruction at location 80_000. But how can this works if the kernel is in position 32Mib?
I know we could overwrite the kernel start location by putting kernel_address=0x4000000
inside the config.txt, but since this wasn't requested in the tutorial, I didn't do it but still it's working and also nicely verifiable via qemuasm
.
How is this possible? I think it has something to do with .got section you added in the linker, can I have some more insight on that?
Thanks!
I'm uncertain if a github pull request or an issue (or a mailing list?) is appropriate for this so I'm just putting it here. Please let me know if it needs to go somewhere else.
Based on my own experience working through the tutorials I think a small paragraph about docker might be desirable. Here's a possible suggestion:
Docker
To facilitate downloading, building and running the required development tools some of the tutorials in this repository use a piece of software called docker. While docker specifics are outside of this tutorial's scope it is worth mentioning that human readable docker configuration files named "Dockerfile" exist in various repository sub-directories. These files may be of interest to those who want details about what will happen when docker is invoked.
Hello!
In the step 05_safe_globals, I've tried to add the following code in the runtime_init/bss_range function:
26 | panic!("Bss start:{:?}, end:{:?}", __bss_start, __bss_end);
This is the resulting output:
Kernel panic: Bss start:26, end:0
I've tried the same in step 09_timestamps:
Kernel panic: Bss start:0, end:0
Additional background:
make qemu
rust-toolchain
with "nightly" in those directories, because I'm too lazy to switch rustup default.Any clues on what's going on or how to fix this? Thanks!
The output of diff_all.rb
that is stored in the readmes is missing some special characters. E.g. double escapes with backslashes are swalloed \\
, and it also has hickups around single quotes '
.
Cannot do final step in [Prerequisites]:
cmd: rustup component add clippy-preview --toolchain=nightly
error: component 'clippy' for target 'x86_64-unknown-linux-gnu' is unavailable for download for channel 'nightly'
First of all thanks for this tutorial, it's a great start for programming a Raspberry Pi with embedded Rust!
Now to my problem: I am working on Windows 10, and I managed to build the 06_drivers_gpio_uart
example kernel without the supplied Makefile. I verified that the kernel8.img
runs in QEMU, but then I had problems to boot it on a real Raspberry Pi 3 B+.
The instructions in the 06_drivers_gpio_uart/README.md
say this:
- On the card, generate a file named config.txt with the following contents:
init_uart_clock=48000000
But with such a config.txt
file the kernel doesn't boot for me.
After looking at some other bare metal kernel examples for the Raspberry Pi I found the arm_64bit
option, and with that the kernel boots on my Raspberry Pi 3 B+. This is the config.txt
file that works for me:
arm_64bit=1
init_uart_clock=48000000
kernel=kernel8.img
Is this an omission in the documentation of the 06_drivers_gpio_uart
example, or might I have made some mistake that caused the proposed config.txt
to fail in my case?
Hey 👋 and thanks again for this tutorials series.
I get these questions marks (see images below) after connecting my Raspberry Pi4 to power after completing this section of the tutorial. I've correctly setup the pins. I've loaded the correct files to the SDCard, and have even tried switching between start.elf
vs start4.elf
.
It seems that the Miniterm software does recognize that a device is connected, however it does not shown anything other than two questions marks once I connect the power. I have even cloned and used the tutorial that is in this repo just incase I made a typo or mistake anywhere.
me$ DEV_SERIAL=/dev/tty.usbserial-14220 make miniterm
Miniterm 1.0
me/kernel/.vendor/bundle/ruby/2.7.0/gems/serialport-1.3.1/lib/serialport.rb:25: warning: rb_secure will be removed in Ruby 3.0
[MT] ✅ Serial connected
��
Hi there,
I'm using a custom build target for my Raspberry Pi bare metal kernel. After setting everything up and compiling the binary kernel for unit testing I get the compiler error "can't find crate for test
".
Any clue what might be the root cause for this?
I'm not using cargo xbuild, but the new cross compile configuration in /.cargo/config like so:
[unstable]
build-std = ["core", "compiler_builtins", "alloc"]
Any help would be much appreciated.
Thanks in advance.
just use the virtual serial port like tty in linux, i make a simple python scirpt to do that. is't work, but I don’t know if there will be other problems
#! /usr/bin/env python3
#coding=utf-8
import pty
import os
import select
def mkpty():
# open tty
master1, slave = pty.openpty()
slaveName1 = os.ttyname(slave)
master2, slave = pty.openpty()
slaveName2 = os.ttyname(slave)
print ('\nslave device names: ', slaveName1, slaveName2)
return master1, master2
if __name__ == "__main__":
master1, master2 = mkpty()
while True:
rl, wl, el = select.select([master1,master2], [], [], 1)
for master in rl:
data = os.read(master, 128)
print ("read %d data." % len(data))
if master==master1:
os.write(master2, data)
else:
os.write(master1, data)
Hey,
I'm having the following problem when I try to execute make qemu
:
RUSTFLAGS="-C link-arg=-Tsrc/bsp/raspberrypi/link.ld -C target-cpu=cortex-a53 -D warnings -D missing_docs" cargo rustc --target=aarch64-unknown-none-softfloat --features bsp_rpi3 --release
Finished release [optimized] target(s) in 0.07s
Failed to execute tool: objcopy
No such file or directory (os error 2)
Makefile:62: recipe for target 'kernel8.img' failed
make: *** [kernel8.img] Error 101
I have no clue of what is wrong, I tried looking it up but can't find a fix. The objcopy
command also works fine in the terminal. I'm running the following configuration by the way:
Default host: x86_64-unknown-linux-gnu
rustup home: /home/aurelien/.rustup
installed toolchains
--------------------
stable-x86_64-unknown-linux-gnu
nightly-2019-12-20-x86_64-unknown-linux-gnu
nightly-x86_64-unknown-linux-gnu (default)
installed targets for active toolchain
--------------------------------------
aarch64-unknown-linux-gnu
aarch64-unknown-none-softfloat
x86_64-unknown-linux-gnu
active toolchain
----------------
nightly-x86_64-unknown-linux-gnu (default)
rustc 1.45.0-nightly (4bd32c980 2020-05-29)
Do I need to do something with the docker files? Thanks in advance :)
My environment is Ubuntu 18.04.3 LTS with docker-ce 19.03.2. I follow the README.md
and have install the suitable Rust toolchain.
When I run make qemu
, get this output:
rm -rf target
RUSTFLAGS="-C link-arg=-Tsrc/bsp/rpi/link.ld -C target-cpu=cortex-a53 -D warnings -D missing_docs" cargo xrustc --target=aarch64-unknown-none-softfloat --features bsp_rpi3 --release
error: The sysroot can't be built for the Stable channel. Switch to nightly.
note: run with `RUST_BACKTRACE=1` for a backtrace
Makefile:56: recipe for target 'target/aarch64-unknown-none-softfloat/release/kernel' failed
make: *** [target/aarch64-unknown-none-softfloat/release/kernel] Error 1
I checked the Makefile and don't know what to do.
Hi,
Not really an issue, I'd just like to say how much I like what you did with my little tutorials! Good job, well done! I've recommended your repo in the README in case someone is looking for Rust examples.
I was wondering if your 07_uart_chainloader example could be used with USBImager -S. That works with my 14_raspbootin64 example, so I think it should work with yours too out-of-the-box!
Cheers,
bzt
Hi, @andre-richter do you mind if I translate your project to Chinese? I think this is worth spreading.
First of all these tutorials are excellent. They are an excellent resource overall for Rust embedded work so thank you for that!
I have been working through some of these examples and I wanted to provide my 2 cents for implementation details for v2.
There are two main details that I found confusing or inefficient that I thought would be good to at least have documented somewhere so people understand trade offs.
I was curious on the design choice of using modules and set values instead of enum
s that held all the variants for things such as Tags within the mailbox api. (ex. https://github.com/r0ck3tAKATrashPanda/raspi-os/blob/master/src/bsp/rpi3/mbox.rs#L77)
Additionally, not that it affects anything, but mbox.buffer[4] (5th byte of the buffer) doesn't need to be filled out, apparently that is used by the VideoCore to send back the size of the response buffer. It should be parsed in order to determine if the size changed when the mail was responded too. Again this isn't an issue by any means, just hoping to make more information available to people about the mailbox API.
println!
macro and console()
- Currently in v2 you are using the magical QEMU structure to print. In the current implementation we have a console()
function that is building the magical structure each time it is called, which is every time print!
or println!
is called. Obviously this isn't an issue in QEMU and it is an empty struct, but this design has major performance implications when you move to hardware and start to use UART, the amount of overhead for re-initializing and the risk of having multiple consumers attempting to access the same peripheral is quite high. Instead this should be moved to a single static ref (maybe even for the QEMU struct for good practice early on in the process of learning)If we implement UART something like
lazy_static! {
pub static ref CONSOLE: Mutex<Uart> = {
let mut uart = Uart::new();
let mut mbox = mbox::Mbox::new();
{
uart.init(&mut mbox, 4_000_000);
}
Mutex::new(uart)
};
}
We A) don't initialize UART until we need it B) We have a mutexed (spin) UART access so we can println! from anywhere without worry (sorta).
Again, these tutorials are excellent and I am not trying to nit pick, but these are some pain points I ran into, that would be great if we could get covered in one way or another. It would be great to hear your thoughts and let me know if there is anything I can do to contribute!
I am trying 07_uart_chainloader
.
I found a problem on minipush.rb
.
It fails sometimes like below.
$ DEV_SERIAL=/dev/tty.usbserial-1420 BSP=rpi4 make chainboot
Minipush 1.0
[MP] ✅ Serial connected
[MP] 🔌 Please power the target now
__ __ _ _ _ _
| \/ (_)_ _ (_) | ___ __ _ __| |
| |\/| | | ' \| | |__/ _ \/ _` / _` |
|_| |_|_|_||_|_|____\___/\__,_\__,_|
Raspberry Pi 4
[ML] Requesting binary
[MP] ⚡ Connection or protocol Error: Remove power and USB serial. Reinsert serial first, then power
Hi,
Have you ever used a RPi4 as a host for a RiP3 as the target. In this case for me the makefile does not build the kernel. It just seems to silently fail.
Everything works fine for me if I use an X64 machine as the host.
Thanks
Regards
Tim.
I think there are two timing-related logic bugs in the implementation of delays.rs
in 09_delays. Each is a source of divergent behavior between the CPU-based timers and the BCM ones, such that delays::SysTmr::wait_msec_st(n)
and delays::wait_msec(n)
wait for different durations.
The first is in SysTmr::wait_msec_st
, which is documented to "Wait N microsec" using the Pi's BCM system timer (empirically this seems to increment monotonically at a rate of 1Mhz).
loop {
if self.get_system_timer() < (t + n) {
break;
}
}
I think this needs to be > (t + n)
. Since the timer increases monotonically, don't we want to break only when the system timer reaches t + n
? As written, this code will break in the very first iteration for any meaningful value of n
because the system timer will be < (t + n)
for the entire duration of the intended wait.
UPDATE: Looking at the original C source, I can see how this came to be. A more direct port of the [correct] original could be this:
while self.get_system_timer() < (t + n) {
asm::nop();
}
The second bug is an incorrect unit conversion in the delays::wait_msec
function. It's also documented to "Wait N microsec" but uses the ARM CPU timer. The conversion occurs in this line:
// Calculate number of ticks
let tval = (frq as u32 / 1000) * n;
Dividing by 1000 gives the number of ticks per millisecond but as n
is in microseconds, tval
winds up being "milliticks" rather than just ticks.
A more correct implementation would divide by 1,000,000:
// Calculate number of ticks
let tval = ((frq as u64) * (n as u64) / 1_000_000) as u32;
Funnily, this doesn't always result in 1000X longer waits. On my Pi hardware, where frq
is reported as 0x0124F800
= 19,200,000 Hz, calling delays::wait_msec(1_000_000)
causes tval
to overflow the 32-bit register and waits 2_020_130_816 ticks, or only 1m45s.
(Also, this version does the math in u64 for accuracy; the simpler (frq as u32 / 1000000) * n
implementation results delays::wait_msec(1_000_000)
ending nearly 11 milliseconds early due to integer truncation of 19.2
to 19
.)
When powering a RPi 4B externally (+5v power pin is disconnected) and running 'BSP=rpi4 make chainboot', the minipush timeout of 7 seconds occurs before the RPi has booted the kernel8.img and output what minipush is expecting. This causes minipush to display the generic "Protocol Error" message.
Increasing the timeout to 30 seconds allows the user to perform the following steps without the timeout occurring.
Please increase the default timeout to more than 7 seconds. Please note that pushing larger images appears to take more time during which the timeout can occur.
Aka making it a "runtime".
I am not able to make the UART example (06) work on my Raspberry Pi 4.
I started from an up-to-date Raspbian SD Card, deleted everything from the boot partition except:
Then compiled example 06 with BSP=rpi4 make
and copied kernel8.img to the SD. I also created the config.txt with content:
init_uart_clock=48000000
I know the UART cable is working since I enabled the bootloader UART (as per https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711_bootloader_config.md) and I'm getting the bootloader output.
However, I do not get any output from the Rust code, and I'm not sure how to debug. The last lines of the bootloader are:
Loading start4.elf hnd: 0x0002e101
Loading fixup4.dat hnd: 0x00033c3d
MEM GPU: 76 ARM: 948 TOTAL: 1024
FIXUP src: 128 256 dst: 948 1024
Starting start4.elf @ 0xfec00200
First of all, thank you for organizing this material. This is of great help for me to learn Rust.
I have a question about the change in "04_zero_overhead_abstraction" where assembly code is replaced with Rust.
In _start
function, there are at least two function calls (cpu::smp::core_id
and SP.set
) before the stack pointer is initialized. For this code to work, the Rust compiler has to inline these function calls.
The inline
attribute, however, is just a hint and, in my understanding, the Rust compiler can ignore the hint.
https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute
I feel like anything before the stack pointer initialization needs to be written in assembly to avoid function calls.
Curious to hear your thoughts.
I may be jumping the gun a little bit with this question....
Is there any special initialization code (raspi3_boot/etc) needed to enable interrupt handling outside of setting a particular IRQ enable bit in one of the 3 interrupt enable registers ( 0x7E00_B210, 214, 218)?
I'm trying to setup a DMA transfer that generates an IRQ when it finishes (by providing DMA Channel 0 with a control block that has INTEN bit set and enabling Irq 16 in IRQ Enable_1 register) but the handler I provide in the current_elx_irq vector never gets called.
I seem to be out of ideas. Any thoughts?
Thanks in advance.
I found this project in Google and am asserting that here is a good a place as any for this post.
I had an interesting discussion on https://youtu.be/cWuFLEb-pUM
The idea there was that since an Arduino does less it has some advantage over the less expensive PI zero W($20 cheeper). My point was that it would be entirely possible to have a PI do less. This repo very effectively demonstrates this, I think.
The challenge is to write something that looks like an Aurduino, but is a PI running a not so fancy OS.
Note to self.
When following the install scripts in the main README, i get the following warning:
bundle install --path .vendor/bundle
[DEPRECATED] The --path
flag is deprecated because it relies on being remembered across bundler invocations, which bundler will no longer do in future versions. Instead please use bundle config set path '.vendor/bundle'
, and stop using this flag
Ive had not problems with any of the first 5 tutorials but on Tutorial 6 I can't get the screen part to work.
I get Cannot exec '/dev/ttyUSB0': No such file or directory
I have tried ls /dev/ttyUSB*
and it doesn't return any results.
Can it have anything to do with using a USB->Thunderbolt adapter?
Any ideas?
It looks like I/O with the mailbox is not working after mmu::init
. This needs to be investigated.
Prevents issues like #10
Hi there,
Thanks for this effort -- it's amazing!
I'd just like to make a quick suggestion. It might be nice to add instructions for people who already have Rust installed, like myself. I had a lot of trouble getting started before I finally realised I needed to run rustup component add llvm-tools-preview
. I think this, plus instructions to switch to nightly
, would make a nice addition to "The tl;dr Version" of System Requirements for other people like myself. (Related: #57)
Happy to make a PR if this sounds good.
CC @rust-embedded/cortex-a in case someone feels bored and wants to do this exercise ;)
I am running into an interesting issue where a slightly modified version of 05_uart0 example keeps hanging on my rpi3 hardware.
I modified the example and for clarify moved code to get the board serial number into its own function. The function takes a slice (&mut [u32]) to fill in the part of mbox buffer that represents request data and uses it to collect response output.
The issue I'm running into is around how my slice gets copied into the mbox buffer.
If I simply do a loop and iterate over slice values, it works fine.
for i in 0..data.len() {
mbox.buffer[5 + i] = data[i];
}
However, if I use rust's copy_from_slice function, the program hangs
mbox.buffer[5..7].copy_from_slice(&data[0..2]);
This feels to me like some unaligned access issue but I'm not sure how to further debug it. Is there anything I'm doing wrong when trying to use copy_from_slice?
Thanks
-Seb
Here is the complete modified source code for main.rs.
#![no_std]
#![no_main]
#![feature(asm)]
const MMIO_BASE: u32 = 0x3F00_0000;
mod gpio;
mod mbox;
mod uart;
use core::sync::atomic::{compiler_fence, Ordering};
fn kernel_entry() -> ! {
let mut mbox = mbox::Mbox::new();
let uart = uart::Uart::new();
// set up serial console
match uart.init(&mut mbox) {
Ok(_) => uart.puts("\n[0] UART is live!\n"),
Err(_) => loop {
unsafe { asm!("wfe" :::: "volatile") }; // If UART fails, abort early
},
}
uart.puts("[1] Press a key to continue booting... ");
uart.getc();
uart.puts("Greetings fellow Rustacean!\n");
let mut data = [0, 0];
let serial_avail = get_serial(&mut mbox, &mut data[0..2]);
if serial_avail {
uart.puts("[i] My serial number is: 0x");
uart.hex(data[1]);
uart.hex(data[0]);
uart.puts("\n");
} else {
uart.puts("[i] Unable to query serial!\n");
}
// echo everything back
loop {
uart.send(uart.getc());
}
}
#[inline(never)]
fn get_serial(mbox: &mut mbox::Mbox, data: &mut [u32]) -> bool {
// get the board's unique serial number with a mailbox call
mbox.buffer[0] = 8 * 4; // length of the message
mbox.buffer[1] = mbox::REQUEST; // this is a request message
mbox.buffer[2] = mbox::tag::GETSERIAL; // get serial number command
mbox.buffer[3] = 8; // buffer size
mbox.buffer[4] = 8;
mbox.buffer[5..7].copy_from_slice(&data[0..2]);
//for i in 0..data.len() {
// mbox.buffer[5 + i] = data[i];
//}
//mbox.buffer[5] = data[0]; // clear output buffer
//mbox.buffer[6] = data[1];
mbox.buffer[7] = mbox::tag::LAST;
// Insert a compiler fence that ensures that all stores to the
// mbox buffer are finished before the GPU is signaled (which is
// done by a store operation as well).
compiler_fence(Ordering::Release);
// send the message to the GPU and receive answer
match mbox.call(mbox::channel::PROP) {
Err(_) => false,
Ok(()) => {
//data[0..2].copy_from_slice(&mbox.buffer[5..7]);
for i in 0..data.len() {
data[i] = mbox.buffer[5 + i];
}
//data[0] = mbox.buffer[5];
//data[1] = mbox.buffer[6];
true
},
}
}
raspi3_boot::entry!(kernel_entry);
According to https://github.com/RPi-Distro/raspi-gpio/blob/master/raspi-gpio.c#L194 and to BC2711 ARM Peripherals, there is no more a GPPUD0
register. Instead GPPUPPDN0
is the register to use. Additionally, the procedure for pull-up/pull-down activation does not use the GPPUDCLK0
for BCM2711.
if (is_2711)
{
int pullreg = GPPUPPDN0 + (gpio>>4);
int pullshift = (gpio & 0xf) << 1;
unsigned int pullbits;
unsigned int pull;
switch (type)
{
case PULL_NONE:
pull = 0;
break;
case PULL_UP:
pull = 1;
break;
case PULL_DOWN:
pull = 2;
break;
default:
return 1; /* An illegal value */
}
pullbits = *(gpio_base + pullreg);
pullbits &= ~(3 << pullshift);
pullbits |= (pull << pullshift);
*(gpio_base + pullreg) = pullbits;
}
else
{
int clkreg = GPPUDCLK0 + (gpio>>5);
int clkbit = 1 << (gpio & 0x1f);
*(gpio_base + GPPUD) = type;
delay_us(10);
*(gpio_base + clkreg) = clkbit;
delay_us(10);
*(gpio_base + GPPUD) = 0;
delay_us(10);
*(gpio_base + clkreg) = 0;
delay_us(10);
}
Could this be behind some of the glitches we randomly see when using the chainloader?
I have made changes as per the below diffs and tested several times on Pi 4 with no issues when plugging/unplugging the USB serial cable. I am not sure though if I should enable pup/pdn for the TX or RX when using a serial to USB cable. Maybe you could enlighten me there.
diff --git a/07_uart_chainloader/src/_arch/aarch64/cpu.rs b/07_uart_chainloader/src/_arch/aarch64/cpu.rs
index 34cf2ab..b1518d5 100644
--- a/07_uart_chainloader/src/_arch/aarch64/cpu.rs
+++ b/07_uart_chainloader/src/_arch/aarch64/cpu.rs
@@ -39,14 +39,6 @@ pub unsafe fn _start() -> ! {
pub use asm::nop;
-/// Spin for `n` cycles.
-#[inline(always)]
-pub fn spin_for_cycles(n: usize) {
- for _ in 0..n {
- asm::nop();
- }
-}
-
/// Pause execution on the core.
#[inline(always)]
pub fn wait_forever() -> ! {
diff --git a/07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs b/07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs
index 0b01db4..85ee230 100644
--- a/07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs
+++ b/07_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs
@@ -5,7 +5,7 @@
//! GPIO Driver.
use crate::{
- bsp::device_driver::common::MMIODerefWrapper, cpu, driver, synchronization,
+ bsp::device_driver::common::MMIODerefWrapper, driver, synchronization,
synchronization::NullLock,
};
use register::{mmio::*, register_bitfields, register_structs};
@@ -39,18 +39,22 @@ register_bitfields! {
]
],
- /// GPIO Pull-up/down Clock Register 0
- GPPUDCLK0 [
- /// Pin 15
- PUDCLK15 OFFSET(15) NUMBITS(1) [
- NoEffect = 0,
- AssertClock = 1
+ /// GPIO Pull-up/down Register 0
+ GPPUPPDN0 [
+ /// GPIO 15
+ PUPPDN15 OFFSET(30) NUMBITS(2) [
+ NoResistor = 0,
+ PullUp = 1,
+ PullDown = 2,
+ Reserved = 3
],
- /// Pin 14
- PUDCLK14 OFFSET(14) NUMBITS(1) [
- NoEffect = 0,
- AssertClock = 1
+ /// GPIO 14
+ PUPPDN14 OFFSET(28) NUMBITS(2) [
+ NoResistor = 0,
+ PullUp = 1,
+ PullDown = 2,
+ Reserved = 3
]
]
}
@@ -65,9 +69,7 @@ register_structs! {
(0x10 => GPFSEL4: ReadWrite<u32>),
(0x14 => GPFSEL5: ReadWrite<u32>),
(0x18 => _reserved1),
- (0x94 => GPPUD: ReadWrite<u32>),
- (0x98 => GPPUDCLK0: ReadWrite<u32, GPPUDCLK0::Register>),
- (0x9C => GPPUDCLK1: ReadWrite<u32>),
+ (0xe4 => GPPUPPDN0: ReadWrite<u32, GPPUPPDN0::Register>),
(0xA0 => @END),
}
}
@@ -117,16 +119,10 @@ impl GPIOInner {
.GPFSEL1
.modify(GPFSEL1::FSEL14::AltFunc0 + GPFSEL1::FSEL15::AltFunc0);
- // Enable pins 14 and 15.
- self.registers.GPPUD.set(0);
- cpu::spin_for_cycles(150);
-
+ // PUPDN to 0 for GPIO14 and GPIO15
self.registers
- .GPPUDCLK0
- .write(GPPUDCLK0::PUDCLK14::AssertClock + GPPUDCLK0::PUDCLK15::AssertClock);
- cpu::spin_for_cycles(150);
-
- self.registers.GPPUDCLK0.set(0);
+ .GPPUPPDN0
+ .modify(GPPUPPDN0::PUPPDN14::NoResistor + GPPUPPDN0::PUPPDN15::NoResistor);
}
}
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.