sizeof()

struct __attribute__((__packed__)) whatever
 {      unsigned int  f1: 2;
        unsigned int  f2: 5;
        unsigned int  f3: 5;
        unsigned int  f4: 6;
        unsigned int  f5: 6;
        unsigned int  f6: 8;
 };

[...]

sizeof(struct whatever) = ?

Hozzászólások

>>> (2+5+5+6+6+8)/8
4.0

A strange game. The only winning move is not to play. How about a nice game of chess?

C forditokon ennyi, errol szol a packed. Ha nem packed, akkor persze sebesseg miatt szetszabdalhatja. Ha valami binaris input ertelmezesere kell neked, akkor meg mindig nem erre valo - fuggetlenu attol, hogy neha mukodik.

Nem C forditokon megint csinalhat barmit - ideertve a Microchip termekeit is.

A strange game. The only winning move is not to play. How about a nice game of chess?

Szerkesztve: 2022. 11. 26., szo – 10:49

Szoval igen... ez valoban mindenhol 4, kiveve PIC18 alatt... Ebbe beleszaladtunk a minap. Valaki PIC guru ra tudna nezni hogy mit lehet tenni hogy 4 legyen? :) 

Opcionalisan ennek a forditottja is erdekel:

struct __attribute__((__packed__)) whatever
 {      unsigned int  f6: 8;
        unsigned int  f5: 6;
        unsigned int  f4: 6;
        unsigned int  f3: 5;
        unsigned int  f2: 5;
        unsigned int  f1: 2;
 };

Buta fordítóval én is találkoztam. PIC18-ra a legbutább C fordító, amivel találkoztam, az még az ANSI C szintet sem tartotta. Volt olyan kifejezés, amelyet szét kellett szednem neki, mert nem tudta lebontani regiszter szintre.
A GCC és CLANG fordítók jól elkényelmesítettek engem is.

A legbosszantóbb, amikor a fordító rinya nélkül megeszi a szintaktikai cheet-eket ( __attribute__ , #pragma, ... ) és közben a háttérben nem hajtja végre.

XC8 C Compiler
User’s Guide

D.10.4 The order of allocation of bit-fields within an int (6.5.2.1)
The first bit-field defined in a structure is allocated the LSb position in the
storage unit. Subsequent bit-fields are allocated higher-order bits.
D.10.5 Whether a bit-field can straddle a storage-unit boundary (6.5.2.1)
A bit-field cannot straddle a storage unit. Any bit-field that would straddle a
storage unit will be allocated to the LSb position in a new storage unit.

4 vagy több. A bitfieldekről kb. annyit ír elő a szabvány, hogy a fordító azt csinál belőle, amit csak akar.

Muszáj neked ilyen bitmező? Nem jó, ha shifteléseket, maszkolásokat írsz helyette? Vagy írd assembly-ben, ilyen kis MCU-ra elég béna dolog C-ben programozni.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Ez a bitfield egy elegge osszetett/rendszerkritikus kommunikacios protokoll resze, es a kapcsolodo konyvtarat/konyvtarakat eddig sikerrel, valtoztatas nelkul hasznaljuk mindenfele architekturan - x86, x86_64, ARM Cortex-M*, MSP430, AVRx, ezutobbiba beleertve a sajat soft CPU-s processzorokon/SoC-okon futtatott programokat is. Most nezzuk PIC-en, igazabol ezt a bitfieldet leszamitva az is megy. 

Nem jó, ha shifteléseket, maszkolásokat írsz helyette?

De, vegul ez lesz/lett, belekerult egy plusz reteg plusz egy ENABLE_BITFIELDS jellegu feltetel, par makro, meg egy compile-time assert a sizeof()-ra. Ettol fuggetelnul azert erdekel a dolog hogy ez miert nem megy. Oke, a fenti listabol minden arch gcc-vel (is) fordulhat, kiveve a PIC. Ez vsz szamit...:)

Vagy írd assembly-ben, ilyen kis MCU-ra elég béna dolog C-ben programozni.

Nincs nagy ellenvetesem az assemblyvel kapcsolatban, sot, sok dialektusat gagyogom is - de ennyit azert nem akarnank atirni. Ezekre a problemakra adasulra a gcc eleg jo kodot csinal, mondjuk ugy :)

Vegyük észre, hogy a felsorolt architektúrák legalább 16 bitesek, kivétel a PIC!

