Forum: PC-Programmierung Linux/c++: errno in multithreaded Code


von Uhu U. (uhu)


Lesenswert?

Folgender code:
1
#include <sched.h>
2
#include <errno.h>
3
4
static void *timerThread(void *pPar) {
5
6
    struct sched_param schedParam = { 16 };
7
    if (sched_setscheduler(0, SCHED_RR, &schedParam))
8
        printf("%d", errno);
9
    
10
}
timerThread wird per pthread_create als eigener Thread gestartet. 
-lpthread ist in der Linker-Kommandozeile.

Der Linker erzeugt folgende Fehlermeldung:
1
TLS definition in /lib/x86_64-linux-gnu/libc.so.6 section .tbss mismatches non-TLS reference

errno muss eine Thread-private Variable sein - errno.h sollte eigentlich 
dafür sorgen, dass das funktioniert. Tuts aber nicht.

Warum?

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Uhu U. schrieb:
> timerThread wird per pthread_create als eigener Thread gestartet.
> -lpthread ist in der Linker-Kommandozeile.

Eigentlich müsste es -pthread beim Compilieren und Linken sein statt 
deiner Variante. Das ändert wohl auch das Handling von errno:
https://stackoverflow.com/questions/2127797/significance-of-pthread-flag-when-compiling


> Der Linker erzeugt folgende Fehlermeldung:
> TLS definition in /lib/x86_64-linux-gnu/libc.so.6 section .tbss mismatches
> non-TLS reference
>
> errno muss eine Thread-private Variable sein - errno.h sollte eigentlich
> dafür sorgen, dass das funktioniert. Tuts aber nicht.

Wie kommst du darauf, dass das daran liegt, dass errno nicht 
thread-specific sei?

von Uhu U. (uhu)


Lesenswert?

Rolf M. schrieb:
> Eigentlich müsste es -pthread beim Compilieren und Linken sein statt
> deiner Variante. Das ändert wohl auch das Handling von errno:

Ich habe pthread in meinem Code::Blocks unter Link Libraries 
eingetragen. Daraus wird dann in der Kommandozeile -lpthread.

Das habe ich jetzt mal probeweise gelöscht und dafür unter Other linker 
options -pthread eingetragen. Das l ist jetzt auf der Kommandozeile weg, 
aber der Fehler bleibt.

Außerdem habe ich noch -D_REENTRANT und -pthread zu den Compileroptionen 
gegeben - der Linkerfehler bleibt.

> Wie kommst du darauf, dass das daran liegt, dass errno nicht
> thread-specific sei?

Es ist wohl eher ein Längenkonflikt oder sowas in der Art. In der 
ausführlichen Liste steht noch eine Erläuterung:
1
/lib/x86_64-linux-gnu/libc.so.6: error adding symbols: Bad value".

von Jim M. (turboj)


Lesenswert?

Uhu U. schrieb:
> /lib/x86_64-linux-gnu/libc.so.6: error adding symbols: Bad value".

Der Pfad zur Libc.so.x sollte eigentlich gar nicht auftauchen in den 
Compiler Fehlermeldungen.

Da ist IMHO was oberfaul in den Linker Flags - die LibC gibt man einfach 
als "-lc" an.

von Rolf M. (rmagnus)


Lesenswert?

Jim M. schrieb:
> Da ist IMHO was oberfaul in den Linker Flags - die LibC gibt man einfach
> als "-lc" an.

Eigentlich gibt man sie gar nicht an.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Kannst du mal ein komplettes Minimalbeispiel mit makefile bzw. 
komplettem Compiler-Aufruf zeigen?
Warum pthreads und nicht std::thread?

von Rolf M. (rmagnus)


Lesenswert?

Niklas G. schrieb:
> Warum pthreads und nicht std::thread?

Wie stellt man dort die Thead-Priorität und die Scheduling-Policy um?

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

