Hardware Abstraction Layer



  • Ich teste gerade ob der Compiler das ganze auch mit einer "normalen" Klasse optimiert bekommt.

    class GpoTest {
    public:
    	inline GpoTest(uint8_t port_no, uint8_t pin_no) : k_port_no(port_no), k_pin_no(pin_no) {
    	}
    
    	inline GPIO_Type* get_base() const {
    		switch(k_port_no) {
    			case 0: return PTA;
    			case 1: return PTB;
    			case 2: return PTC;
    			case 3: return PTD;
    			case 4: return PTE;
    		}
    		return 0;
    	}
    
    	inline void init() const {
    		get_base()->PDDR |= 1<<k_pin_no;
    	}
    
    	inline void set() const {
    		get_base()->PSOR = 1<<k_pin_no;
    	}
    	inline void clr() const {
    		get_base()->PCOR = 1<<k_pin_no;
    	}
    
    private:
    	const uint8_t k_port_no;
    	const uint8_t k_pin_no;
    };
    

    Wenn ich das Objekt lokal erzeuge, optimiert der Compiler die ganzen this-Zeiger weg.

    int main() {
    	GpoTest led(0, 0);
    	led.init();
    	for(;;) {
    		led.set();
    		led.clr();
    	}
    	return 0;
    }
    

    Wenn ich das Objekt stattdessen global erzeuge, optimiert der Compiler da bis auf das inlining garnix.

    const static GpoTest led(0, 0);
    
    int main() {
    	led.init();
    	for(;;) {
    		led.set();
    		led.clr();
    	}
    	return 0;
    }
    

    Mir ist klar, dass das Objekt, da es global ist, jederzeit verändert werden könnte, aber da es static ist, sollte es doch nur im Modul sichtbar sein. Sollte dem Compiler nicht auffallen, dass k_port_no und k_pin_no sich niemals ändern? Zudem ist das ganze auch noch const ...

    Jemand ne Idee, wie ich den Compiler trotzdem noch dazu bewegen kann, das ganze zu optimieren? Oder sollte ich dann doch eher bei den "statischen" Klassen bleiben?



  • Wenn ich den Konstruktor mit constexpr versehe, greift die Optimierung wieder:

    constexpr GpoTest(uint8_t port_no, uint8_t pin_no) : k_port_no(port_no), k_pin_no(pin_no) {
    	}
    

    Der Compiler macht mich grad wahnsinnig ...



  • Ick würde nicht auf einen bestimmten Compiler setzen, wenn der Code universell einsetzbar sein soll.

    Versuch den Code lieber so primitiv wie möglich zu gestalten.

    Weniger ist mehr.
    C schlägt C++.
    Jedenfalls immer dann, wenn Speicherbedarf und Taktfrequenz eine Rolle spielen.
    Und das ist bei Embedded Systems fast immer der Fall. 🙂


  • Mod

    Andromeda schrieb:

    Weniger ist mehr.
    C schlägt C++.
    Jedenfalls immer dann, wenn Speicherbedarf und Taktfrequenz eine Rolle spielen.

    Bist du bescheuert?



  • Arcoth schrieb:

    Andromeda schrieb:

    Weniger ist mehr.
    C schlägt C++.
    Jedenfalls immer dann, wenn Speicherbedarf und Taktfrequenz eine Rolle spielen.

    Bist du bescheuert?

    Klar doch, 😃



  • Weniger ist mehr. 
    C schlägt C++. 
    Jedenfalls immer dann, wenn Speicherbedarf und Taktfrequenz eine Rolle spielen. 
    Und das ist bei Embedded Systems fast immer der Fall.
    

    Also die Erfahrung habe ich bisher noch nicht gemacht, eher im Gegenteil 😛



  • fenrayn schrieb:

    Ist diese Vorgehensweise üblich? Ich finde leider sehr wenig Ressourcen im Netz zu dem Thema

    Ich denke, dass wird der Weg sein, den wir gehen sollten. Was bei Deiner Lösung noch fehlt, ist die Berücksichtigung von globalen Initialisierungen (peripheral clocks) und die Berücksichtigung von konkurrierenden Recourcen-Belegungen (z.B. dass ein Pin nicht gleichzeitig als GPIO und als Anschluss für irgend ein Peripheral genutzt werden kann).

    Dazu müsste man die gesamte Controller-Nutzung der Library zur Verfügung stellen, dann wüsste die, welche Ports verwendet werden und wie die Initialisierung aussehen muss.

    Ich hatte das mal in einem Projekt ausprobiert, dabei ist dann so etwas heraus gekommen:

    typedef hammer::gpio::output_pin< device::P0_30 > puls;
    typedef hammer::gpio::output_pin< device::P0_15 > ami_enabled;
    
    typedef hammer::gpio::output_pin< device::P0_08, hammer::gpio::inverted, hammer::gpio::toggle > green_led;
    typedef hammer::gpio::output_pin< device::P0_09, hammer::gpio::inverted > red_led;
    
    typedef hammer::gpio::output_pin< device::P0_21, hammer::gpio::toggle > debug_pin;
    
    typedef hammer::timer::interval_timer<
        base_timer_interval,
        hammer::callback_member< device_logic, &device_logic::timer_callback, &logic > > main_timer;
    
    typedef hammer::serial::i2c_master<
        hammer::serial::scl_pin_with_pullup< device::P0_01 >,
        hammer::serial::sda_pin_with_pullup< device::P0_02 >,
        hammer::serial::i2c_bus_speed_400k > i2c_master;
    
    typedef radio_receiver radio;
    
    typedef lm75a< i2c_master > temperatur_sensor;
    
    struct hardware : hammer::device< hardware, device > {
        typedef boost::mpl::vector< red_led, green_led, debug_pin > debug_pins;
        typedef boost::mpl::vector< puls, debug_pins > pins;
    
        typedef boost::mpl::vector<
            pins,
            main_timer,
            i2c_master,
            temperatur_sensor
        > peripherals;
    };
    

    Dabei müsste man einen Ansatz wählen, die gewünschte Funktionseinheit sehr konkret und auf höchstem Level zu beschreiben, sodass sich die Library eine passende Implementierung mit den zur Verfügung stehenden pheriperals zusammen suchen kann.

    Hier noch ein Talk von der Meeting C++ von 2014 zum Thema: https://www.youtube.com/watch?v=k8sRQMx2qUw

    Hier habe ich einen C++ Bluetooth LE stack, der auf einem Cortex-M0 läuft und mit sehr wenig resourcen auskommt, das geht in etwas in die selbe Richtung: https://github.com/TorstenRobitzki/bluetoe

    mfg Torsten



  • fenrayn schrieb:

    Weniger ist mehr. 
    C schlägt C++. 
    Jedenfalls immer dann, wenn Speicherbedarf und Taktfrequenz eine Rolle spielen. 
    Und das ist bei Embedded Systems fast immer der Fall.
    

    Also die Erfahrung habe ich bisher noch nicht gemacht, eher im Gegenteil 😛

    C ist gewissermaßen sowas wie eine portable Assemblersprache. Für einen HAL gibt es quasi nicht Besseres.

    In C++ kanst du zu 99.9% Plain-C programmieren, wenn du willst. Von daher kann man nicht sagen, dass C++ irgendwie weniger geeignet ist.

    Problematisch wird es aber immer dann, wenn man die erweiterten Features von C++ einsetzen will. Du siehst ja selbst, auf welche Hindernisse du stößt. Aber vielleicht ist es ja gerade diese intellektuelle Herausforderung, die dich besonders anspornt.



  • fenrayn schrieb:

    Weniger ist mehr. 
    C schlägt C++. 
    Jedenfalls immer dann, wenn Speicherbedarf und Taktfrequenz eine Rolle spielen. 
    Und das ist bei Embedded Systems fast immer der Fall.
    

    Also die Erfahrung habe ich bisher noch nicht gemacht, eher im Gegenteil 😛

    Das Problem ist eher, dass im Embedded Bereich wenige ausgebildete SW-Entwickler arbeiten. Die meisten sind E-Techniker, die da mehr oder weniger rein geworfen wurden. Mit wachsenden Projektgrößen wird die Notwendigkeit von Abstraktionen bestimmt noch weiter steigen und da hat C wenig zu bieten (vor allem, wenn es keine Recourcen kosten darf).



  • Andromeda schrieb:

    Problematisch wird es aber immer dann, wenn man die erweiterten Features von C++ einsetzen will.

    Naja, es gibt sehr viele features in C++, die überhaupt keine Ressourcen kosten (bzw. nur welche kosten, wenn man das feature auch verwendet) und C++ genau deshalb zur besseren Wahl für embedded systems machen:

    - namespaces
    - templates
    - strenger Typenprüfung
    - gezieltere casts
    - class enums
    - Verhinderungen von narrowing conversions ({})
    - constexpr
    - const
    - constructors
    usw.

    mfg Torsten



  • Problematisch wird es aber immer dann, wenn man die erweiterten Features von C++ einsetzen will. Du siehst ja selbst, auf welche Hindernisse du stößt. Aber vielleicht ist es ja gerade diese intellektuelle Herausforderung, die dich besonders anspornt.
    

    Ja natürlich, wenn ich OO programmiere, ist dies mit gewissen Overhead verbunden, aber ich kann ja auch in C OO programmieren ... das ist also keine Frage der Programmiersprache, sonders des Konzepts. Verwendet man Sachen wie RTTI oder Exceptions, dann kann es schon mal dazu führen das der C Code schneller ist als der äquivalente C++ Code. Aber da ich diese Features beim Compiler eh komplett deaktiviert habe, mach ich mir da keine Sorgen.

    Großes Potential besonders für den Embedded Bereich sehe ich für C++ in Templates/Metaprogrammierung, constexpr und Lambdas. Inline-Funktionen und Const sind ja auch schon eine weile im C-Standard.

    Auch wenn ich für den PC programmiert habe stand ich immer dem Dilemma: Performance oder Flexibilität. Meine Hoffnung ist, dass ich dieses Dilemma mit den oben genannten C++ Features nicht mehr habe.

    Das Problem ist eher, dass im Embedded Bereich wenige ausgebildete SW-Entwickler arbeiten. Die meisten sind E-Techniker, die da mehr oder weniger rein geworfen wurden.

    Da ich gerade am Ende meines E-Technik Studiums bin, kenne ich dieses Phänomen 😃 Was wir an der Hochschule an Programmiertechniken gelernt haben war nen Witz. Kein C++, nur "einfaches" C, und je nach Vertiefungsrichtung durfte man noch ein wenig Java für Webanwendungen (LOL) lernen^^



  • Torsten Robitzki schrieb:

    Das Problem ist eher, dass im Embedded Bereich wenige ausgebildete SW-Entwickler arbeiten. Die meisten sind E-Techniker, die da mehr oder weniger rein geworfen wurden.

    Ja, das sehe ich auch so.

    Wobei in den letzen 5-10 Jahren durchaus eine Tendenz spürbar ist, für die Softwareentwicklung eher Informatiker und ähnliche Code-Monkeys anzuheuern.

    Das hängt wohl auch mit dem erweiterten Leistungsspektrum moderner MCUs zusammen. Daher lässt sich inzwischen auch bei Embedded Devices zwischen hardwarenaher Programmierung und "Userland" trennen.



  • @Torsten:
    Alle Pins in einem Enum anzugeben finde ich interessant. Vllt. werde ich das auch so übernehmen.

    Was bei Deiner Lösung noch fehlt, ist die Berücksichtigung von globalen Initialisierungen (peripheral clocks) und die Berücksichtigung von konkurrierenden Recourcen-Belegungen (z.B. dass ein Pin nicht gleichzeitig als GPIO und als Anschluss für irgend ein Peripheral genutzt werden kann)

    Das ein Pin zur selben Zeit von zwei unterschiedlichen Modulen verwendet wird sollte auf jeden Fall vermieden werden, aber es soll schon die Möglichkeit geben einen Pin zu muxen.



  • fenrayn schrieb:

    Das ein Pin zur selben Zeit von zwei unterschiedlichen Modulen verwendet wird sollte auf jeden Fall vermieden werden, aber es soll schon die Möglichkeit geben einen Pin zu muxen.

    Das war auch nur ein Beispiel, warum ich der Meinung bin, dass das ganze am besten funktionieren wird, wenn man so einer Library die gesamte "Wahrheit" mitteilt. Wenn die die Möglichkeit hätte, konkurrierende Ressourcenverwendung anzuzeigen, dann sollte es auch möglich sein, Ihr mitzuteilen, dass das an der Stelle so gewollt ist.


  • Mod

    Andromeda schrieb:

    C ist gewissermaßen sowas wie eine portable Assemblersprache. Für einen HAL gibt es quasi nicht Besseres.

    Das reicht nicht. Damit C die bessere Wahl ist, muss die Größe der ausführbaren Datei eine große Rolle spielen - das ist das einzige Kriterium, was mir auf die Schnelle einfällt, um überhaupt irgendeinen Einsatz von C per se zu rechtfertigen. Und selbst dort kann ein C++ Compiler optimieren ( -Os , -s & UPX). Es gibt auch einen Grund für die Definition von "freestanding implementation".

    In C++ kanst du zu 99.9% Plain-C programmieren, wenn du willst.

    Oder man kann sich der Vielfalt an Abstraktionen bedienen, die Programmieren um einiges Erleichtern. Mein Vorposter hat ja schon einiges erwähnt. Wenn dir das nicht relevant scheint, bist du eben bescheuert, daher die Frage.

    Problematisch wird es aber immer dann, wenn man die erweiterten Features von C++ einsetzen will. Du siehst ja selbst, auf welche Hindernisse du stößt. Aber vielleicht ist es ja gerade diese intellektuelle Herausforderung, die dich besonders anspornt.

    Nix ist "problematisch". Wenn sowohl die Variable als auch die entsprechenden Funktionen als constexpr deklariert werden, wird der Compiler all die Funktionsaufrufe mit Sicherheit weg optimieren. Und bei der Template-Lösung gab es überhaupt keine Probleme. Du scheinst überdies außer Acht zu lassen, dass jedes unproblematische Feature von C++ (was praktisch fast alle sind) einen weiteren, womöglich großen Nutzen für den Programmierer darstellt. Keine Last.

    Im Übrigen hat dein C Fundamentalismus in diesem Jahrzehnt wenig verloren.



  • Arcoth schrieb:

    Im Übrigen hat dein C Fundamentalismus in diesem Jahrzehnt wenig verloren.

    http://www.tiobe.com/tiobe_index



  • Die Statistik ist ja jetzt sehr allgemein gehalten. Mich würde mehr Interessieren, wie es im Embedded Bereich aussieht!





  • ieee schrieb:

    http://spectrum.ieee.org/static/interactive-the-top-programming-languages-2015

    Du würdest Dich bei der Wahl eines Werkzeuges eher an Statistiken, als an Argumenten orientieren? Warum?



  • Und wann habe ich das gesagt? Genau, gar nicht. Hör also auf, hier Unsinn zu schreiben.


Anmelden zum Antworten