Jak zvětšit návratový zásobník v AVR Studiu?

Zdravím,

Potřeboval bych zvětšit návratový zásobník v AVR studiu.
V ASM to umím, ale jak na to v Céčku ?

Co je “návratový zásobník”? Myslíš STACK? Ten nejde zvětšit žádným způsobem v asm ani v C. U AVR je umístěn na konci RAM, roste dolů a je tedy dán její velikostí (její volné části). Můžeš si ho akorát zmenšit. Na PC je paměťových prostorů povícero a možná s nima půjde manipulovat, ale tady o ničem takovém nevím ani jsem na to v manuálu gcc nenarazil.

Jde mi o to, že po 4 násobném volání funkce se mi program skočí na náhodné místo v programu.

Main() -> funkce1() -> funkce2() -> funkce3() -> funkce4()

Funkce4 mi ještě projde, ale už se nevrátí tam kam má.

Ano, ale lze (alespoň v ASM) velikost stacku reservovat,
takže se ti tam pak necpou data z jiných částí programu.

Jak si ho chceš rezervovat? Prostě když se do paměti nevejdeš, můžeš si rezervovat co chceš, stejně ti to padne.
Stack má maximální možnou velikost a ta je rovna volné ram. To prostě nejde nijak zvětšit. Pokud si paměť zaplácneš tunou zbytečných globálních proměnných, máš smůlu.
Myslíš, že když máš 128B paměti, nacpeš do ní 100B pole a rezervuješ si 64B stack, že ti to projde? Ani omylem. Velikost paměti se zkrátka nedá obejít.

Když se ti data necpou do rezervovaného stacku, kam se tedy cpou? Nemají přeci kam a program nemůže fungovat.

Spíš zkus nadhodit kód a typ mcu. Uvidíme, zda ho nezoptimalizujem. Taky jsem s tím měl už problém, ale změnou paměťové třídy se to vyřešilo.

U tebe spíš tipuju mnoho parametrů nebo mnoho vracených dat.
Teď nechci kecat, ale možná je možné vynutit si funkci jako “inline” (bez volání a využívání stacku za cenu zvětšení programu).

Ještě se můžeš mrknout do manuálu gcc:
11.22 How to detect RAM memory and variable overlap problems?

Jak píše pityy - stack můžeš jenom zmenšovat ale compiller myslím nastavuje addresu na max - ty si ale můžeš nastavit ručně v Project -Configuration Options - Memory settings adresu jinou, ale samozřejmě menší - možná by stálo za to se tam podívat, jestli tam něco nastaveného nemáš. Jinak myslím, že vícenásobné vnořování funkcí vůbec nevadí - je to opravdu spíš záležitost toho, že máš RAM zahnojenou datama a stack ti přerůstá do ní - já mám v jednom programu i 12 vnořených funkcí, ale celé to zabírá jen 200 bytů. Taky jde o to - a to ještě neznám, jestli GCC neukládá při volání funkce vždy všech 32 registrů +SREG- doufám, že ne. Ale i to by dělalo při tvých čtyřech funkcích nějakých 140 bytů, jestli dobře počítám - 432 registrů+4SREG + 4*2 byte návratová adresa ovšem bez použití přerušení. Ale jestli se tak děje, tak si myslím, že by to mělo při překladu hodit minimálně varování, když už ne chybu.

GCC má registry rozdělené do několika skupin dle toho, jak je používá. Rozhodně se všechny neukládají. Jako dočasné používá horní sadu až k pointerům s tím, že přes některé se předávají parametry. Pushuje se jich relativně málo a pak ješte sreg. Pokud je parametrů moc, použije se stack. Stejně tak se stack použije pro funkce s ppp (jako printf a další nesmysly). Další zabraná paměť může být pomocí malloc, pro který se vytváří heap za datovou sekcí (glob. a static. proměnnýma).

Problém jsem již nalezl. Nedocházelo ni k přetečení stacku ani k přímému zásahu z jiné proměnné.

