Forum: Compiler & IDEs STM32Fxxx: Wie kann man das Bitbanding nutzen in C?


von Christian J. (Gast)


Lesenswert?

Hallo,

weiss zufällig jemand wie man das Bitbanding für _Bool Variablen nutzen 
kann? Ich kann wohl ein Bit über #defines definieren aber das liegt dann 
eben an einer festen Adresse.

Mit fällt auf Anhieb nur ein einen  Bit Struct zu definieren:

typedef bitfeld {

_Bool flag_1:1;
_Bool flag_2:1;
_Bool flag_2:1;
...

} __attribute_packed(()) bitfeld_t;

Dabei erzeugt der Compiler aber einiges mehr an Code für den Zugriff.

Und da verliessen sie ihn auch schon.

von eagle user (Gast)


Lesenswert?

Knifflig. Lokale Variablen werden gern in Registern aufbewahrt, damit 
geht es überhaupt nicht. Theoretisch wäre es möglich, wenn sie auf dem 
Stack liegen. Aber dann müsste man ihre Adresse kennen um die in die 
BitBand Adresse umzurechnen. Das ist in C irgendwie nicht vorgesehen.

Bleiben globale Variablen. Dazu könnte man im Linker Script zwei 
Sections anlegen, eine im RAM für die Bits und eine für den zugehörigen 
Bereich in der BitBand Region. In dieser Section legt man die 
Bool-Variablen als int an (sie haben 32 Bit, können aber nur die Werte 0 
und 1 annehmen).

Damit kann man mit einfachen, schnellen 32-Bit-Zugriffen einzelne Bits 
lesen und schreiben. Der Zugriff auf das RAM ist angeblich ein echter, 
unteilbarer read-modify-write Zyklus. Stimmt das wirklich, auch bei 
einem Fault während die Floating Point Register gerettet werden (oder 
noch schlimmeren Sachen)?

Auf die Section im RAM sollte man wahrscheinlich nicht selbst zugreifen. 
Wie würde man mit C-Mitteln die richtige Zuordnung der beiden Sections 
machen?

von Irgendwer (Gast)


Lesenswert?

Christian J. schrieb:
> weiss zufällig jemand wie man das Bitbanding für _Bool Variablen nutzen
> kann? Ich kann wohl ein Bit über #defines definieren aber das liegt dann
> eben an einer festen Adresse.
>
> Mit fällt auf Anhieb nur ein einen  Bit Struct zu definieren:
>
> typedef bitfeld {
>
> _Bool flag_1:1;
> _Bool flag_2:1;
> _Bool flag_2:1;
> ...
>
> } __attribute_packed(()) bitfeld_t;

Irgendwie passt das nicht zusammen. Beim Bitbanding bekommt ja gerade 
jedes Bit eines Registers ein eigenes Byte (bzw. sogar mehrere) 
zugewiesen (und nicht nur ein Bit).

Auszug aus dem Handbuch des F407:
2.3.3 Bit banding
The Cortex™-M4F memory map includes two bit-band regions. These regions 
map each word in an alias region of memory to a bit in a bit-band region 
of memory. Writing to a word in the alias region has the same effect as 
a read-modify-write operation on the targeted bit in the bit-band 
region.

In the STM32F4xx devices both the peripheral registers and the SRAM are 
mapped to a bitband region, so that single bit-band write and read 
operations are allowed. The operations are only available for 
Cortex™-M4F accesses, and not from other bus masters (e.g. DMA).

A mapping formula shows how to reference each word in the alias region 
to a corresponding bit in the bit-band region. The mapping formula is:

bit_word_addr = bit_band_base + (byte_offset x 32) + (bit_number × 4)
where:

– bit_word_addr is the address of the word in the alias memory region 
that maps to
the targeted bit
– bit_band_base is the starting address of the alias region
– byte_offset is the number of the byte in the bit-band region that 
contains the targeted bit
– bit_number is the bit position (0-7) of the targeted bit
Example

