Sida 1 av 2

Bra design av SW FIFO

Postat: 11 juni 2012, 11:44:29
av Korken
Godagens!

Jag arbetar lite med att skapa en SW FIFO för att kunna samla "stora" mängder data och sedan processera allt istället för att arbeta på byte efter byte som kommer farandes.
Jag designade först såhär, men det finns ett problem. Hur kan jag göra så tex en ISR för högre prioritet och kan lägga in data i FIFOn när den behöver, även när ett användarprogram har låst den, och att mina applikationer får sedan fortsätta sitt arbete.

För just nu så kan det bli ett race-condition mellan en ISR som preemptar en FIFO skrivning/läsning och så ser ISRen att den är upptagen och fastnar i en oändlig loop.
Så programmet väntar på ISRen och ISRen väntar på programmet. Inge bra.

Jag kommer inte på någon bra lösning på detta problem just nu och skulle gärna ha lite tips!
Kanske att man ska ha någon form av tvånivå-system där pherialen för skriver till en liten egen buffer och sedan att den vidarebefordrar det till den stora när den inte är låst?

kanske att jag attackerar problemet med fel vinkel?

fifo.h

Kod: Markera allt

#ifndef __FIFO_H
#define __FIFO_H

typedef struct
{
	FlagStatus locked;
	uint8_t *base_addr;
	uint16_t read_addr;
	uint16_t write_addr;
	uint16_t size;
} fifo;

void fifo_push(fifo *, uint8_t);
uint8_t fifo_pop(fifo *);
void fifo_flush(fifo *);

#endif
fifo.c

Kod: Markera allt

#include "fifo.h"

/**********************************************
 *
 * Software FIFO for use of different applications.
 * OBSERVE! It is only allowed to use the FIFO via these
 * functions, manual change is prohibbited and unpredictable.
 *
 **********************************************/

void fifo_push(fifo *fifoblock, uint8_t data)
{
	// If the FIFO is busy, wait here
	while(fifoblock->locked == SET);

	fifoblock->locked = SET;
	{
		kod kod kod...
	}
	fifoblock->locked = RESET;
}

uint8_t fifo_pop(fifo *fifoblock)
{
	// If the FIFO is busy, wait here
	while(fifoblock->locked == SET);

	fifoblock->locked = SET;
	{
		kod kod kod...
	}
	fifoblock->locked = RESET;


	return 0;
}

void fifo_flush(fifo *fifoblock)
{
	// If the FIFO is busy, wait here
	while(fifoblock->locked == SET);

	fifoblock->locked = SET;
	{
		kod kod kod...
	}
	fifoblock->locked = RESET;

}

Re: Bra design av SW FIFO

Postat: 11 juni 2012, 11:59:18
av bearing
Sånt där brukar väl lösas genom att stänga av interrupt i rutinen, eller innan rutinen anropas.

Re: Bra design av SW FIFO

Postat: 11 juni 2012, 12:27:19
av labmaster
Väntar den med att bearbeta data till FIFO:n är full? Normalt har man ju en inpekare och en utpekare till cellen i FIFO:n och så kollar man att dessa inte passerar varandra.

Berätta lite mera om vilken princip du tänkt dig att flödet skall vara i FIFO:n mellan indata och bearbetning så kanske jag kan ge dig några alternativa förslag.

Re: Bra design av SW FIFO

Postat: 11 juni 2012, 12:37:44
av sodjan
Som bearing säger, du får så klart stänga av interrupt när interrupt inte kan tillåtas.
Det är inte något som helst unikt för just detta exempel, det gäller generellt.

> och så ser ISRen att den är upptagen

ISR'en ska över huvudtaget inte köras om FIFO'n är "upptagen" på ett sådant
sätt att andra samtidiga accesser skulle bli ett problem. Det beror ju mycket
på den övergripande designen av det hela, men just justeringen av FIFO'ns
pekare kan vara ett sådant moment som inte får avbrytas.

Men att tillfälligt stänga av interrupt är helt normalt.

Att du fastnar i en dead-lock (main blockerar ISR och ISR blockerar main) är
ju för att din kod tillåter att det kan hända.