PS: Bei mir (Linux Mint 19) funktioniert
1
#include <sched.h>
2
#include <errno.h>
3
#include <pthread.h>
4
#include <cstdio>
5
6
static void *timerThread(void*) {
7
  struct sched_param schedParam = { 16 };
8
  if (sched_setscheduler(0, SCHED_RR, &schedParam))
9
    printf("%d", errno);
10
  
11
  return nullptr;
12
}
13
14
int main () {
15
  pthread_t t;
16
  if (pthread_create (&t, nullptr, timerThread, nullptr) == 0) {
17
    pthread_join (t, nullptr);
18
  }
19
}
kompiliert mit
1
g++ test.cc -pthread -O2 -Wall -Wextra
problemlos.

Rolf M. schrieb:
> Wie stellt man dort die Thead-Priorität und die Scheduling-Policy um?

Genauso, siehe z.B.:
https://en.cppreference.com/w/cpp/thread/thread/native_handle

von Rolf M. (rmagnus)


Lesenswert?

Niklas G. schrieb:
> Rolf M. schrieb:
>> Wie stellt man dort die Thead-Priorität und die Scheduling-Policy um?
>
> Genauso, siehe z.B.:
> https://en.cppreference.com/w/cpp/thread/thread/native_handle

Dann hab ich aber doch wieder pthread-Funktionen benutzt.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Rolf M. schrieb:
> Dann hab ich aber doch wieder pthread-Funktionen benutzt.

Aber nur die eine fürs Einstellen der Priorität. Dinge wie 
Parameter-Übergabe kannst du dann sauber über std::thread abwickeln...

von AbcAbc (Gast)


Lesenswert?

Die Frage ist eher: Warum bist du der Ansicht das einstellen zu müssen. 
In ca. 90% der Fälle weis dein Betriebssystem wesentlich besser wie viel 
Zeit dein Thread kriegen soll als du. Einen Echtzeit Timer wirst du auch 
mit deiner Methode nicht hinkriegen. Das beste was du ohne 
Echtzeitkernel machen kannst, ist in regelmäßigen Abschnitten die 
aktuelle Systemzeit zu messen und zu vergleichen. Dazu gibt es in 
std::chrono auch high precision clocks.

Und zu deiner Frage: std::thread verwenden und nur so minimal viel 
pthread direkt verwenden die möglich. Macht den Code für die Zukunft 
angenehmer zu verwenden. Die stl wrappt das schon sehr gut.

Dein Problem dürfte eben sein, dass du in Codeblocks, dass nur als 
Linkerflag und nicht auch als Compilerflag mitgegeben hast. Vorallem 
weißt du auch nicht im Detail wie Codeblock denn eigentlich compiliert. 
Das kann auch getrennt Compiler und Linker aufrufen.

Mal die generierten Compiler aufrufe anschauen oder noch besser: sowas 
wie Makefile oder CMake oder Meson direkt verwenden

von DPA (Gast)


Lesenswert?

Und auch noch wichtig, nach hinzufügen/ändern/entfernen von Optionen, 
die mit den vorherigen nicht kompatibel sind, nicht vergessen alle 
generierten Dateien zu löschen, auch Libs, Object files und 
vorkompilierte Header. "make clean".

von Rolf M. (rmagnus)


Lesenswert?

AbcAbc schrieb:
> Die Frage ist eher: Warum bist du der Ansicht das einstellen zu müssen.

Weil ich Echtzeit-Anwendungen schreibe.

> Einen Echtzeit Timer wirst du auch mit deiner Methode nicht hinkriegen.

Die funktionieren auf pthread-Basis aber sehr gut bei mir.

> Das beste was du ohne Echtzeitkernel machen kannst, ist in regelmäßigen
> Abschnitten die aktuelle Systemzeit zu messen und zu vergleichen.

Ich habe aber einen Echtzeitkernel.

> Dein Problem dürfte eben sein, dass du in Codeblocks, dass nur als
> Linkerflag und nicht auch als Compilerflag mitgegeben hast.

Ich bin nicht der Threadstarter. Aber da das mit std::thread bei mir 
auch letztens aufkam, hat mich interessiert, ob damit die 
Thread-Prioritäten und Realtime-Funktionalitäten auch möglich sind. Wie 
sieht es z.B. mit clock_nanosleep() aus? Können die Standard-Funktionen 
das genauso gut? Was passiert, wenn die Wartezeit durch ein Signal 
unterbrochen wird?