The following example shows how to map bit 2 of the byte located at SRAM 
address
0x20000300 to the alias region:
0x22006008 = 0x22000000 + (0x300*32) + (2*4)

Writing to address 0x22006008 has the same effect as a read-modify-write 
operation on bit 2 of the byte at SRAM address 0x20000300.

Reading address 0x22006008 returns the value (0x01 or 0x00) of bit 2 of 
the byte at SRAM address 0x20000300 (0x01: bit set; 0x00: bit reset).

Also eher ausrechen als Structs verwenden...

von Irgendwer (Gast)


Lesenswert?

eagle user schrieb:
> Damit kann man mit einfachen, schnellen 32-Bit-Zugriffen einzelne Bits
> lesen und schreiben. Der Zugriff auf das RAM ist angeblich ein echter,
> unteilbarer read-modify-write Zyklus. Stimmt das wirklich, auch bei
> einem Fault während die Floating Point Register gerettet werden (oder
> noch schlimmeren Sachen)?
>
Es ist beim BitBanding gar keine read-modify-write mehr sondern nur noch 
ein einzelner (atomarer) write. Dafür das die restlichen Bits in ruhe 
gelassen werden sorgt hier die Hardware

> Auf die Section im RAM sollte man wahrscheinlich nicht selbst zugreifen.
> Wie würde man mit C-Mitteln die richtige Zuordnung der beiden Sections
> machen?
So ganz verstehe ich nicht was du mit deinen zwei Sectionen meinst?
An den BitBandig Adressen befindet sich kein RAM. Der ARM hat nur einen 
einzigen Linearen Adressraum für alles gemeinsam auf dem immer gleich 
zugegriffen werden kann ganz egal ob unter einer bestimmten Adresse 
Flash, RAM, Register, I/O oder sonstwas gemappt ist.

von eagle user (Gast)


Lesenswert?

Irgendwer schrieb:

> Es ist beim BitBanding gar keine read-modify-write mehr sondern nur noch
> ein einzelner (atomarer) write. Dafür das die restlichen Bits in ruhe
> gelassen werden sorgt hier die Hardware

das glaube ich nicht, tim... Das Ziel ist ein beliebiges Wort im 
normalen RAM, das ist 32 Bit breit. Woher soll diese Hardware die 
restlichen 31 Bit kennen, ohne sie vorher zu lesen? Auch dann, wenn die 
Daten keinen Umweg durch die CPU nehmen, weil es eine Spezial-Hardware 
direkt am Bus gibt.

>> Auf die Section im RAM sollte man wahrscheinlich nicht selbst zugreifen.
>> Wie würde man mit C-Mitteln die richtige Zuordnung der beiden Sections
>> machen?
> So ganz verstehe ich nicht was du mit deinen zwei Sectionen meinst?

Die Section im RAM braucht man, damit der Linker diesen Bereich nicht 
für andere Daten benutzt. Außerdem braucht man die RAM Adressen, damit 
man sie in BitBanding Adressen umrechnen kann. Die BitBandig Section 
braucht man nicht, auf diese Adressen kann man natürlich mit beliebigen 
C-Mitteln zugreifen. Aber ich finde
1
int __attribute__((section(".bitband"))) foo;
 schöner als #define.

von Markus F. (mfro)


Lesenswert?

eagle user schrieb:
> das glaube ich nicht, tim... Das Ziel ist ein beliebiges Wort im
> normalen RAM, das ist 32 Bit breit. Woher soll diese Hardware die
> restlichen 31 Bit kennen, ohne sie vorher zu lesen? Auch dann, wenn die
> Daten keinen Umweg durch die CPU nehmen, weil es eine Spezial-Hardware
> direkt am Bus gibt.

Richtig. Es ist immer noch ein read-modify-write Zyklus, allerdings 
atomar ausgeführt (was einem das Sperren der Interrupts bei 
Bitmanipulationen erspart).