Man brukar också tala om "atomic sequences" när det handlar om kodsekvenser
som måste köras igenom utan avbrott. Det gäller ofta t.ex buffertar (som i ditt fall)
eller flaggor, semaforer, locks eller andra objekt vars accesser måste koordineras.

Re: Bra design av SW FIFO

Postat: 11 juni 2012, 12:50:31
av sodjan
> och så ser ISRen att den är upptagen

Hur gör den det?
Vad betyder "upptagen"?

> // If the FIFO is busy, wait here
> while(fifoblock->locked == SET);

Hur har du tänkt att det där över huvudtaget ska fungera ?
Om procssorn sitter fast där, hur ska FIFO-låset kunna släppas ??

Enda alternativer verkar vara en annan ISR som (enbart) släpper låset
utan att själv först testa det och också fastna i samma loop...

Re: Bra design av SW FIFO

Postat: 11 juni 2012, 13:00:08
av jappelino_1
Du kan nog undvika en del problem och göra en del av designen enklare om du bygger fifot som en dubbelbuffer.
Fifot fyller på ena halvan och när det är klart får applikationen läsa den.
Du kan hålla reda på vilken del som är låst med en flagga och ha ytterligare en eller två flaggor för att markera
när en halva är full.
På det viset kan du också hålla reda på om applikationen har missat och läsa ut en halva innan ISR behöver skriva i denna.

Det här tillvägagånssättet var vanligt förr för bildskärmar och talkodare (vet inte hur man gör idag dock).
En nackdel är att du får en fördröjning i systemet som är proportionellt mot bufferstorlekarna.

Re: Bra design av SW FIFO

Postat: 11 juni 2012, 13:03:49
av Icecap
Jag använd själv en FIFO-funktion till t.ex. att mellanbuffra utskrifter till en UART.

Essentiellt för rätt funktion är att interrupt stängs av just när det läggs in data i FIFO'n (eg. en cirkulär buffer) och när just den bit är klar slås den på igen. Det är ett fåtal instruktioner som ska utföras under tiden interrupten är avstängd och jag brukar använda den globala interrupt enable som finns.

Re: Bra design av SW FIFO

Postat: 11 juni 2012, 13:05:16
av Korken
Här kommer lite mer funktionell beskrivning:
Den har en in- och en utpekare, och lite bokföring håller koll på så vis om FIFOn är tom/full.
Det ramlar in, i ganska så stadigt takt, data i FIFOn och när detta händer (går från tom till inte tom) så avblockeras en rutin i mitt RTOS som tar hand om datat.
Men den har ganska låg prioritet så, så länge FIFOn är under 90% full så kommer den vänta om det finns funktioner som har högre prioritet (annars så börjar den bearbeta datat på en gång).
Går FIFOn över 90% får den medium prioritet och över 95% får den top prioritet för att det inte ska bli en overrun.

sodjan:
Den har tillgång till FIFOns pekare och kan på så vis se om den är "locked" för den måste också veta vart den ska skriva till.
De som jag glömde säga som är viktigt är att detta körs i ett RTOS så när den kommer till och fastnar i while(fifoblock->locked == SET); så tillåter OSet en context switch och får återkomma senare till den funtionen.
Tanken är att while-satsen ska utökas för att sedan innehålla bokföringen för att OSet ska kunna göra en korrekt context switch.

jappelino_1:
De är en ganska så bra idé. jag ska testa den och se hur bra det fungerar i mitt fall.

Re: Bra design av SW FIFO

Postat: 11 juni 2012, 13:19:02
av kimmen
Ja ett sätt är väl att stänga av avbrottet som kan skriva medans annan kod än avbrottsrutinen pillar i FIFO:n. Har man ett OS med flera trådar som skall kunna läsa ur FIFO:n får man ju också se till att bara en tråd i taget går in i det blocket. FIFO-strukturen kommer garanterat att vara olåst om man lyckas ta sig in i avbottsrutinen.

Men det går också att göra en FIFO där ingen låsning behövs mellan skrivning och läsning. Har man endast en tråd/ett avbrott för varje roll behövs då ingen låsning alls. Det görs genom att skrivaren är exklusivt ansvarig för att uppdatera skrivpekaren och läsaren för läspekaren. Se t.ex:
http://www.codeproject.com/Articles/435 ... r-Circular

