High-Voltage
Het probleem bij een maatschappelijke samenleving is dat je geen "normale" uitzondering kan zijn...
Hey all,
Ik heb op dit moment een gek probleempje in Pic Basic. Ik bereken een gemiddelde over vier getallen uit een array (van vier). Dit gaat ongeveer twintig keer goed en dan gaat het één keer mis. Op dat moment slaat ie de bal volledig mis. Dus ik krijg bv een twintig tal keer 89 als gemiddelde en vervolgens ineens 133. Eerder kreeg ik dit frequent, maar ik ben tot de vaststelling gekomen dat ik een variabele dubbel had gebruikt, zowel in mijn voorgrondcode als in de interrupt routine. Hierdoor werd deze variabele tijdens een interrupt gewijzigd waardoor natuurlijk mijn berekening de mist in ging. Nu heb ik hiervoor twee onafhankelijke variabelen gebruikt, maar het probleem blijkt nog steeds te bestaan, desalniettemin eerder uitzonderlijk.
Ik kan in de Basic code niets fout vinden en deze code ga ik dus ook niet posten. Er zijn al tien nobele ridders geweest die het zwaard uit de steen wilden trekken, maar geen van hen heeft iets verkeerd gezien in de Basic code.
Daarom zoek ik mijn toevlucht in de ASM file, gegenereerd door de PicBasic compiler. Hopelijk kan ik hierin een onregelmatigheid ontdekken.
ASM lezen lukt nog wel, doch begrijp ik volgende stukje niet helemaal:
Movlw 127
Bsf STATUS,5
ram_bank = 1
Movwf OSCCONHier wordt 127 in het OSCCON register geschreven: Eerst wordt 127 in het w-register geladen, vervolgens wordt van bank gewisseld door PR0 te setten in het STATUS register. Dan staat er echter de lijn "ram_bank = 1". Deze lijn begrijp ik niet. Zover ik weet is ram_bank geen variabele en het bankwisselen wordt de regel ervoor al uitgevoerd door PR0 te setten. Als dit een commentaarregel is, ontbreekt de puntkomma van voor. En als dit een label is mochten er geen spaties in zitten. Tot slot wordt het w-register in het OSCCON register geschreven en is de operatie rond.
Iemand een idee'tje?
Damn, zo een lap uitleg voor zo een stomme vraag.
H-V
Edit:
Hier volgt nog een stukje code dat mijn petje te boven gaat. Deze stukken hebben tot nu toe nog iets met de fout te maken, maar ik zou graag alles verstaan!
Movlw 32
Movwf FSR
Movlw 92
F@Call cl@r
Movlw 160
Movwf FSR
Movlw 80
F@Call cl@r
Movlw 16
Movwf FSR
Movlw 96
F@Call cl@rh
Movlw 144
Movwf FSR
Movlw 96
F@Call cl@rhDeze lap dient om alle ram geheugens te clearen. Hierin komen dingen voor als "F@Call cl@r". Met Call kan je een subroutine aanroepen, vrij logisch, maar wat staat die "F@" er nog voor te doen? Ik kan overigens nerges "cl@r" terugvinden als subroutine.
Later staat er ook een "F@Jump" waarvan ik niet begrijp wat het doel is. De instructie "Jump" bestaat voor zover ik weet niet op een PIC... Kan dit soms iets te maken hebben met macro's (hier weet ik eigenlijk nog niets van) ofzo?
[Bericht gewijzigd door High-Voltage op (18%)]
benleentje
Golden Member
Ram_bank is een compiler directive. Dus een compiler aanwijzing. Zit in proton. Je wissel nu 2 keer naar bank 1.
Proton zelf heeft namelijk ook een aantal variabele die ook mag gebruiken.
Dat laatste stuk code kan ik niet thuis brengen. Waar heb je die code vandaan misschien dat je vanaf daar meer info kan krijgen.
hadv
Just find out what you like and let it kill you
Als je in de asm kijkt die door proton wordt gebakken zie je die @ heel erg vaak. Ze gebruiken die om 'zeker' te zijn dat ze unieke namen genereren.
Ik weet natuurlijk niet welke versie jij gebruikt, maar als ik een bas-file assembleer kan ik perfect zien welke regel op welke wijze in assembler wordt omgezet.
Ergens bovenin de asm-file staat een include van een *.pbp bestand. Hierin kun je al die @malligheid terugvinden.
Als ik zomaar een gok mag doen...een clear routine verwacht 2 parameters: een adres en het aantal bytes te clearen.
Op 1 september 2009 11:42:04 schreef High-Voltage:
Ik kan in de Basic code niets fout vinden en deze code ga ik dus ook niet posten. Er zijn al tien nobele ridders geweest die het zwaard uit de steen wilden trekken, maar geen van hen heeft iets verkeerd gezien in de Basic code.
Daarom zoek ik mijn toevlucht in de ASM file, gegenereerd door de PicBasic compiler. Hopelijk kan ik hierin een onregelmatigheid ontdekken.
Wie weet bevindt Sir Lancelot zich op dit forum.
benleentje
Golden Member
Kan het dan toevallig zo zijn dat als de cpu bezig is met het gemiddelde te berekenen en er een interrupt komt er daardoor iets fout gaat? Of gaat het ook fout als de interrupt zijn uitgeschakeld.
High-Voltage
Het probleem bij een maatschappelijke samenleving is dat je geen "normale" uitzondering kan zijn...
Daar zit inderdaad de fout, zo ver was ik al. De vraag is echter *waarom*. Ik vermoed persoonlijk dat door de interrupt ergens ofwel de carry- ofwel de zerobit wordt verneukt. De carry bit wordt gebruikt bij schuifoperaties (delen) en aangezien ik met 16 bit getallen werk is die carry dus zeker van belang. Het kan ook zijn dat de zero bit wordt verneukt waardoor mn tellertje dat bijhoudt hoeveel getallen uit de array reeds opgeteld zijn verkeerd wordt geïnterpreteerd (bittest operatie op het statusregister). Dat zijn de twee mogelijkheden die ik voor ogen heb.
Als ik kijk hoe de interrupt aangevangen en afgesloten wordt (in de ASM file), lijkt daar toch alles in orde te zijn met betrekking tot context saven.
Om het verhaal nog een beetje straffer te maken: als ik in mn programma de delay tussen twee opeenvolgende berekenen 100 of 1000ms maak dan gaat het mis. Neem ik 99, 200 of 500 dan lijkt het tot nu toe allemaal goed te gaan (interruptsnelheid is ongeveer 1ms).
Verder hangt het ook af van de waardes in mn array. Bij 113 gaat het mis, dan krijg ik af en toe 85 tussendoor. Neem ik daarentegen bv 99 dan blijft hij constant goed gaan. Dit kan erop wijzen dat dat echt uit de carry bit komt.
Ik kan de interrupts helaas niet uitzetten, dan zie ik het resultaat niet meer op mn display. Ik durf echter met 99% zekerheid zeggen dat die interrupt roet in het eten gooit.
Op 1 september 2009 16:44:23 schreef benleentje:
Ram_bank is een compiler directive. Dus een compiler aanwijzing. Zit in proton. Je wissel nu 2 keer naar bank 1.
Hmmm... Als dat een compiler directive is, waarom wordt die erbij gezet? Ze clearen/setten toch al de bijbehorende bits uit het Status register? Zoals je zelf zegt, dan wissel je twee keer van bank. Dat is toch totaal zinloos, of zie ik dat fout?
Vage bedoeling van de Proton compiler.
Dat laatste stuk code kan ik niet thuis brengen. Waar heb je die code vandaan misschien dat je vanaf daar meer info kan krijgen.
Dat komt ook uit de ASM file die de Proton compiler voor mij genereert.
Op 1 september 2009 17:57:29 schreef hadv:
Als je in de asm kijkt die door proton wordt gebakken zie je die @ heel erg vaak. Ze gebruiken die om 'zeker' te zijn dat ze unieke namen genereren.
Klopt, maar meestal staan die tekentjes gewoon in de labels, dat begrijp ik. Alleen die rare aanroepen zoals F@Call en F@Jump begrijp ik niet helemaal. Ik vermoed dat dit macro's zijn. Als ik bv een deling doe dan zie ik dat eerst een aantal onbekende variabelen ingeladen worden met stukken van mijn variabelen en vervolgs volgt zo een rare F@Call met een parameter die lekker vaag is.
Ik weet natuurlijk niet welke versie jij gebruikt, maar als ik een bas-file assembleer kan ik perfect zien welke regel op welke wijze in assembler wordt omgezet.
Ergens bovenin de asm-file staat een include van een *.pbp bestand. Hierin kun je al die @malligheid terugvinden.
Versie 3.2.5.5 van de compiler. De IDE versie is 1.4.6, maar dat is niet zo van toepassing. Inderdaad, in die file is alles terug te vinden, daar ben ik deze middag ook achter gekomen. Het is echter zoooo onoverzichtelijk, je moet in twee of drie files tegelijk gaan zoeken wat er allemaal gebeurt. Ik vrees dat deze aanpak ook niet gaat werken.
[Bericht gewijzigd door Henry S. op (4%)]
hadv
Just find out what you like and let it kill you
Een goede context-save bewaart ook het STATUS-register, daar mag het niet aan liggen. Adviesje: voor je de 'heavy' berekeningen uitvoert even alle interrupts disablen, na de berekeningen weer enablen. Zou kunnen helpen.
En dat je je een slag in de rondte zoekt met al die bestanden is helaas helemaal waar. Maar dat is een quaestie van even fix doorbijten 
benleentje
Golden Member
Vage bedoeling van de Proton compiler.
Waarschijnlijk gaat het eerst van basic code naar asm code en dan pas naar micro code. Om die micro code efficiënter te kunnen maken voegen ze misschien extra directives toe.
Het kan zijn dat in de asm code naar bank 1 gewisseld wordt terwijl dat daar voor ook al is gebeurt en feitelijk niet nodig is. Dit kan dan een manier zijn door aan de compiler die micro code maakt door te geven dat het al in ram_bank 1 is zodat de code verderop over geslagen kan worden.
Hoe communiceert je interrupt routine met het voorgrond programma? Besef je dat dat alleen via bit of byte variabelen kan, niet via integers?
Als je integers gebruik moet je ook gebruik maken van z.g. critical sections, een stukje code waar je alle interrupts even uitzet, je integer een waarde geeft of uitleest, en dan de interrupts weer aanzet.
High-Voltage
Het probleem bij een maatschappelijke samenleving is dat je geen "normale" uitzondering kan zijn...
Op 1 september 2009 19:13:03 schreef hadv:
Een goede context-save bewaart ook het STATUS-register, daar mag het niet aan liggen.
Inderdaad, in de voorbeelden van Microchip wordt enkel maar het W-register en het Status register bewaard. In de Proton compiler gegenereerde files wordt zelfs de PCLatch bewaard!
En het terugzetten van het Status register gebeurt netjes door een swap, dus daar zouden de statusbits ook niet verneukt mogen worden. Inderdaad, zoals de broncode eruit ziet zou alles goed moeten gaan, doch gebeuren er die vage dingen (afhankelijk van de waardes in het array en de delay tussen twee berekeningen).
Adviesje: voor je de 'heavy' berekeningen uitvoert even alle interrupts disablen, na de berekeningen weer enablen. Zou kunnen helpen.
vermoedelijk gaat dat helpen ja, maar daarom is het net een interrupt hé. Altijd op vaste tijdstippen even eruit springen en weer terugspringen. Nu is de timing bij mij niet zooo cruciaal, maar het is ook niet de bedoeling dat die tijd verdubbeld wordt. Het is nu ook zo dat die interrupt als tijdbasis dient voor mn virtuele timers. Dus als ik elke ms ga verlengen tot bv 1.2ms en ik wil dan pakweg een 5000ms timen, dan heb ik eens 6 seconden.
Op 1 september 2009 23:49:28 schreef benleentje:
Waarschijnlijk gaat het eerst van basic code naar asm code en dan pas naar micro code.
Zo gaat dat inderdaad 
Om die micro code efficiënter te kunnen maken voegen ze misschien extra directives toe.
Negatief... Als je zelf in ASM progt zitten die directives er ook niet. Of ik gebruik ze toch nooit. De omzetting van ASM naar microcode is gewoon een letterlijke vertaling naar hex code. Daar gebeurt geen optimalisatie meer. Wat jij in ASM progt gaat op die manier rechtstreeks de proc in.
Het kan zijn dat in de asm code naar bank 1 gewisseld wordt terwijl dat daar voor ook al is gebeurt en feitelijk niet nodig is. Dit kan dan een manier zijn door aan de compiler die micro code maakt door te geven dat het al in ram_bank 1 is zodat de code verderop over geslagen kan worden.
Als je tien keer dezelfde bit cleared achter elkaar, dan zal de MPLAP compiler (omzetting van ASM naar hex) dat ook tien keer in hex code omzetten en zal je processor tien keer die bit clearen.
Op 2 september 2009 10:56:34 schreef Brem:
Hoe communiceert je interrupt routine met het voorgrond programma? Besef je dat dat alleen via bit of byte variabelen kan, niet via integers?
Daar heb je een goed punt! Gelukkig heb ik hier geen last van. De 16 bit variabele wordt opgedeeld in drie byte variabelen. Nu kan het zijn dat de interrupt juist plaatsheeft als die opdeling gebeurt, maar dat is geen ramp. Ik had eerst die omzetting van 16 bit naar drie keer byte in de interrupt routine opgenomen, maar dan leek het erop dat de processor voor eeuwig in interrupt bleef hangen. Vermoedelijk duurt die omzetting van 16 bit naar drie keer byte meer dan 1ms (500ns instructie klok).
Als je integers gebruik moet je ook gebruik maken van z.g. critical sections, een stukje code waar je alle interrupts even uitzet, je integer een waarde geeft of uitleest, en dan de interrupts weer aanzet.
Nu heb ik geen ervaring of expliciete kennis (boekenwijsheid) over het programmeren van 16 bits operaties op een 8 bit processor, maar waarom is dit zo hard nodig? De gehele context wordt toch gesaved bij een interrupt?
Als ik het voorbeeld neem van een 16 bit variabele te delen door 2. We nemen als variabelen dan Var en VarH waarbij VarH de MSB bevat. Eerst doe je een schuifoperatie op de VarH (naar rechts schuiven). De bit die er eventueel uitvalt wordt in de carry gestoken. Treedt er nu een interrupt op, dan wordt het Status register opgeslagen. Bij het terugkeren wordt het Status register hersteld, dus zou die carry bit er nog altijd moeten inzitten. Vervolgens wordt er een schuifoperatie op Var uitgevoerd waarbij de carry links wordt ingeschoven. De bit die er rechtsuitvalt wordt genegeerd.
Nu zou ik eens moeten kijken of die Proton compiler er aan gedacht heeft om de carry te clearen alvorens aan die schuifoperatie te beginnen, maar ik vermoed van wel, anders zou dat een major bug zijn.
[Bericht gewijzigd door High-Voltage op (29%)]
Arco
Special Member
Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com
Inderdaad, in de voorbeelden van Microchip wordt enkel maar het W-register en het Status register bewaard.
Alle voorbeelden in de template directory saven ook het PCLATH register...
Het kan wel nodig zijn om het FSR te saven, maar dat hangt ervan af of je die gebruikt in de ISR.
High-Voltage
Het probleem bij een maatschappelijke samenleving is dat je geen "normale" uitzondering kan zijn...
Op 2 september 2009 13:10:21 schreef Arco:
Alle voorbeelden in de template directory saven ook het PCLATH register...
Het kan wel nodig zijn om het FSR te saven, maar dat hangt ervan af of je die gebruikt in de ISR.
Klopt! In de datasheet staat enkel maar W en Status register, daar was ik even mee verward. De Proton compiler bewaart tevens het FSR register. Dus wat betreft context saven zou het gewoon goed moeten gaan.
...We nemen als variabelen dan Var en VarH waarbij VarH de MSB bevat. Eerst doe je een schuifoperatie op de VarH (naar rechts schuiven). De bit die er eventueel uitvalt wordt in de carry gestoken. Treedt er nu een interrupt op, dan wordt het Status register opgeslagen. Bij het terugkeren wordt het Status register hersteld, dus zou die carry bit er nog altijd moeten inzitten...
Als je in het interrupt naar Var en VarH kijkt, dan zie je een hele rare waarde, want die carry bit is nog niet toegepast maar de schuif operatie op VarH wel.
Als vervolgens in het interrupt de waarde wordt veranderd, dan wordt even later in het voorgrond programma de carry bit alsnog toegepast op de nieuwe waarde, wat ook iets onvoorspelbaars oplevert.
Je kunt ook een bit variable gebruiken die aangeeft wie de 'eigenaar' is van de eigenlijke variable die wordt gebruikt voor communicatie tussen interrupt en voorgrond.
- Interrupt wijzigd de variabele alleen als het bitje aanstaat, en zet hem dan uit.
- Voorgrond leest variabele alleen als het bitje uit staat, en zet hem dan aan.
Een bitje uitzetten kan wel in een 'atomic operation', een integer lezen niet.
High-Voltage
Het probleem bij een maatschappelijke samenleving is dat je geen "normale" uitzondering kan zijn...
Op 2 september 2009 14:06:10 schreef Brem:
Als je in het interrupt naar Var en VarH kijkt, dan zie je een hele rare waarde, want die carry bit is nog niet toegepast maar de schuif operatie op VarH wel.Als vervolgens in het interrupt de waarde wordt veranderd, dan wordt even later in het voorgrond programma de carry bit alsnog toegepast op de nieuwe waarde, wat ook iets onvoorspelbaars oplevert.
Inderdaad, het klopt helemaal wat je zegt. Dit is echter niet van toepassing op mijn situatie. In de interrupt gebeurt er eigenlijk alleen maar een "read only" actie. Doch gaat de berekening de mist in.