von Christian J. (Gast)


Lesenswert?

eagle user schrieb:
> C-Mitteln zugreifen. Aber ich findeint
> __attribute__((section(".bitband"))) foo; schöner als #define

Ich weiss wie das technisch funktioniert aber wie man das nun in C 
umsetzt, daran hapert es. Der Bitband Bereich kann zb im Debugger nicht 
angezeigt werden. Klar kann ich da Variablen reinlegen mit #definde 
(volatile) (*adresse)..... aber die bügelt der Compiler ggf. über. Und 
wie man das Linker File umschreibt weiss ich nicht. Es nützt natürlich 
auch nichts, wenn für jedes Bit ein Riesenaufwand an Umrechnung 
passieren muss. Aber ich habe derzeit ein kleines Problem 320x240 Bits 
unterzubringen in einem 20kb Chip.
Die Beispiele im Netz geben immer absolute Adressen als Ziel an aber 
"dynamische" zb globale Variablen, dazu findet sich nichts. Es entsteht 
ja erst die Variable an Platz x, dazu muss sie noch in den Bitband 
Bereich bewegt werden durch eine section und dann erfolgt der Zugriff 
über die Bitband Alias Adressen.

edit: Sorry, glaube das ist Müll. Das ganze SRAM ist ja als Bitband 
abgebildet, d.h. jedes Bit meiner 20kb ab 0x20000000 lässt sich in eine 
32 Bit Adresse "weit oben" umrechnen. Und über die erreiche ich dann 
jedes Bit. So legt man zb nur einen Array an char abc[32] und hat dann 
32*8 Adressen oben zur Verfügung um auf die Bits zuzugreifen. Über 
uint8_t *ptr = &abc kriegt man die Adresse des Arrays raus. Diese Alias 
Adressen müssten nur gut verpackt werden, so dass zb ein Array 
entstünde, der [256] Elemente hat, vom Typ Bit ist und schreibt man in 
array[100] rein wird tatsächlich nur das eine Bit gesetzt.

Mal durchgrübeln... kann ja nicht so schwer sein....

https://searchcode.com/codesearch/view/17574679/

von eagle user (Gast)


Lesenswert?

Christian J. schrieb:

> Das ganze SRAM ist ja als Bitband abgebildet, d.h. jedes Bit meiner
> 20kb ab 0x20000000 lässt sich in eine 32 Bit Adresse "weit oben"
> umrechnen. Und über die erreiche ich dann jedes Bit. So legt man
> zb nur einen Array an char abc[32] und hat dann 32*8 Adressen oben
> zur Verfügung um auf die Bits zuzugreifen.

genau so.

> Über uint8_t *ptr = &abc kriegt man die Adresse des Arrays raus.
> Diese Alias Adressen müssten nur gut verpackt werden, so dass zb ein
> Array entstünde, der [256] Elemente hat, vom Typ Bit ist und schreibt
> man in array[100] rein wird tatsächlich nur das eine Bit gesetzt.

Naja, nach der Umrechnung von ptr auf die BitBand Adresse hast du ja 
einen int Pointer auf array[0]. Damit kannst du doch alles machen.

von Christian J. (Gast)


Lesenswert?

eagle user schrieb:

> Naja, nach der Umrechnung von ptr auf die BitBand Adresse hast du ja
> einen int Pointer auf array[0]. Damit kannst du doch alles machen.

#define BITBAND_ALIAS_ADDRESS(addr, bit) \
    ((volatile uint32_t*)((((uint32_t)(addr) & 0xF0000000) + 0x02000000) 
\
                          +((((uint32_t)(addr)&0xFFFFF)*32)\
                          +(  (uint32_t)(bit)*4))))

Alsoooooo.... mal aus der Hüfte geschossen:

uint32_t ptr = BITBAND_ALIAS_ADDRESS(myarray, 0)

Mit * oder &, müsste ich erst kompilieren.

