mikrocontroller.net

Forum: Compiler & IDEs Rust auf AVR: AVR-Rust -> So geht's


Autor: Kaj G. (Firma: RUB) (bloody)
Datum:

Bewertung
4 lesenswert
nicht lesenswert
Hallo Forum.

Programmiersprachen fuer uC gibt es ja eher wenige, verglichen zu der 
Masse an Programmiersprachen fuer den PC.

Von der Programmiersprache Rust gibt es den Ableger AVR-Rust, der Rust 
fuer die AVRs verfuegbar macht.

AVR-Rust:
https://github.com/avr-rust

Das Projekt muss man selber compilieren. Wie das geht hab ich mal in 
einem Bugreport beschrieben:
https://github.com/avr-rust/rust/issues/108#issuec...

Als zusaetzliche Abhaengigkeiten sind avr-gcc (zum Linken) und avr-libc 
zu installieren.

Ich hab ein kleines Testprojekt fuer den AT90USB1286 (Teensy++ 2.0) 
gemacht um zu zeigen wie das geht, auch mit Interrupts.

avr-rust-teensy-blink
https://gitlab.com/Bloody_Wulf/avr-rust-teensy-blink

Ist noch nicht wirklich schoen der Code, sollte aber reichen um zu 
zeigen wie es geht.

Zu dem Code:
Datei: std.rs
#[lang = "eh_personality"]
#[no_mangle]
pub unsafe extern "C" fn rust_eh_personality(
    _state: (),
    _exception_object: *mut (),
    _context: *mut (),
) -> () {
}

#[lang = "panic_fmt"]
#[unwind]
pub extern "C" fn rust_begin_panic(_msg: (), _file: &'static str, _line: u32) -> ! {
    loop {}
}
Dieser Code ist nur fuer den Compiler, deswegen hab ich ihn in eine 
extra Datei ausgelagert, damit main nicht so zugemuellt aussieht.


Datei: main.rs
#![feature(abi_avr_interrupt)]
#![feature(asm)]
#![feature(lang_items)]
#![feature(unwind_attributes)]
#![no_main]
#![no_std]

extern crate avrd;

pub mod led;
pub mod std;
pub mod timer;

use avrd::at90usb1286::*;
use core::ptr::write_volatile;

#[no_mangle]
pub extern "C" fn main() -> ! {
    set_output();

    timer::init();

    enable_interrupts();

    loop {

    }
}

fn enable_interrupts() {
    unsafe {
        asm!("SEI");
    }
}

fn set_output() {
    unsafe {
        write_volatile(DDRD, 0x40);
    }
}


Datei: led.rs
use avrd::at90usb1286::*;
use core::ptr::write_volatile;

pub fn on() {
    unsafe {
        write_volatile(PORTD, 0x40);
    }
}

pub fn off() {
    unsafe {
        write_volatile(PORTD, 0x00);
    }
}


Datei: timer.rs
use avrd::at90usb1286::*;
use core::ptr::write_volatile;
use led;

static mut FLAG: bool = false;


#[no_mangle]
//  Datasheet start counting interruptvectors at 1. But we
//  have to decrease that number by 1 to get the right interrupt
//  TIMER1 OVF = Vector 21 --------------------+
//                                             v
pub unsafe extern "avr-interrupt" fn __vector_20() {
    // disable the timer
    write_volatile(TCCR1B, 0x00);

    // set timer back to start value
    write_volatile(TCNT1, 0x0B_DB);

    FLAG = !FLAG;

    if FLAG {
        led::on();
    } else {
        led::off();
    }

    // set prescaler to 256 and start the timer
    write_volatile(TCCR1B, 0x04);
}

pub fn init() {
    unsafe {
        // timer start value so we should get close to 1 sec
        // with a prescaler of 256
        write_volatile(TCNT1, 0x0B_DB);

        // enable overflow interrupt
        write_volatile(TIMSK1, 0x01);

        // set prescaler to 256 and start the timer
        write_volatile(TCCR1B, 0x04);
    }
}

Vielleicht hat ja der ein oder andere mal Lust AVR-Rust auszuprobieren, 
ist ja Wochenende :)