: Bearbeitet durch User
von AbcAbc (Gast)


Lesenswert?

Mit einem Echtzeitkernel sieht das noch mal ganz anders aus. Ja da kann 
man das machen wenn man will. Ohne Echtzeitkernel funktioniert ein 
Timerthread aber nicht wirkich präzise.

Und zum Thema sleep:
std::this_thread::sleep_for(std::chrono::nanoseconds(...))

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Rolf M. schrieb:
> Wie
> sieht es z.B. mit clock_nanosleep() aus?

Es gibt:
https://en.cppreference.com/w/cpp/thread/sleep_for

Wie das genau in der jeweiligen C++-Bibliothek umgesetzt ist muss man 
prüfen. Es wird aber letztendlich genauso den nanosleep-Syscall nutzen.

Rolf M. schrieb:
> Was passiert, wenn die Wartezeit durch ein Signal
> unterbrochen wird?

https://stackoverflow.com/a/52869260

Alle Details vom Linux-Scheduling wird man mit std::thread natürlich 
nicht nachbilden können. Aber wenn man es so viel wie möglich verwendet, 
vereinfacht man sich ggf. später die Portierung, und std::thread fügt 
sich besser in C++-Anwendungen als das pthread-API ein.

von Uhu U. (uhu)


Angehängte Dateien:

Lesenswert?

AbcAbc schrieb:
> Die Frage ist eher: Warum bist du der Ansicht das einstellen zu müssen.

Die Frage kann man stellen, aber mit dem Thread-Thema hat sie nichts zu 
tun - errno braucht man überall…

Grund ist, dass Linux kein suspend/resume für Threads kennt. Ich will 
versuchen, diese Klippe dadurch zu umschiffen, dass ich (Timer-)Threads, 
die den Haupt-Thread vorübergehend suspendieren müssten, eine höhere 
Priorität gebe, und den gesamten Prozess auf einen Kern begrenze.

Ziel der Übung ist die Simulation eines µC, bzw. dessen 
Interruptbehandlung.

> Dein Problem dürfte eben sein, dass du in Codeblocks, dass nur als
> Linkerflag und nicht auch als Compilerflag mitgegeben hast.

Siehe hier: Beitrag "Re: Linux/c++: errno in multithreaded Code"

CB zeigt natürlich an, was Compiler und Linker treiben - siehe Anhang.
Das Make-System von CB funktioniert ziemlich gut, sofern man nicht die 
ausgetretenen Pfade verlässt. Es gibt einige Spezialoptionen, von denen 
man besser die Finger lässt, weil sie völlig unausgereift und buggy sind 
- aber die brauche ich derzeit nicht.

> Ohne Echtzeitkernel funktioniert ein Timerthread aber nicht wirkich
> präzise.

Das tut in meinem Fall nicht weiter weh. Es geht nur darum, die 
Parallelität zwischen Hauptprogramm und ISR auf einem ein-kernigen µC 
nachzubilden, ohne in den Applikationscode eingreifen zu müssen.

: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Uhu U. schrieb:
> dass ich (Timer-)Threads,
> die den Haupt-Thread vorübergehend suspendieren müssten, eine höhere
> Priorität gebe, und den gesamten Prozess auf einen Kern begrenze.

Ziemlicher Hack! Kannst du das nicht mit Signalen machen (sigaction)? 
Oder vor jedem (Instruktions-)Schritt des Simulators prüfen ob der Timer 
abgelaufen ist?

Uhu U. schrieb:
> CB zeigt natürlich an, was Compiler und Linker treiben - siehe Anhang.

Warum "-DCONST=/**/" und nicht einfach "-DCONST=" ?

"-D__AVR_ATmega328P__" und "-D__flash" ist gefährlich - Bezeichner, 
welche mit __ anfangen, sind in C reserviert. Könnte es vielleicht sein, 
dass das die Standard Library durcheinander bringt?

Hast du mal mein o.g. Programm kompiliert? Funktioniert das bei dir?

von Uhu U. (uhu)


Lesenswert?

Niklas G. schrieb:
> Hast du mal mein o.g. Programm kompiliert? Funktioniert das bei dir?