Inkább béna dolog a <=PIC18-ra C programot írni. Igaz, a gyártó szerint az extended utasításkészlet (bocsánat:  C Compiler Optimized Architecture for Re-Entrant Code) meg egy ordas nagy hazugság. Pl. a 8086 magasszitű programnyelvek támogatása == a stack frame kezelés támogatása. Na, itt meg még stack sincs, azt is csak emulálni lehet. Ha eltekintünk attól, hogy bármit bármire meg lehet írni, akkor a 8 bites PIC nem támogatja a magasszintű nyelveket. Ennyit az assemblyről. ;)

Valószínűleg nem az én véleményem számít, de talán "a gyártóval kompatibilitás miatt" maradt 1 bájtos a bitfield.

Vegyük észre, hogy a felsorolt architektúrák legalább 16 bitesek, kivétel a PIC!

Hát, az AVR az nyócbites mint a fene! Szoval nem, nem ez a különbség és a hasonlóság között... Persze ott banking helyett van valódi stack, ami azért segít. Nagyjából mindenen, kivéve pont a bitfieldeken, mert ahhoz pont semmi köze :)

Igaz nyócbites.

De.

Az AVR regiszterek száma R0..R31=32, míg a PIC esetében W=1 (egy, one, stb.). Beláthatod, hogy az 1 db regiszter nem fogható össze 16 bites regiszterpárrá, mint az AVR esetében pl. az X, Y, Z. Ez a CPU tipikusan a 6800 < 8080 < 68000 - az utóbbi a kifejezetten sok regiszteres, magasszintű nyelvek támogatására készült szerkezet.

A PIC18 stack 31 mélységű dedikált hw stack. Nagyon picit tudod manipulálni, de címezni nem és kevés is a szoftveres felhasználáshoz, tehát a stack frame alkalmazását nem teszi lehetővé. Ezt a PIC C-ben dedikált változók és a RAM-ban elhelyezkedő stack helyett szoftverrel emulálják. Pont ezért a stack-nek (dedikált) semmi köze a bankhoz (RAM). A bankolás csak azt teszi lehetővé, hogy egy db egy szavas utasításban mindent meg tudjál tenni azon az áron, hogy csak egy banknyi adatot tudsz címezni. A többi esetben vagy módosítod a BSR értékét, vagy direkt címzésel (csak) mozgatod az adatot. Ezek mind legalább 2 szavas műveletek.

Tehát egyszerű esetben a PIC címtartománya 8 bit+a BSR értéke, amit állítgatni kell néha. A PIC18 már rendelkezik az ACCESS képességgel, amellyel a BSR tartalmától függetlenül a 0. lapon 60H db bájtot címezhetsz, illetve felette a - szerintük - gyakrabban használt SFR-eket. Ezzel minden további nélkül létrehozhatsz akár 96 regisztert, amit a BSR tartalmától függetlenül címezhetsz. Bitfield manipulációs utasításokkal az AVR sem rendelkezik, ezért kb. ugyanolyan befektetéssel a PIC is képes rá. Sajnos extended utasításkészletre kapcsolva megszűnik az ACCESS, marad a BSR, de azzal sem változnak az utasítások. A lényeg: Mindkét MCU kb. ugyanannyi utasítással képes a bitfieldeket több bájton keresztül, ugyanolyan módon kezelni. Hát meg kell írni erre a MCU-ra is, meg kell kezelni a bankolás miatti exceptiont is.

Itt csak a lusta szoftveresekről szól a történet. Esetedben a hordozhatóság is lényeges. Én meg nem hordozom sehova a programomat (azaz jajjdehogyisnem), ezért assemblerben írok. Ebből következően soha nem akadtam össze a fordító side effecjeivel. Nálam csak az a probléma, hogy a mai világban C, C#, java, stb. tudorok írják az assembler fordítót, aminek egyenes következménye, hogy a 40 éve jól működő dolgokat nem lehet használni. Helyette C szintaxissal csak hiányos és korlátozott lehetőségeim vannak.

Tényleg van különbség. A PIC jó iskolapélda a legbutább, még életképes architektúrák egyikére.

AVR sok CPU munkaregisztert és normális index regisztereket tartalmaz: https://microchipdeveloper.com/local--files/8avr:gpr/gpr.png
PIC egyetlen Work regisztert és CPU utasítás híján sok, valójában CPU alapfunkciót is már memóriacímbe bújtatott hardvertámogatással csinál.