Gruesse

Autor: m.n. (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Bevor mein Spieltrieb geweckt wird eine kurze Zwischenfrage:
Werden bei AVR-Rust die Typen f32 und f64 unterstützt?

Autor: Kaj G. (Firma: RUB) (bloody)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
m.n. schrieb:
> Werden bei AVR-Rust die Typen f32 und f64 unterstützt?
Ich hab jetzt spasseshalber mal einfach sowas gemacht:
#[no_mangle]
pub extern "C" fn main() -> ! {
    let mut var1: f32 = 0.0;
    let mut var2: f64 = 0.0;

    loop {
        var1 = var1 + 0.37;
        var2 = var1 as f64 + 3.21;
    }
}
Es compiliert zumindestens ohne Fehler.

Autor: m.n. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke.
Aber Compilieren macht der AVR-GCC auch, ohne double entsprechend zu 
würdigen.

Autor: Kaj G. (Firma: RUB) (bloody)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
m.n. schrieb:
> Danke.
> Aber Compilieren macht der AVR-GCC auch, ohne double entsprechend zu
> würdigen.
Ach so. Ich dachte Du beziehst dich auf diesen Bug:

Compiler uses f64 literals by default; both float and double are 32 bits 
on AVR #76
https://github.com/avr-rust/rust/issues/76#issue-258761696

Da wird auch ueber die breite von f32 und f64 diskutiert.

Autor: m.n. (Gast)
Datum:

Bewertung
3 lesenswert
nicht lesenswert
Gut, f64 geht dann wohl nicht.
Die Argumente "ist zu langsam", "braucht man nicht" oder "schreib es Dir 
doch selber" kenne ich von diesem Forum. Das sind Ausreden aber keine 
Lösung. Schade.

Autor: Karl K. (karl2go)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kaj G. schrieb:
> fn enable_interrupts() {
>     unsafe {
>         asm!("SEI");
>     }
> }

Das ist aber nicht ernstgemeint, oder?

Muss man im Jahr 2018 immer noch so eine bescheidene ASM-Einbindung 
machen wie C im letzten Jahrhundert?

Autor: Kaj G. (Firma: RUB) (bloody)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Karl K. schrieb:
> Muss man im Jahr 2018 immer noch so eine bescheidene ASM-Einbindung
> machen wie C im letzten Jahrhundert?
Noe, muss man nicht.
fn enable_interrupts() {
    unsafe {
        //asm!("SEI");
        write_volatile(SREG, 0x80);
    }
}

Ist nur zum Zeigen das man auch sowas machen kann, wenn man meint das 
man es braucht. Ich werde es im Repo ergaenzen. Danke.

Oder wenn es C aehnlicher sein soll:
fn enable_interrupts() {
    unsafe {
        //asm!("SEI");
        //write_volatile(SREG, 0x80);
        *SREG = 0x80;
    }
}

Letzteres macht den Code aber um 2 Byte groesser, was wohl aber nur fuer 
SREG gilt. Spreche ich andere Register auf diese weise an, dann wird der 
Code nicht groesser. :-/

: Bearbeitet durch User
Autor: Karl K. (karl2go)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kaj G. schrieb:
> Ist nur zum Zeigen das man auch sowas machen kann, wenn man meint das
> man es braucht.

Das habe ich nicht gemeint. Es ist durchaus sinnvoll, für µC mal ASM mit 
einzubinden, wenns schnell gehen soll.

Aber doch bitte nicht durch seitenweise asm!("..."); Das ist doch 
peinlich.

Autor: Kaj G. (Firma: RUB) (bloody)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl K. schrieb:
> Das habe ich nicht gemeint. Es ist durchaus sinnvoll, für µC mal ASM mit
> einzubinden, wenns schnell gehen soll.
>
> Aber doch bitte nicht durch seitenweise asm!("..."); Das ist doch
> peinlich.
Das asm-Makro ist nicht spezifisch fuer AVR-Rust. Das Makro ist ein 
Bestandteil der Sprach-Features.

Siehe hier, da ist die Beschreibung mit Beispielen:
https://doc.rust-lang.org/unstable-book/language-f...

Aber ja, ich gebe dir Recht, sieht nicht schoen aus. Aber dafuer kann 
das AVR-Rust Projekt halt auch nichts. Das ist dem LLVM geschuldet.

https://doc.rust-lang.org/unstable-book/language-f...
The current implementation of the asm! macro is a direct binding to
LLVM's inline assembler expressions, so be sure to check out their
documentation as well for more information about clobbers,
constraints, etc.

If you need more power and don't mind losing some of the niceties of
asm!, check out global_asm.

: Bearbeitet durch User
Autor: S. R. (svenska)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl K. schrieb:
> Muss man im Jahr 2018 immer noch so eine bescheidene ASM-Einbindung
> machen wie C im letzten Jahrhundert?

Hardware-Interrupts existieren in allgemeinen Hochsprachen eher nicht, 
also werden sie immer als ASM-Einbindung gemacht. Man kann das natürlich 
auch in eine Bibliothek auslagern, wenn man will... es bleibt trotzdem 
ein ASM.

Autor: Karl K. (karl2go)
Datum:

Bewertung
-2 lesenswert
nicht lesenswert
S. R. schrieb:
> Hardware-Interrupts existieren in allgemeinen Hochsprachen eher nicht,
> also werden sie immer als ASM-Einbindung gemacht.

Das ist Quatsch, in Freepascal schreibe ich Hardware-Interrupts genauso 
wie normale Prozeduren. Theoretisch brauche ich keinen ASM. Praktisch 
ist er manchmal ganz nützlich.

Der Unterschied ist der:
// exakte Pause msec, 1 bis 255 msec

procedure delay_ms(time : uint8); assembler; nostackframe;
// Rein: r24 Dauer
const
  fmul = 1 * Cfcpu div 1000000;
label
  loop1, loop2, loop3;
asm
  push r23
  push r22
  loop1:
    ldi r23, fmul
    loop2:  // 1000 * fmul = 1000 * 1 * 8 = 8000 cycles / 8MHz
      ldi r22, 250
      loop3:  // 4 * 250 = 1000 cycles
        nop
        dec r22
        brne loop3
      dec r23
      brne loop2
    dec r24
    brne loop1
  pop r22
  pop r23
end;

fn delay_ms(time : u8) {
    let fmul = 1 * Cfcpu div 1000000;
    unsafe {
        asm!("  push r23");
        asm!("  push r22");
        asm!("  loop1:");
        asm!("    ldi r23, fmul");
        asm!("    loop2:");  // 1000 * fmul = 1000  1  8 = 8000 cycles 
/ 8MHz
        asm!("      ldi r22, 250");
        asm!("      loop3:");  // 4 * 250 = 1000 cycles
        asm!("        nop");
        asm!("        dec r22");
        asm!("        brne loop3");
        asm!("      dec r23");
        asm!("      brne loop2");
        asm!("    dec r24");
        asm!("    brne loop1");
        asm!("  pop r22");
        asm!("  pop r23");
    }
}

Oder so... Letzteres kann in Detais falsch sein.

Fällt Dir was auf?

Autor: Balatan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Karl K. (karl2go)

"Wenn der Weise auf den Mond zeigt,
sieht der Affe nur den Finger."

(Sprichwort, China)

Autor: Bernd K. (prof7bit)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Karl K. schrieb:

> fn delay_ms(time : u8) {
>     let fmul = 1 * Cfcpu div 1000000;
>     unsafe {
>         asm!("  push r23");
>         asm!("  push r22");
>         asm!("  loop1:");
>         asm!("    ldi r23, fmul");
>         asm!("    loop2:");  // 1000 * fmul = 1000  1  8 = 8000 cycles
> / 8MHz
>         asm!("      ldi r22, 250");
>         asm!("      loop3:");  // 4 * 250 = 1000 cycles
>         asm!("        nop");
>         asm!("        dec r22");
>         asm!("        brne loop3");
>         asm!("      dec r23");
>         asm!("      brne loop2");
>         asm!("    dec r24");
>         asm!("    brne loop1");
>         asm!("  pop r22");
>         asm!("  pop r23");
>     }
> }
>
> Oder so... Letzteres kann in Detais falsch sein.
>
> Fällt Dir was auf?

Wahrschenlich kann man auch alle Befehle in ein einziges asm Makro 
schreiben und wahrscheinlich kann man auch clobber Listen und dergleiche 
nutzen und wahrscheinlich müßte man sich auch keine hartcodierten 
Register aus den Fingern saugen sondern könnte sich die vom Compiler 
zuweisen lassen. Schreib also das zweite Beispiel mal entsprechend um 
bevor Du einen Vergleich ziehst.
















[obiger Bereich absichtlich leer wegen bescheuerter Forensoftware]

: Bearbeitet durch User
Autor: S. R. (svenska)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl K. schrieb:
> Fällt Dir was auf?

Nur, dass du mich vollständig bestätigt hast, obwohl meine 
Ursprungsaussage falsch war. Ich bin mir nicht sicher, was du eigentlich 
sagen wolltest.

"Interrupts ein- und ausschalten" sind sehr spezielle 
Hardwarefunktionen, die in allgemeinen Hochsprachen nichts zu suchen 
haben. Daher nutzt man dafür direkt Assembler, oder ein Makro/eine 
Bibliotheksfunktion, die das irgendwie versteckt.

Zur Korrektur: Ich meinte nicht, dass Interrupt-Handler grundsätzlich in 
Assembler geschrieben sein müssen. Mein eigener Code widerspräche dem 
schon.

Aber ein asm("sti") ist schlicht keine Gotteslästerung.

Autor: asm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gibts beim AVR-Rust auch die, IMHO, recht gelungenen 'Verzahnung' 
zwischen Variablen und Asm Argumenten wie in AVR-C/AVR-Ada?

Autor: Carl D. (jcw2)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Kaj G. schrieb:
> Karl K. schrieb:
>> Muss man im Jahr 2018 immer noch so eine bescheidene ASM-Einbindung
>> machen wie C im letzten Jahrhundert?
> Noe, muss man nicht.
.
>
> fn enable_interrupts() {
>     unsafe {
>         //asm!("SEI");
>         write_volatile(SREG, 0x80);
>     }
> }
> 
.
> Ist nur zum Zeigen das man auch sowas machen kann, wenn man meint das
> man es braucht. Ich werde es im Repo ergaenzen. Danke.

Wobei SEI nicht alle anderen Flags löscht!

Autor: Kaj G. (Firma: RUB) (bloody)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Carl D. schrieb:
> Wobei SEI nicht alle anderen Flags löscht!
Das ist ein sehr guter Einwand! Danke.

asm schrieb:
> Gibts beim AVR-Rust auch die, IMHO, recht gelungenen 'Verzahnung'
> zwischen Variablen und Asm Argumenten wie in AVR-C/AVR-Ada?
Was genau meinst du? Kannst du vielleicht ein kleines Beispiel geben?

: Bearbeitet durch User
Autor: Karl K. (karl2go)
Datum:

Bewertung
-5 lesenswert
nicht lesenswert
Bernd K. schrieb:
> Wahrschenlich kann man auch alle Befehle in ein einziges asm Makro
> schreiben...

Was dann so aussieht?
      asm volatile (
         "ror %3"                          "\n"
         "BIT_FF_LOOP%=:"                  "\n"
         "ld %0,  %a1"                     "\n"
         "adc %0, %0"                      "\n"
         "st %a1+, %0"                     "\n"
         "dec %2"                          "\n"
         "brne BIT_FF_LOOP%="              "\n"
            : "=&r" (shifted)                               // outputs
            : "e"  (buffer), "r" (length), "r" (newbit)     // inputs
            : "memory"                                      // clobbered
      );

Auch nicht besser.

> ... und wahrscheinlich kann man auch clobber Listen und dergleiche
> nutzen und wahrscheinlich müßte man sich auch keine hartcodierten
> Register aus den Fingern saugen sondern könnte sich die vom Compiler
> zuweisen lassen.

Das sind mir zu viele Wahrscheinlichs. Komm bitte wieder, wenn Du es 
weißt, nicht nur vermutest.

Autor: Eric B. (beric)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kaj G. schrieb:
> Von der Programmiersprache Rust gibt es den Ableger AVR-Rust, der Rust
> fuer die AVRs verfuegbar macht.

**thumbs up!** Jetzt muss ich mir nur noch ein Projekt ausdenken wofür 
ich ein AVR brauche :-D

Autor: Eric B. (beric)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kaj G. schrieb:
> Das Projekt muss man selber compilieren. Wie das geht hab ich mal in
> einem Bugreport beschrieben:
> https://github.com/avr-rust/rust/issues/108#issuec...

Als Anregung: für das Patchen der "OrcRemoteTargetClient.h" Datei kannst 
du das patch commando verwenden; das spart das manuelle Hin- und her 
kopieren und du kriegst trotzdem Updates in vom Repository in der 
Datei mit.

https://www.thegeekstuff.com/2014/12/patch-command-examples

HTH HAND

Autor: Christopher J. (christopher_j23)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eric B. schrieb:
> **thumbs up!** Jetzt muss ich mir nur noch ein Projekt ausdenken wofür
> ich ein AVR brauche :-D

Alternativ läuft das natürlich auch auf Cortex-M oder MSP430 ;)