Nein, es compiliert nicht: error: ‘nullptr’ was not declared in this 
scope

Wenn ich nullptr durch NULL ersetze, compiliert und bindet er.

Wenn ich getrennt compiliere und binde, geht das mit folgenden Kommandos 
ebenfalls glatt:
1
g++ -Wall -fexceptions -O2 -Wextra -Wall -g -pthread  -c /tmp/test/main.cpp -o obj/Debug/main.o
2
g++  -o bin/Debug/test obj/Debug/main.o  -pthread

> Ziemlicher Hack! Kannst du das nicht mit Signalen machen (sigaction)?

Nicht unter der Prämisse, dass am zu simulierenden µC kein Jota geändert 
werden soll.

Der Mangel liegt eher bei Linux.

> Warum "-DCONST=/**/" und nicht einfach "-DCONST=" ?

Weil der Compiler das nicht anders frißt.

> "-D__AVR_ATmega328P__" und "-D__flash" ist gefährlich - Bezeichner,
> welche mit __ anfangen, sind in C reserviert.

Das erste Symbol wird von avr-gcc definiert und das zweite hat den Sinn, 
das dem Linux-gcc unbekannte Symbol __flash unschädlich zu machen.

Ich habe die -D*=/**/-Definitionen in den per Kommandozeile überall 
includierten Header verschoben - geändert hat es nichts.

Interessant ist jedenfalls, dass der Compiler mit -D__flash= einen 
Syntaxfehler produziert. Mit -D__flash=/**/ ist der weg.

> Könnte es vielleicht sein, dass das die Standard Library durcheinander
> bringt?

Da muss ich nochmal genau prüfen, was da passiert.

: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Uhu U. schrieb:
> Nein, es compiliert nicht: error: ‘nullptr’ was not declared in this
> scope

Kompiliere mit -std=c++11. Wie alt ist dein GCC, dass das nicht 
standardmäßig aktiv ist?

Uhu U. schrieb:
> Wenn ich nullptr durch NULL ersetze, compiliert und bindet er.

Dann stimmt noch irgendwas anderes an deinem Projekt nicht. Reduziere 
dein Projekt schrittweise so weit bis es zu meinem Mini-Programm 
identisch ist; auf dem Weg dahin wird es irgendwann von kaputt zu 
funktionierend übergehen...

Uhu U. schrieb:
> Nicht unter der Prämisse, dass am zu simulierenden µC kein Jota geändert
> werden soll.
Sollte hinkommen. Der Prozess wird unterbrochen. Emulierst du den uC, 
oder kompilierst du den uC-Code für das Linux neu?

Uhu U. schrieb:
> Weil der Compiler das nicht anders frißt.

Bei mir schon. Dann stimmt bei dir was nicht. Beachte dass es "-DCONST=" 
und nicht "-DCONST" ist.

Uhu U. schrieb:
> Das erste Symbol wird von avr-gcc definiert und das zweite hat den Sinn,
> das dem Linux-gcc unbekannte Symbol __flash unschädlich zu machen.

Da aber dein Linux-Programm wohl nicht mit dem AVR-GCC kompiliert wird, 
ist das hier an der falschen Stelle. Wenn die libc irgendwo ein "#ifdef 
__AVR_ATmega328P__" hat...

Uhu U. schrieb:
> Ich habe die -D*=/**/-Definitionen in den per Kommandozeile überall
> includierten Header verschoben - geändert hat es nichts.
Wenig überraschend.

von Uhu U. (uhu)


Lesenswert?

Niklas G. schrieb:
> Wie alt ist dein GCC, dass das nicht standardmäßig aktiv ist?

Ich habe im Moment Linux Mint 17 mit gcc version 4.8.4 (Ubuntu 
4.8.4-2ubuntu1~14.04.4)

> Dann stimmt noch irgendwas anderes an deinem Projekt nicht.

Das bezog sich auf dein Testprogramm. Ich benutze in meinem Code nullptr 
nicht, insofern ist das kein Problem.

> Sollte hinkommen. Der Prozess wird unterbrochen. Emulierst du den uC,
> oder kompilierst du den uC-Code für das Linux neu?