Já (ani původní autor mnou přebraného programu) si neuvědomil,
že parametry funkcí se také uchovávají ve stacku.
No a právě v té funkci3 se měnil (přepočítával) vlastní parametr.
Dál už si jen domýšlím, že během výpočtu pravděpodobně docházelo
k přetečení proměnné a tím zasahoval do návratové adresy.

Jakmile jsem tento zádrhel vyřešil, tak najednou vše funguje bez problémů.

Přetečením parametru nemůže dojít ke změně okolních bytů.
Ovšem jiná věc by byla při bitových operacích. V manuálu GCC se píše něco ve smyslu, že při bitových operacích se 8b proměnná povyšuje na 16b pokud tam výslovně nenapíšeš, že to má být 8b. Ani tak by ale překladač neměl zasahovat do okolních bytů. Běžně to nedělá, otázka je, zda třeba nemá chybku při práci se stackem.
To by chtělo vyzkoušet. Bohužel momentálně není čas :frowning:

Mohl bys vložit ten kus problémového kódu?

Tady je:

[code]
#define CHAR 0
#define INT 1
#define LONG 2

//***************************************************
//Function to transmit a single byte
//***************************************************
void transmitByte( char data )
{
while ( !(UCSR1A & (1<<UDRE1)) ); /* Wait for empty transmit buffer /
UDR1 = data; /
Start transmition */
return;
}

//***************************************************
//Function to transmit hex format data
//first argument indicates type: CHAR, INT or LONG
//Second argument is the data to be displayed
//***************************************************
void transmitHex( char dataType, unsigned long data )
{
char count, i, temp;
char dataString] = "0x ";

if (dataType == CHAR) count = 2;
if (dataType == INT) count = 4;
if (dataType == LONG) count = 8;

dataString[count+3] = 0x00;
for(i=count; i>0; i–)
{
temp = data % 16;
if((temp>=0) && (temp<10)) dataString * = temp + 0x30;
else dataString * = (temp - 10) + 0x41;

data = data/16;
}

for(i=0; i<count+2; i++)
transmitByte(dataString*);
}[/code]

Problém dělala proměnná “data”.
Jakmile jsem tuto proměnnou zkopíroval do zvláštní proměnné,
chyba pak jako zázrakem zmizela.***

No já nevím, ale mně by asi nikdy nenapadlo dělat jakékoliv operace přímo s parametrem - myslím tím jakékoli změny na něm - ne porovnávání, nebo kopírování ho někam. A jak je vidět má to něco do sebe.

A pokud je to tak, že se ukládají do stacku i parametry, tak by asi bylo účelnější pokud budou parametrem nějaké ty longy, floaty, či pole dávat jako parametr ukazatel na ně - nebo ne?

Pointerem na primitivní datový typ si moc nepomůžeš (pokud tu proměnnou nechceš měnit a po skončení ji mít změněnou ve volající funkci). On totiž i ten pointer zabere 2B a pak režie s jeho používáním… U pole to význam má, ale jelikož název pole je sám o sobě pointer na jeho začátek, tak je to jedno. Z toho také vyplývá, že pokud pole změníš ve funkci, po jejím ukončení máš k dispozici již změněné pole - pracuje se na původních datech, nikam se nekopírují. Pokud potřebuješ ve funkci pracovat pouze s kopií pole, musíš si ji ručně vytvořit.

Co význam má je sdružování proměnných do struktur. Pokud totiž předáváš hodně parametrů, zacpal by sis stack a procesor by se docela napočítal než by se k proměnným dostal. Kdežto pokud jsou ve struktuře, tak předáváš pouze její název, což je opět pointer. Ve funkci si pak překladač naplní pouze 1 hw pointer (x,y,z) a pomocí něj může přistupovat ke kterémukoli prvku ve struktuře během 1 instrukce. Kdybys předal parametrů hejno samostatně, tak před přístupem ke každému z nich by si ten hw pointer musel přepisovat a to ho zpomaluje.
Narazil bys na to i v AVR035 Efficient C Coding.

Jenom pro zajímavost, ta funkce transmitHex() se dá stručně napsat takto

[code]void transmitHex(unsigned long data)
{
char tempstr[10];

uart_puts(“0x”);
uart_puts(ultoa(data, tempstr, 16));
}
[/code]