https://blog.japaric.io/quickstart/
http://namniart.com/programming/rust/msp430/2018/0...

Der Blog-Artikel zum Cortex-M ist schon etwas veraltet aber unter der 
Hauptseite blog.japaric.io gibt es Nachschub von aktuellen Infos.

Autor: Eric B. (beric)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Christopher J. schrieb:
> Eric B. schrieb:
>> **thumbs up!** Jetzt muss ich mir nur noch ein Projekt ausdenken wofür
>> ich ein AVR brauche :-D
>
> Alternativ läuft das natürlich auch auf Cortex-M oder MSP430 ;)

Mein Ziel wäre eher Z80. Es gibt dafür aber kein funktionierendes 
gcc/llvm...

Autor: Christopher J. (christopher_j23)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Ein llvm-Backend scheint es für Z80 zu geben: 
https://github.com/jacobly0/llvm-z80 . Alternativ kann man eventuell das 
C-Backend von llvm nutzen und über den Umweg Rust->LLVM-IR->C das ganze 
dann mit dem sdcc kompilieren. Auf dem gleichen Weg kann man auch C++ 
mit dem sdcc realisieren, siehe z.B. 
http://www.colecovision.eu/llvm+sdcc/ aber das ist jetzt endgültig OT.

Hier findet man eine Übersicht wo derzeit so Rust im Embedded-Bereich 
eingesetzt wird: https://github.com/rust-embedded/awesome-embedded-rust