Letzteres. Wenn ich den µC emulieren würde, hätte ich das Problem mit 
den echt parallel laufenden Interrupt- und Timerthreads nicht.

Die Idee ist es, den 8-Bit-Code für x86 zu compilieren und dort in 
Echtzeit laufen zu lassen. Lediglich die Register der Memory-Mapped-I/O 
werden als C++-Objekte implementiert und dem µC-Code durch geschickte 
Definitionen und Comlipation als C++ untergejubelt.

Das hätte gegenüber den üblichen emulierenden Simulatoren den Vorteil, 
dass es sehr viel schneller läuft und vor allem, dass man mit einem ganz 
normalen Linux-Debugger arbeiten kann. Zudem entfällt die doch recht 
zeitraubende Flasherei.

Was man damit sicher nicht hinbekommt, ist exakt taktgenaues Verhalten. 
Man kann damit aber zumindest die nicht zeitkritischen Algorithmen in 
einem µC-Programm in aller Ruhe entwickeln und austesten, bevor man auf 
die Hardware geht.

> Da aber dein Linux-Programm wohl nicht mit dem AVR-GCC kompiliert wird,
> ist das hier an der falschen Stelle. Wenn die libc irgendwo ein "#ifdef
> __AVR_ATmega328P__" hat...

Das _AVR_ATmega328P_ wird in den avr/* Headern benutzt. Es beißt sich 
nicht mit den Standard-Bibliotheken. Das Symbol wird gebraucht, um aus 
reinen #define-Headern die zum jeweiligen µC-Typ passenden Definitionen 
zu selektieren. Der Name ist offensichtlich so gewählt, dass die 
Kollisionswahrscheinlichkeit auch mit künftigen Standard-Headern gering 
ist.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Uhu U. schrieb:
> Ich habe im Moment Linux Mint 17 mit gcc version 4.8.4 (Ubuntu
> 4.8.4-2ubuntu1~14.04.4)

Also eine über 4 Jahre alte Version.

Uhu U. schrieb:
> Das bezog sich auf dein Testprogramm. Ich benutze in meinem Code nullptr
> nicht, insofern ist das kein Problem.

Es ist allgemein keine verkehrte Idee aktuelle Software zu benutzen. Und 
nullptr und viele weitere Dinge aus aktuellen C++-Sprachstandards sind 
auch sehr sinnvoll.

Uhu U. schrieb:
> Was man damit sicher nicht hinbekommt, ist exakt taktgenaues Verhalten.

Und exakt identisches Verhalten bezüglich Integer-Arithmetik oder 
Speicherlayouts.

von Uhu U. (uhu)


Lesenswert?

Niklas G. schrieb:
> Es ist allgemein keine verkehrte Idee aktuelle Software zu benutzen. Und
> nullptr und viele weitere Dinge aus aktuellen C++-Sprachstandards sind
> auch sehr sinnvoll.

Das mag ja sein, aber avr-gcc ist auch schon ziemlich alt und das ist 
hier "das Maß aller Dinge".

> Und exakt identisches Verhalten bezüglich Integer-Arithmetik oder
> Speicherlayouts.

Wenn man konsequent Datentypen mit genau definierter Länge benutzt, 
passt das. Bei sehr hardwarenaher Programmierung ist das die Regel. Bei 
float/double geht es schief, aber da, wo man es auf einem µC braucht, 
kommt es dann eher nicht auf identisches Speicherlayout an - zumindest 
wenn das Zielmaschinchen keine entsprechende Hardware hat.

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Uhu U. schrieb:
> Das mag ja sein, aber avr-gcc ist auch schon ziemlich alt und das ist
> hier "das Maß aller Dinge".

avr-gcc ist ein gcc, und daher naturgemäß immer aktuell. Zur Zeit also 
in Version 8.3.

Oliver

: Bearbeitet durch User
von Uhu U. (uhu)


Lesenswert?

Oliver S. schrieb:
> avr-gcc ist ein gcc, und daher naturgemäß immer aktuell. Zur Zeit also
> in Version 8.3.

Das ändert nichts daran, dass er auf dem Stand C99 ist…

von Oliver S. (oliverso)


Lesenswert?

Auf die Gefahr, mich um wiederholen, avr-gcc ist ein gcc, und daher 
immer aktuell.

https://gcc.gnu.org/onlinedocs/gcc/Standards.html

Oliver

von Rolf M. (rmagnus)


Lesenswert?

Niklas G. schrieb:
> Wie das genau in der jeweiligen C++-Bibliothek umgesetzt ist muss man
> prüfen. Es wird aber letztendlich genauso den nanosleep-Syscall nutzen.

Nicht nanosleep, sondern clock_nanosleep. Da gibt's zwei wichtige 
Unterschiede: Erstens kann man wählen, welche Uhr für die Zeitmessung 
verwendet wird, und zweitens kann man den Aufwachzeitpunkt auch absolut 
angeben.

Uhu U. schrieb:
> Grund ist, dass Linux kein suspend/resume für Threads kennt.

Muss der Thread denn von außen schlafen gelegt werden können? Wenn er 
sich auch selber schlafen legen kann, könntest du auch einfach mit 
Semaphoren arbeiten.

> Ich will versuchen, diese Klippe dadurch zu umschiffen, dass ich
> (Timer-)Threads, die den Haupt-Thread vorübergehend suspendieren müssten,
> eine höhere Priorität gebe, und den gesamten Prozess auf einen Kern
> begrenze.

Das klingt nach keinem guten Design, um ehrlich zu sein.

> Ziel der Übung ist die Simulation eines µC, bzw. dessen
> Interruptbehandlung.

Uhu U. schrieb:
>> Ohne Echtzeitkernel funktioniert ein Timerthread aber nicht wirkich
>> präzise.
>
> Das tut in meinem Fall nicht weiter weh. Es geht nur darum, die
> Parallelität zwischen Hauptprogramm und ISR auf einem ein-kernigen µC
> nachzubilden, ohne in den Applikationscode eingreifen zu müssen.

Aber es gibt doch dort gar keine Parallelität. Du willst an sich etwas, 
das rein sequenziell abläuft, mit Threads umsetzen und dann erzwingen, 
dass diese nicht paralellisiert werden.

Niklas G. schrieb:
> Ziemlicher Hack! Kannst du das nicht mit Signalen machen (sigaction)?

Signale sind ja eigentlich das exakte User-Space-Pendant zu Interrupts. 
Die sollten sich also ganz gut für die Simulation von ISRs eignen.

Uhu U. schrieb:
>> Und exakt identisches Verhalten bezüglich Integer-Arithmetik oder
>> Speicherlayouts.
>
> Wenn man konsequent Datentypen mit genau definierter Länge benutzt,
> passt das.

Nicht unbedingt. Berechnungen werden immer mindestens mit int 
ausgeführt, auch wenn sämtliche Operanden kleiner sind. Und int ist auf 
avr und x86 verschieden groß. Es kann also durchaus Fallstricke geben, 
in die man damit läuft, wenn ein Zwischenergebnis auf dem PC noch in int 
passt, auf dem AVR aber nicht mehr.

> Bei sehr hardwarenaher Programmierung ist das die Regel.

Hmm, ich nutze sie eigentlich möglichst nur dort, wo wirklich auf 
Register oder irgendwelche Schnittstellen zugegriffen wird.

> Bei float/double geht es schief, aber da, wo man es auf einem µC braucht,
> kommt es dann eher nicht auf identisches Speicherlayout an - zumindest
> wenn das Zielmaschinchen keine entsprechende Hardware hat.

float sollte eigentlich auch gehen, da der AVR meines Wissens die selbe 
Repräsentation wie der PC nutzt. Bei double nicht, da das auf dem AVR 
"baugleich" mit float ist.

von Uhu U. (uhu)


Lesenswert?

Rolf M. schrieb:
> Muss der Thread denn von außen schlafen gelegt werden können? Wenn er
> sich auch selber schlafen legen kann, könntest du auch einfach mit
> Semaphoren arbeiten.

Ich habe mich gerade durch die Signal-Behandlung gelesen - das scheint 
die Lösung zu sein: der Signal-Handler kommt in den Hauptthread, in dem 
auch der µC-Code läuft. Die Timer-Threads, die das Zeitverhalten der 
Peripherie grob simulieren sollen, schicken nur ein Signal an den 
Hauptthread und der Signal-Handler erledigt die Aufgabe dann in 
Konkurrenz zum µC-Code.

Nachteil sind allerdings die zwei Latenzzeiten, die auf diesem Weg 
entstehen.

> Aber es gibt doch dort gar keine Parallelität. Du willst an sich etwas,
> das rein sequenziell abläuft, mit Threads umsetzen und dann erzwingen,
> dass diese nicht paralellisiert werden.

Das Problem sind die Timer und Delays, die durch die Hardware verursacht 
werden, z.B. beim USART. Letzterer wird ja vom µC-Programm nur über die 
zugehörigen I/O-Register gesteuert und der Simulator soll das 
Zeitverhealten zumindest grob nachbilden.

> Es kann also durchaus Fallstricke geben, in die man damit läuft, wenn
> ein Zwischenergebnis auf dem PC noch in int passt, auf dem AVR aber
> nicht mehr.

Für einen 8-Bitter muss man sich sowieso immer überlegen, ob die Breite 
für die jeweilige Aufgabe ausreicht - das muss man eben im Simulator 
genauso halten.

> Hmm, ich nutze sie eigentlich möglichst nur dort, wo wirklich auf
> Register oder irgendwelche Schnittstellen zugegriffen wird.

Das ist generell nicht ratsam. Einfach nur int hinzuschreiben läßt einen 
leicht die Grenzen aus den Augen verlieren.

> float sollte eigentlich auch gehen, da der AVR meines Wissens die selbe
> Repräsentation wie der PC nutzt. Bei double nicht, da das auf dem AVR
> "baugleich" mit float ist.

Das habe ich noch nicht überprüft - eine Stelle habe ich bis jetzt, wo 
float ein Problem machen könnte; dort steht ein assert(0), damit ich 
darüber fliege, sollte mir das Problem doch in die Quere kommen.

Ich drücke mich aber sowieso um die Fließkommaformate, wo es nur geht.

: Bearbeitet durch User
von S. R. (svenska)


Lesenswert?

Uhu U. schrieb:
> Nachteil sind allerdings die zwei Latenzzeiten,
> die auf diesem Weg entstehen.

Was hast du denn vor, dass die Performance so krass wichtig ist?

Du lässt Code für einen 20 MHz AVR auf einem 2000 MHz PC laufen. Selbst 
ein zyklengenauer Simulator sollte eigentlich problemlos einen AVR mit 
dreistelligen MHz-Äquivalenten hinbekommen...

von Uhu U. (uhu)


Lesenswert?

S. R. schrieb:
> Was hast du denn vor, dass die Performance so krass wichtig ist?

Nichts besonderes.

> Du lässt Code für einen 20 MHz AVR auf einem 2000 MHz PC laufen. Selbst
> ein zyklengenauer Simulator sollte eigentlich problemlos einen AVR mit
> dreistelligen MHz-Äquivalenten hinbekommen...

Es gibt ja den Simulavr, der wirklich taktgenau auf dem PC simuliert - 
der schafft keine Echtzeit.

von Rolf M. (rmagnus)


Lesenswert?

Uhu U. schrieb:
>> Du lässt Code für einen 20 MHz AVR auf einem 2000 MHz PC laufen. Selbst
>> ein zyklengenauer Simulator sollte eigentlich problemlos einen AVR mit
>> dreistelligen MHz-Äquivalenten hinbekommen...
>
> Es gibt ja den Simulavr, der wirklich taktgenau auf dem PC simuliert -
> der schafft keine Echtzeit.

Das liegt aber daran, dass der Code interpretiert werden muss und das 
die Taktgenauigkeit auch Performance kostet. Allerdings denke ich schon, 
dass das auch in Echtzeit möglich sein sollte.
Ein Signalhandler jedenfalls wird jetzt keine riesigen Latenzzeiten 
erzeugen. Abgesehen davon schafft auch der AVR es nicht, von einem 
Takzyklus zum nächsten in der ISR zu sein.

von Oliver S. (oliverso)


Lesenswert?

Uhu U. schrieb:
> Es gibt ja den Simulavr, der wirklich taktgenau auf dem PC simuliert -
> der schafft keine Echtzeit.

Mit ein paar Aktualisierungen schafft der zwar immer noch keine 
Echtzeit, da jeder Befehl halt so lange braucht, wie er braucht, aber 
einen einstelligen MHz-Takt bekommt man im Mittel schon hin.

Oliver

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Hat noch keiner versucht einen "AVR-Emulator" mit 
Maschinencode-Erzeugung zu bauen? Praktisch ein AVR->AMD64 oder AVR->ARM 
Maschinencode-Transpiler? Das wird doch bei diversen anderen Emulatoren 
auch so gemacht.
Eine Abwandlung davon wäre ein AVR->Java Bytecode Umwandler (z.B. mit 
der Java ASM Bibliothek) - die JVM übersetzt das dann typischerweise in 
nativen Maschinencode. Wäre vermutlich nicht ganz so effizient, aber 
dafür portabel.

von Oliver S. (oliverso)


Lesenswert?

Das Problem ist doch sowieso, daß einem eine Simulation des reinen 
Prozessor-Cores fast nichts nützt. Jedes reales Programm nutzt Timer, 
etc. und interagiert mit der Außenwelt.

Oliver

von Uhu U. (uhu)


Lesenswert?

Niklas G. schrieb:
> Hat noch keiner versucht einen "AVR-Emulator" mit
> Maschinencode-Erzeugung zu bauen? Praktisch ein AVR->AMD64 oder AVR->ARM
> Maschinencode-Transpiler? Das wird doch bei diversen anderen Emulatoren
> auch so gemacht.

Wozu? C-Programme kann man doch auch auf dem PC compilieren und wenn man 
die Peripherie des µC nachbildet, dann hat man einen Simulator - genau 
das, woran ich hier bastele…

von Uhu U. (uhu)


Lesenswert?

Oliver S. schrieb:
> Das Problem ist doch sowieso, daß einem eine Simulation des reinen
> Prozessor-Cores fast nichts nützt. Jedes reales Programm nutzt Timer,
> etc. und interagiert mit der Außenwelt.

Eben. Mit der Peripherie kommt das Zeitverhalten ins Spiel und dann 
wirds kompliziert. Das reale Zeitverhalten des PC ist dabei relativ 
unwichtig, so lange er den µC nicht in Schneckentempo nachbildet. 
Schwierig wird es, die PC-Zeit (gemessen in Maschinentakten!) halbwegs 
korrekt ins Verhältnis des Periperie-Timings zu bringen.

Wenn man den µC nicht wirklich taktgenau emulieren will, muss man sich 
was einfallen lassen, wie man die Stimulus-Generierung mit der 
"Geschwindigkeit" des Prozessors synchronisieren kann.

von Oliver S. (oliverso)


Lesenswert?

Uhu U. schrieb:
> Schwierig wird es, die PC-Zeit (gemessen in Maschinentakten!) halbwegs
> korrekt ins Verhältnis des Periperie-Timings zu bringen.
>
> Wenn man den µC nicht wirklich taktgenau emulieren will, muss man sich
> was einfallen lassen, wie man die Stimulus-Generierung mit der
> "Geschwindigkeit" des Prozessors synchronisieren kann.

Na ja, es kommt darauf an, was man will. Einen vollständigen Emulator, 
den man an Stelle des Originals in eine Hardware einbauen kann, oder 
einen reinen Softwaresimulator, der wie SimulAVR Takte genau zählt, 
dessen Arbeitsgeschwindigkeit aber egal ist.

Kaufen könnte man beide Versionen, wenn man wollte.

Oliver

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Uhu U. schrieb:
> Wozu? C-Programme kann man doch auch auf dem PC compilieren

Aber eben nicht 100% identisch. Bei einer Emulation des Prozessors kann 
man die Takte zählen (um die Peripherie damit zu synchronisieren) und 
auch zwischen den Instruktionen in ISRs springen, ohne Signale oder 
mehrere Threads oder solche Hacks.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.