Például vegyük az alábbi példát: https://c.godbolt.org/z/ohhWda4cM
PIC az indirekt címzésre nem tartalmaz CPU-ban lehetőséget, helyette külső hardvertámogatása van erre az FSR és INDF memóriacímeken keresztül.
Erre nem egyszerű átszelidíteni egy normál C fordítót, ez inkább zsonglőrködés.

Azért a #define csodákat tud ám művelni, ha valamit nem értesz. ;)

A "külső hardvertámogatás" éppúgy a cpu része, mint az a tény, hogy AVR esetén éppen az r27+r26 == X. Pl. a 8080-nál a (HL) neve M regiszter. Azaz az A, B, C, D, E, H,  L regiszterek mellé került egy M, amit ugyanúgy használhatsz, mint a többit,  csak éppen a HL regiszterpárral címzett bájtot jelenti.

Képzeld el, hogy FSR0 = X, FSR1=Y és FSR2=Z, FSR0L=XL=R26 (stb.) és máris AVR-t fogsz látni. Az st X=mov?? INDF0, az st X+ = mov?? POSTINC0 ... stb., tehát X=INDF0, X+=POSTINC0, stb. Az, hogy miként valósítja meg a cpu az az utasításkészlet és a regiszterek számának függvénye.

                            MOVLB   15          (extended módban)
        ldi r30,lo8(a)      LFSR    FSR0, a     (X=&a, mivel &a statikus)
                                                (de AVR Z=&a ??????)
        ldi r31,hi8(a)
        ldi r24,lo8(20)     MOVLW   20          (W=i=20)
        mov r26,r30                             (AVR X=Z ??????)
        mov r27,r31
0:
        st X+,__zero_reg__  CLRF    POSTINC0    (*X++=0)
        dec r24             DECFSZ  WREG
        brne 0b             BRA     0:
        ret                 RET

Én nem látok különbséget.

A "külső hardvertámogatás"azért is poénos, mert a nyomorult processzor egyetlen nyomorult W regisztere is "külső hardvertámogatás" segítségével is elérhető.

Pl. a POSTINC0 az FEEh, míg a W (WREG álnéven) az FE8h címen tartózkodik. Így aztán egy regiszter sincs, csak "külső hardvertámogatás".  Szerinted.

Extended módban már primitív frame pointer / enter-leave technika támogatás is van ezekre az indexregiszterekre.

Nem a "nem lehet megcsinálni" a kérdés, hiszen C fordítót látsz PIC-re is. Viszont hogy eltér a "normális" processzortól, amelyben sok regiszter, index regiszter stb. van, az nem kérdés.
Ezért a GCC-t relatív könnyű volt x86 és sok más architektúra mellett AVR kódgenerálásra rávenni (gcc-avr), miközben sokkal mélyebben át kellett volna írni a lelkét, ha PIC-re is akarták volna portolni.

A PIC-re írt fordítók pedig "butábbak" egy kiforrott GCC-hez képest. Sok alapvető dolgot nem tudnak, némelyiknél az ANSI szabvány (C89) implementálásban is látsz hibát. A C99 és C11 szabványok pedig felejtősek ezeknél.

Azért olvasd el azt is, amit feljebb írtam! Ha kell, van 96 regiszter is. Meg amit előbb írtam! VAN indexregiszter, csak számodra (és a GCC fejlesztői számára) idegen, mert nem a 68000 szerinti általános regiszterhalmaz része.

MPLAB XC8 Compilers support all 8-bit PIC® and AVR® microcontrollers...

Tehát van olyan fordító, amelyik mindkettőt támogatja, vagyis megcsinálható és meg is csinálták.

A magasszintű nyelveket támogató cpu-ra könnyű portolni egy C fordítót. A PIC teljesen más filozófiával készült, szerintem nem is a legutolsó, hanem egyáltalán nem szempont, hogy miiyen szabványnak megfelelő C-t támogat. Van C, ha abban szeretnél programozni vagy létező lib-et használni. Az elsődleges szempont itt a hardver, mert annak alapján választod ki a példányt. Legyen benne legalább 4 db CLC, viszont a C89 tökmindegy. Ugyanaz a kód valószínűleg fog futni egy másik típuson is, amiben van 4 db CLC. Vagyis nem tudsz és nem is akarsz PIC -> x86 irányba hordozható kódot előállitani. ;)  (Na, a topicnyitó az üdítő kivétel.) Talán az 512B flash  (<= 256 utasítás) + 25B ram esetében fontosabb szempont, hogy csak 6 vagy 8 lába van.