Autor: Sebastian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
http://llvm.org/doxygen/md_lib_Target_AVR_README.html

Das AVR Backend ist experimental, und es gibt eine beunruhigend 
ausehende Liste von Fehlern. Aber vielleicht betreffen die rust ja nicht 
weil die entsprechenden LLVM Funktionen nicht genutzt werden?

Autor: Wilhelm M. (wimalopaan)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Christopher J. schrieb:
> Ein llvm-Backend scheint es für Z80 zu geben:
> https://github.com/jacobly0/llvm-z80 . Alternativ kann man eventuell das
> C-Backend von llvm nutzen und über den Umweg Rust->LLVM-IR->C das ganze
> dann mit dem sdcc kompilieren. Auf dem gleichen Weg kann man auch C++
> mit dem sdcc realisieren, siehe z.B.
> http://www.colecovision.eu/llvm+sdcc/ aber das ist jetzt endgültig OT.
>

Das C-Backend existiert nicht mehr (leider) im aktuellen llvm.

Autor: Christopher J. (christopher_j23)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Das C-Backend existiert nicht mehr (leider) im aktuellen llvm.

Danke für die Info. Ist irgendwie an mir vorbeigegangen.

Autor: Verrostete Bits (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
30 Minuten für die leichte Sonntagnachmittag Unterhaltung:

Youtube-Video "RustConf 2018 - Getting Something for Nothing by James Munns"

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.