Mit *(ptr++) würde ich dann die Elemente als Bit vorfinden und hätte die 
Pixel meines Displays, statt auf 76.kb, dann auf 320x240 Bit = 9600 
Bytes. Natürlich uimgerechnet auf 2D Array mit x,y.

Cool... direkt mal ausprobieren heute abend. der GDB Debugger streikt 
leider im Bitbandbereich, "cannot read memory".

von W.S. (Gast)


Lesenswert?

Christian J. schrieb:
> Mit fällt auf Anhieb nur ein einen  Bit Struct zu definieren:

Das ist hier der grundfalsche Gedanke per se.

Also: manche Hersteller haben für manche µC etwas eingeführt, was man 
mit Bitbanding bezeichnen kann, aber das ist erstens nur ein Schlagwort 
und zweitens nicht überall implementiert, also relativ unportabel.

Hintergrund ist, daß die einzelnen Bits der allgemeinen Port-Register 
nicht in einem 8, 16 oder 31 Bit Portregister angesprochen werden (was 
man parallel zum Bitbanding aber trotzdem immer noch tun kann), sondern 
es ist irgendwo im Adreßbereich des µC ein Hardware-Array angeordnet, 
das aus einzelnen Boolean-Variablen besteht und hinter jeder derartigen 
Variablen steckt ein einzelnes Port-Bein. Damit kann man einzelne 
Portbeine separat als Boolean-Variablen ansprechen (0 oder 1). Ob 
allerdings so eine Variable 8 oder 16 oder 32 Bit groß ist, also ob man 
sie als bool oder longbool oder weiß der Geier ansprechen muß, kann nur 
das RefMan klären.

Je nachdem, wie gut du beim Formulieren von #define Konstrukten bist, 
kannst du dir diese Arrays von Hardwareregistern selber definieren oder 
du benutzt dazu ne kleine Assemblerquelle, aus der heraus du diese 
Arrays exportierst. Dann eben in deinen C-Quellen
extern longbool Port_A_Pins[32];
oder so ähnlich.

W.S.

von (prx) A. K. (prx)


Lesenswert?


von Robert S. (robert_s68)


Lesenswert?

setzt ein bit:
1
void set_bit(uint32_t* addr, const int bit)   { *((volatile uint32_t*)(32 * (((uint32_t)addr) - 0x20000000) + 0x22000000 + 4 * bit)) = 1; }

Das Ziel muss als volatile deklariert sein, ansonsten ist nicht sicher 
dass das Programm das gleich mitbekommt wenn per bitband manipuliert 
wird.

Der Zugriff auf die Speicherstelle erfolgt mit der selben Breite wie der 
Zugriff auf die Alias-Region. Schreibzugriffe erfolgen auch als 
read-modify-write, die bits sind also nicht wirklich separat 
ansprechbar.

Sinn macht das hauptsächlich um sich ein disablen von interrupts zu 
sparen, eventuell wirds beim manipulieren von einzelnen bits auch etwas 
schneller und oder kleiner als die herkömmliche methode.

von Christian J. (Gast)


Lesenswert?

Also,

https://searchcode.com/codesearch/view/17574679/

das hier läuft bei mir nicht :-(

volatile uint32_t foo;
volatile uint32_t *adress;

WRITE_BITBANDING(foo,0,1);

foo wird nicht 1.

adress = BITBAND_ALIAS_ADDRESS(foo,0);

Hard Fault Error.

Tja...

von Christian J. (Gast)


Lesenswert?

Das klappt :-)

void set_bit(uint32_t* addr, const int bit)   { *((volatile 
uint32_t*)(32 * (((uint32_t)addr) - 0x20000000) + 0x22000000 + 4 * bit)) 
= 1; }

ergibt:

(48)      set_bit(&foo,0);
0800123E  ldr  r0, [pc, #364]  ; (0x80013ac <main+372>)
08001240  movs  r1, #0
08001242  bl  0x8001210 <set_bit>

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.