A PIC esetében nem kell "okos" fordító, mert berúgod a hardvert és működik. Ez az egyszerű dolog C-ben általában bonyolultabb módon írható le.

Kedvedért itt a mintafüggvényed XC8 fordító C99 módban, kísértetiesen hasonlít az én pontatlan kódomra.

7D7E  EE00     LFSR 0, 0x1           ;X=&a
7D82  0E14     MOVLW 0x14            ;W=20
7D84  6AEE     CLRF POSTINC0, ACCESS ;loop: *X++=0
7D86  06E8     DECF WREG, F, ACCESS  ;W--
7D88  E1FD     BNZ 0x7D84            ;i!=0 -> goto loop
7D8A  0100     MOVLB 0x0
7D8C  EFB8     GOTO 0x7D70

Ennek ellenére nem használok C fordítót, mert a régiek tényleg vállalhatatlanok, az újak meg fizetősek. Ráadásul akkora apparátus kell hozzá...

Érdekességképp a hasonó szerkezetű 8051 sem támogatott, az SDCC kezeli (?),  de a PIC támogatás 2-3 példány után elhalt. Az egyetlen használható asm fordító a gputils, viszon Dunát lehet rekeszteni az elhalt és / vagy működésképtelen ópenszósz PIC utilitiessel.

vagy létező lib-et használni

Pont itt bukott el a dolog, ez inditotta a topicot is... Nem, a szabvanyok nem azert vannak hogy ne tartsuk be. Ha tokon akarom szurni magam, palytonban kezdek el baszkolodni es orulok hogy par honap mulva mar nem fut a sajat vackom... C-nel pont az ellentetes celok vezerelnek :) 

Pedig elég lett volna annyit tudni, hogy nyócbites és elolvasni a dokumentációt. A hardver megközelítése nem a C, python vagy a  java. Igaz van olyan bonyolult hardver is, amit az embedded technológiában a gyártó c99 lib segítségével "specifikál" a felhasználó számára.

A C az egyetlen "hordozható" nyelv, ha eltekintünk a csomó ifdef-től és attól, hogy a fél program pl. a bájt hosszát specifikálja. ;) Nem a "nem lehet megcsinálni" a kérdés, mint írtad. Ebben az esetben egy 8 bites arch nem tudja azt, amit a 32 bites. Hát meg kell írni ÉS a bankolás miatt keletkező kivételkezelést is! Aztán a cpu specifikus ifdef-et berakni a fordítóba.

Érdekességképp ebben találsz bit field manipulációs utasítást egy 8086 "klónban", a 12-21..12-24 oldalakon, de csak 16 bitre.

Hat, ugye mi a helyzet. Ha van egy ilyen kodod, mint pl:

struct whatever1
 {      uint32_t        data;
 };

/* LSB */
struct whatever2
 {      unsigned int    next_bits: 17;
        unsigned int    interesting: 4;
        unsigned int    prev_bits: 11;
 };

struct whatever1 *w1;
struct whatever2 *w2;

w2->interesting = value;
w1->data = ((w1->data) & (~ (((1<<4)-1))<<17 )) | ((value&((1<<4)-1))<<17);

akkor minden jolnevelt C fordito (lenyegeben es/vagy teljesen) ugyanazt csinalja a ket kodbol. Es ez konkretan tapasztaltam is, legalabbis beagyazott cuccoknal ahol deployment elott atnezem az asm reszek kritikusabb reszeit neha (AVRx, ARMv6-M), es mindenhol - ahol nem is nezem at az asm-et - a ketto ugyanazt csinalja, es a sizeof(struct whatever2) ugyanugy 4. Vagyis, mindenhol, kiveve XC8-nal. Ahol a w1->data bit juggling tenyleg azt csinalja amit varunk, de nem lehet az egyszerubb w2->... ertekadast hasznalni. Es ebben a fenti use case-ban az AVRx az ugyanolyan 8 bites mert az X/Y/Z indexinget nem hasznalod ki sehol (max az ld/st-resznel, hogy mondjuk Z-ben van a pointer). PIC-nel meg ha a w1->data zsonglorkodesbol is tok jo kod tud keletkezni (kiprobaltam, mukodik, hasznaljuk), bankolas ide vagy oda, akkor azt a sima bitfieldnel miert nem lehetett... 

Na mindegy, szoval persze, az sajnos lehet egy valid megoldas az egesz kerdesre hogy "a bitfieldek nem definialtak, oszt jonapot", "meg oldd meg ahogy tudod", de... akkor mar az ennyire custom/proprietary C-nel mint az XC8 inkabb dobjon forditasi hibat a bitfieldre minthogy a de facto standardokat ne tudja :/