Re: Bra design av SW FIFO

Postat: 11 juni 2012, 13:27:39
av kimmen
Det kanske också kan vara värt att påpeka att

Kod: Markera allt

   while(fifoblock->locked == SET);

   fifoblock->locked = SET;
   {
      kod kod kod...
   }
   fifoblock->locked = RESET;
inte blir en korrekt mutex. Två trådar kan köra while(fifoblock->locked == SET); "samtidigt" varefter båda är inne i blocket som skall låsas, samtidigt. Har man ett OS bör det finnas funktioner där som fungerar korrekt och dessutom ser till att växla tråd till en som kan köras och kanske dessutom ser till att det inte blir deadlock om trådar med olika prioritet försöker gå in.

Re: Bra design av SW FIFO

Postat: 11 juni 2012, 13:29:35
av labmaster
Bygg ett kösystem med buffrar som innebär att ISR:en aldrig behöver hamna i lock mode. Ty du kommer alltid att hamna i deadlock om du sätter ISR:en i wait genom en loop eftersom det antagligen inte finns någon annan ISR med högre prioritet som kan låsa upp bufferten.

Ta fram papper och penna och gör ett state diagram innan du ger dig på att skriva programkod så att du får stenkoll på flödet. Annars hamnar du lätt i en situation med try and error som ödslar massor med dyrbar tid du kan nyttja till bättre saker.

Fundera också på om det kan vara läge för att ISR:en sparkar igång beräkningsprocessen på något sätt. Jag har gjort detta tidigare i en applikation med signalbehandling.

Re: Bra design av SW FIFO

Postat: 11 juni 2012, 13:39:30
av Korken
Tackar! De va bra tips!
Jag ska nog ta ett par steg bakåt och sätta mig och skissa igenom detta till fullo innan jag kastar mig in i något utan att ha koll. För det va ett mycket mer avancerat problem än vad jag trodde från början.
Jag återkommer säkerligen med fler frågor när jag börjar få snurr på det. :)

Re: Bra design av SW FIFO

Postat: 11 juni 2012, 13:40:29
av sodjan
> De som jag glömde säga som är viktigt är att detta körs i ett RTOS

"Viktigt" var ju ett understatement... :-)
Det ändrar ju i princip hela bilden. Förrutom de vanliga
prylarna med processorn (stänga av interrupt o.s.v) så
har du ju även RTOS'et att ta hänsyn till.

Ja men då får du ju skriva din kod på ett sådant sätt så
att just det RTOS du använder blir "nöjt", så att säga.

Så som du beskrived det från början betyder alltså att
RTOS *inte* avbryter en pågående ISR? Annars skulle
det ju aldrig bli låst !? Eller är problemet att du har två
parallella trådar i RTOS som samtidigt ligger i den där while
satsen ? Och alltså väntar på samma lås ? Feldesign, i så fall. :-)

Om bufferten är "låst" så måste det finnas *EN* rutin som redan
är inne i koden som avslutas med att bufferten låses upp. Lite som
ett rum som låses inifrån, det måste alltid vara *någon* i rummet
om det är låst, annars ska det vara olåst... :-)

Sen så borde väl även RTSO'et ha möjlighet till att skapa
"atomic" sekvenser, alltså då man tillfälligt stänger av
context switch'ingen, om den ställer till det.

Re: Bra design av SW FIFO

Postat: 11 juni 2012, 14:30:08
av Korken
Hehe, jo de va "viktigt" ;)

Jo, RTOSet kan se till så varken context-switch eller ISR händer, ett sk "critical area".
Jag har funderat på att använda det men är lite osäker på om det kommer ge någon oförutsedd bieffekt som sedan kommer bli ett *** att felsöka.
Men jag ska testa det också. :) Ska bara först rita upp allt så jag känner att jag har koll innan jag tar tjuren vid hornen.

Re: Bra design av SW FIFO

Postat: 11 juni 2012, 20:44:13
av Johanb
Finns det ingen buffer/queue-funktion i OSet du kan använda dig av eller är det olämpligt av någon anledning?
Vilket OS är det?