Most az a divi, hogy az XC8 a támogatott fordító, ami kompatibilis az elődjével a C18 fordítóval. A C18 UG 81. oldal B.9 pont alatt írja le a fordító bit field implementációját. Méghozzá idéző jelek között idézi az ANSI C standard specifikációt + az implementációt. Az XC8 képességei a C18-ból öröklődtek, így kompatibilis a két fordító. Múltkor pedig az XC8 UG alapján írtam le, hogy ott is a bájt a bit field maximális mérete.

ANSI C

3.5.2.1 Whether a bit-field can straddle a storage-unit boundary

Depending upon the overall alignment and the type alignment, a bit field is allowed to straddle a storage unit.

Microchip C18, XC8

Implementation: A bit field cannot straddle a storage unit boundary.

Ez van.

Elgondolkodtam azon is, hogy egy 517 + 2 + 857 bites struktúrát hogyan kellene programozni. Mert ugye C-ben leírod, de mit csinál a processzor? :-D (Pl. max 96M x 2b struktúrát már használtam élesben, de az POWER5-ön ment, nem mcu-n.)

Próbálkozhatsz a Mikroelektronika mikroC PRO fordítójával. Abban legálább 16 bitre megoldották ezt a témát, ha jól látom.

Én is ott látom a csalást, hogy az MSP430 16-bites, az STM32 pedig 32-bites mikrokontroller.
...de abból a hat/nyolc lábból kettő táp, ami eléggé limitálja a felhasználhatóságot, így majdnem mindegy is. :)

Egyébként nem az STM32 a limit a Windows futtatásában, hanem a Windows maga. :)
Viccen kívül Linux futtatása már sikerülhet is, csak nagyon nem ezen a példányon és külső perifériákkal.

Nem is limitálja, ha arra használod amire kell. Van benne 32 bit, RTC, AES, RNG, battery backup - ez egy crypto key-hez elegendő, ha raksz hozzá egy soros-usb átalakítót és egy elemet vagy akkut. Az i2c-re még kijelzőt is rakhatsz.

A 8k ramban nem biztos, hogy elmegy a linux, de soros terminál vagy soros-ip modullal webszerver mehet róla.

Ezzel az STM32-vel én is elkezdtem szemezni, meglehetősen érdekes volt a konfigurátorukban a lábkiosztás beállítása...
...kb. amit lehet, kihoztak abból a nyolc lábból, és alapvetően sem buta az alapja a procinak.

(Általában bizonyos funkciók bizonyos lábakon elérhetőek, némi alternatív lehetőséggel, de itt külön lábak vannak egy fizikai lábra vezetve alternatívaként - tényleg érdekes megoldás.)

A PIC-ekben régen sem lehetett alapértelmezetten az összes perifériát egyszerre használni. Az egyes lábakat vagylagosan és prioritással lehetett kiosztani.

A következő verzió a PPS (peripherial pin select), amivel szinte tetszőleges helyre kötheted az io-kat. Ezen kívül számtalan belső és akár többszörös összekötésre is van lehetőség.

A 8..sok lábú tokokba brutális mennyiségű perifériát zsúfolnak, amivel egész bonyolult hardvert is felépíthetsz. A végén alig kell programozni. ;)

Egy cpu hátránya, hogy minden tevékenység valamilyen órajellel szinkron. Az ötletes elemekkel viszont ki lehet alakítani a külső aszinkron események feldolgozását vagy speciális kimenő jelek előállítását. (Pl. 1-Wire, RGB LED)

Az újabb Q sorozat 50+ perifériát tartalmaz, csak feladatot kell hozzá találni. ;) Olvasd csak el a Product Features fület vagy az adatlap első oldalán nézd meg a táblázatot! És ez csak egy vacak 8 bites MCU.

Ah, igen... az MSP430FR sorozatban is van ilyen 8 labu joszag?

Bar ha a labak szamat nezzuk, tenyleg eleg csak 6 lab, pont ugy mint egy ulitimate enterprise sok U-s szervernek: neutral, live, etx+, etx-, erx+, erx-. Minek ennel tobb? :) Na, ezt a szamitasi kapacitast tegyuk bele egy SOT23-6-ba :] (konnyites, mert beletehetnenk egy SOT753-asba is, szoval ennyi engedmeny azert van).