Jump to content

ppulle

EstablishedMember
  • Content Count

    71
  • Joined

  • Last visited

Community Reputation

0 Neutral

About ppulle

  • Rank
    Regular
  1. oh well, perhaps someday. Keep up the good work on Sourceboost, it's a great compiler. As an alternative, perhaps easier...perhaps not. Would it be possible to link to libraries created with MPLAB? The idea being a blob of code such as the much desired CDC stack can be created with MPLAB then linked to a sourceboost codebase. Even a manual process of static linking would help. Surely it's just a matter of getting the right function locations, making sure sourceboost doesn't clobber MPLAB variables and getting call stack conventions right. A shim of sourceboost code could surround the calls. Phil
  2. Hi, Has there been any progress in implementing bit fields in the latest releases of Sourceboost. I'd basically like to drop in Microchip CDC USB stack without major surgery....ie have something like typedef union _USB_DEVICE_STATUS2 { byte _byte; struct { unsigned RemoteWakeup:1;// [0]Disabled [1]Enabled: See usbdrv.c,usb9.c unsigned ctrl_trf_mem:1;// [0]RAM [1]ROM }; } USB_DEVICE_STATUS2; compile and access it like USB_DEVICE_STATUS usb_stat; usb_stat.ctrl_trf_mem = 1;
  3. Looks OK to me too. Lets look at the asm assume the argument is greater than 3, lets say 4; MOVF test_Func_00000_arg_test, W will move 4 into W SUBLW 0x03 will subtract 3 from W, leaving 1 in W and no carry in the status register. No need to use SUBWF, we possibly save some code space by avoiding any bank switching if it were needed. BTFSC STATUS,C tests carry flag, and skips the next statement 'goto' if the flag is clear (Bit Test F, skip if Clear) GOTO label1 goto will be skipped as expected and execution will begin in the main part of the if statement ..... {if...} label1 {else}
  4. Hi Dave, Sorry if the below sounds confused and incoherent...but I'm a little lost at the moment. I updated to version 6.96 but with the same result. Next I looked closely at the i2c_driver.h file, and noticed that some of the variable definitions were short instead of char and the #defines pointing to the variable allocations were out of order (I think this has been noted in another post). I fixed these up and the code works properly. This is a bit puzzling, as even if I don't call the i2c functions, the code fails (if I set the i2c_driver.h back to the way it was that is). I can only think that the changes (short to int) in the i2c_driver.h file just does a rearrangement of the memory allocation to a harmless format as I noted below. Without the changes the memory map may have done something bad. In the asm file I've noticed that there are a lot of CompTempVarxx variables reusing memory allocation (as well as function arguments and return values). No doubt the linker makes sure that nothing is shared when it shouldn't be for a normal call tree. I've also go no doubt the linker makes sure call trees starting from an interrupt call don't inappropriately share temporary variables. Could there be a issue with the fact I'm calling a library function inside my interrupt which may be confusing the linker with regards to temporary variable allocation and sharing? Just to add to the question above, the library function in the interrupt call tree also uses a trick to emulate a function pointer call. This is my current suspect. The library code was written long ago before function pointers were supported by Sourceboost, however does the current version deal with any temporary variables etc called from a function pointer in an interrupt function? Here is the function pointer call emulation code: void __osEmulateFunctionPtr(pFunc pFPtr) { byte Savedintcon=intcon; intcon.GIEH=0; intcon.GIEL=0; asm { // push a dummy entry onto the hardware stack push // copy the three bytes which define the address movff _pFPtr,_tosl movff _pFPtr+1,_tosh movff _pFPtr+2,_tosu }; intcon=Savedintcon; // now enter the routine who's address is at (TOS) return; } Obviously this would confuse any compiler or linker, the question above is to clarify whether the new version of sourceboost properly makes sure the temp vars of a function pointer call are not shared, particularly when done in an interrupt function. FYI the reason for this was the library was adapted from Microchips CDC code which used function pointer arrays to call the appropriate USB service handlers if you had a USB connection with multiple protocols. Fortunately I'm only using one, so I can make a direct call and bypass the function pointer call, probably save some code space as well. I'll update this thread when I've re-written the library to use the newly support function pointer calls or have simply bypassed the use of any function pointers. Phil PS: As an aside, is there an existing option, or can an option be added to a new release, to show the variables used (including temp variables) for each function in the map file?
  5. Hi Dave, I've had a close look at the asm code, and it appears that in the 'bad' project, the linker is adding all the appropriate bank switch statements to access the reshuffled variables (ie movb instructions), as well as using the different op codes for page zero vs page something else accesses. It looks like this is being done even for the few asm{ } constructs as well. In other words it appears the linker and compiler are doing their proper function. It's probable then that maybe I've looped somewhere and done a buffer overrun or equivalent. In the 'good' project the buffer may be overrun into something harmless like a temporary variable of a function that doesn't interfere with things and in the 'bad' project the reshuffling of variables puts the overrun into something critical. Without an ICD I can't tell.....unfortunately my comms is the thing going wrong so debug statements won't help either. Looks like I'll have to manually check all my looping/array operations..yuk. Can you make a suggestion on any free tools to help? I'll give a later version of the compiler a go, and see if that helps. Only problem is I won't know what was causing the problem, which is never a good thing. I was wondering if you could answer whether there is an optimisation option in the linker that re-shuffles variable allocation to minimise bank switching. The compiles I've been doing that manually shuffle things have been showing quite big differences (hundreds of bytes, important when you're close to the limit) in the code size due to bank switching either being necessary or not necessary, or being inserted in code fragments when multiple variables across various pages are being used. Phil
  6. Hi Dave, Thanks for the headsup. It saved me a fair bit of time unnecessarily rearranging my switch statement to see if I could shorten it. I found the problem, though it's a little off topic (nothing to do with switch statements or function sizes), I thought I might explain it here as the process I went through might help others. I had some hardware which basically logged accelerometer and other data to a memory chip. It has a USB connection to download these logs and control bits and pieces. I use a library of USB functions to do the USB connection, that is a compiled library linked in, not some code in C compiled every time. I added an i2c peripheral, a magnetometer, and started logging this data. For various reasons I was using software i2c emulation. Initial tests were OK. Next I added some more functionality and I found that when I was doing a USB conversation things would stuff up. I originally thought this was in the case statement as this was where the new functions were added...hence the topic question. Luckily I keep a weekly archive of my code (hint number one for new programmers). I went back to the old stuff before the new code was added and looked at the '.asm' file. I copied the EQU statements where memory was allocated to a spreadsheet and sorted it. I did the same for the new code. Flicking between spreadsheet pages let me see what was different. I noticed that there was a data structure in the old working code that was allocated into the first page of memory (fyi the USB configuration structure). With the new code added it had been allocated out of that page. Looking a bit further it appeared that the #defines for the i2c software emulation were by default being allocated right in the middle of my data structure, so naturally the linker moved the data structure around it....fair enough Unfortunately the code that used the data structure must have been expecting this structure to be in the first page, when it was moved it behaved erratically. I simply redefined the i2c software emulation #defines out of the data structures path. Now the linker puts everything where the library code expects it to be. So some lessons: - when writing code for compiled libraries be aware that the linker may move data structures around. If your code is optimised for one page or another, there will be trouble. - I still might have some problems if the linker moves data around....but at least I know to check for it now. - Check your asm file if things go weird. Some questions: - how should code for a compiled library be written to ensure that it is impervious to data structures being moved about by the linker? I checked that there was no assembly code, so I presume the compiler and linker at library creation time put the data access optimisations in (I used linker -O1 -rb 0x0800, no optimisations on the compiler command line). - What optimisations are not recommended for compiled libraries? Is this a bug? Some suggestions: - can we get an IDE function to provide a sorted memory map on the codebar, preferably showing whats defined, how big it is and where. Even better showing what functions are using the memory allocations. Using a spreadsheet was OK.....but a little fiddly. Phil As an afterword as I was moving the #defines for the i2c driver about I noticed that one build overfilled by 400 locations, yet another build left me with 206 bytes free.....I only had to move it 8 places to get it good......but just goes to show how memory allocation can affect the compiled code size, presumably due to extra page whacking being necessary or not necessary. I wonder if there is scope for improvement in the linker/compiler combination there?
  7. Hi, I'm using Sourceboost 6.87 on a PIC18F4550 target. I've just started getting strange behavior that looks like a stack overflow though my stack depth is less than 8 for the functions being called. That is functions are stopping for no apparent reason. I was wondering if there is a limit on the number of case statements in a switch function, or if there is a limit on function sizes. This seems the likely culprit as the problems started occuring just after adding new functionality. In my case I have a command handler which is now 2188 bytes long with 43 case statements..... is this too much?? Phil
  8. Just to let you know letting Sourceboost do the days lookup worked well....clocks been running for several days without problems.
  9. Hi, Thanks for the suggestion on using the array. As a quick fix it should work. I must admit to forgetting that Sourceboost can now properly reference variables in asm code.....I've been using it for a while and remember having to use globals for that sort of thing (on a very old version), so have been a bit shy to do so. I'll get to it as soon as possible and come back with the result. I share your mystification on how the code works, one reason why if I get the chance I'll do a re-write. I suppose we can summarize some lessons for other users: - be wary of using PCL lookup tables, in or out of interrupt routines because of the page boundary problem - be careful of dropping in someone elses code, particularly if you don't fathom it entirely, even more particularly if it's been ported from another chip - if you must use PCL lookups, use the fixed function location as Dave suggests. Anyway, thanks all. Phil
  10. Hi IanM, Thanks for you reply. I agree the code is complex, I'd like to make it simpler which is usually important in an interrupt routine....that's what you get using cut and paste from different sources I suppose (I sloppy way of saying 'don't blame me....I didn't write it). Oops I'm sorry I forgot to mention it's a 18F4450. Interestingly the fault doesn't manifest is such a dramatic way as a reset or bus fault. I would have noticed that early on. FYI I have a logging system. The RTC chugs along quite nicely for days on end till the battery runs out, when logging events occur they are logged with the correct time and date. It's only when I start up the USB system again there is a problem. Presumably, as I mentioned, my usb service interrupt gets slyly disabled. Looking at my .lst the days routine; although it is outside the interrupt routine page it doesn't get put across a page boundary...but I get your point, it might, depending on the compilers whim, so PCL lookups are not good. It shall be removed. Looks like I'll have to re-write my RTC clock/calendar.....in 'C'....where possible.....because it's a C compiler! (just being frivolous above...no offense.....please don't wield the dreaded big bold font at me.....arrrrggghghghh mercy ) I suppose it's tempting fate to wonder if anyone has a safe 18F4550 clock calendar code example, I'm wryly ruing wrangling the code from a 16F84 example. Phil
  11. Hi, I have an interrupt servicing a RTC (real time clock) routine. However it uses a lookup table to determine the days of the month that does a call/PCL jump/retlw technique that doesn't seem to work. I'm using TMR1 to set flags to do ADC samples (polled in main code) as well as do the RTC. The samples are sub multiples of the RTC clock. On each second various variables (seconds, minutes, hours, days, months, year) are adjusted to the time. TMR0 is used to call a USB service routine. Here is the gist of the code, various flags and variables have been omitted: void days(void) { asm { ;_days: addwf _pcl,F ; Number of days per month retlw 0x00 ; Leap-day 29 retlw 0x1f ; January 31 retlw 0x1c ; February 28 retlw 0x1f ; Mars 31 retlw 0x1e ; April 30 retlw 0x1f ; May 31 retlw 0x1e ; June 30 retlw 0x1f ; July 31 retlw 0x1f ; August 31 retlw 0x1e ; September 30 retlw 0x1f ; October 31 retlw 0x1e ; November 30 retlw 0x1f ; December 31 } } void interrupt(void) { //Clock code from Microchip datasheet, calendar code from Jaakko Ala-Paavola asm { btfss _pir1,TMR1IF ; check if this is a timer interrupt goto usb_service ; no so do usb stuff bsf _nRTCFlags,fSAMPLE_READY; sets a flag which is polled in main code to do an ADC sample movf _nRTCTMR1H,W ; movwf _tmr1h bcf _pir1,TMR1IF ;clear interrupt flag incf _nRTCRecord,F movf _nRTCRecord,W subwf _nRTCRecordRate,W; more ADC stuff.....no relevant to forum question btfss _status,Z goto int_dortc bsf _nRTCFlags, fREPORT_READY clrf _nRTCRecord ;This is the RTC stuff int_dortc: incf _nRTCSubSecs,F movf _nRTCSubSecs,W subwf _nRTCSampleRate,W btfss _status,Z ;number of samples per second elapsed? goto int_exit ;no clrf _nRTCSubSecs ;Now we adjust the RTC clock on each second interval incf _nRTCSecs,F ;increment seconds movf _nRTCSecs,W sublw 60 ; btfss _status,Z ;60 seconds elapsed?? goto int_exit clrf _nRTCSecs ;yes, so clear seconds incf _nRTCMins,F movf _nRTCMins,W sublw 60 ;60 minutes elapsed?? btfss _status,Z goto int_exit ;no so return clrf _nRTCMins incf _nRTCHours,F ;increment hours movf _nRTCHours,W sublw 24 ;24 hours elapsed?? btfss _status,Z goto int_exit clrf _nRTCHours ; Calendar code from Jaakko Ala-Paavola http://users.tkk.fi/~jalapaav/Electronics/Pic/Clock/index.html bsf _nRTCFlags,dayf ; dayf = 1; incf _nRTCDay,F ; day++; movf _nRTCMonth,W ; ACCU = month; sublw 0x02 ; btfss _status,Z ; if ((ACCU - 2) == 0) goto _noleap ; { btfsc _nRTCFlags,lyf ; if (leap_year) andlw 0x00 ; ACCU = 0; goto _leap ; }else _noleap: movf _nRTCMonth,W ; ACCU = month; _leap: ;>>>>>>>>>>>>>> this seems to be the problem code <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< call days ; ACCU = days[ACCU]; ;>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< subwf _nRTCDay,W ; if ((day-ACCU) != 0) btfss _status,Z ; return; goto int_exit ; else { movlw 0x01 ; ACCU = 1; clrf _nRTCDay ; day = ACCU; bsf _nRTCFlags,monthf; monthf = 1; incf _nRTCMonth,F ; month++; movf _nRTCMonth,W ; ACCU = month; sublw 0x0d ; if ((ACCU-13) != 0) btfss _status,Z ; return; goto int_exit ; else { movlw 0x01 ; ACCU = 1; movwf _nRTCMonth ; month = ACCU; bsf _nRTCFlags,yearf; yearf = 1; incf _nRTCYear,F ; year++; bcf _nRTCFlags,lyf ; lyf = 0; movf _nRTCYear,W ; ACCU = year; andlw 0x03 ; if ((ACCU & 00000011) == 0) btfsc _status,Z ; { bsf _nRTCFlags,lyf ; lyf = 1;} movf _nRTCYear,W ; ACCU = year; sublw 0x64 ; if ((ACCU-100) != 0) btfss _status,Z ; return; goto int_exit ; else { clrf _nRTCYear ; year = 0; incf _nRTCCentury,F ; century++; bcf _nRTCFlags,lyf ; lyf = 0; movf _nRTCCentury,W ; ACCU = century; andlw 0x03 ; if ((ACCU & 00000011) == 0) btfsc _status,Z ; { bsf _nRTCFlags,lyf ; lyf = 1; goto int_exit usb_service: btfsc _intcon,TMR0IF call cdc_service bcf _intcon,TMR0IF ;clear sample interrupt flag goto fin_int int_exit: ;had some stuff here before fin_int: } } The code basically checks if a TMR1 interrupt has occured, if it has it does some flag setting and RTC updates. If not, then it checks if TMR0 interrupt has occured and does a usb service call. The problem is that when the clock clicks over from 23:59:59 to the next day, some undefined chaos occurs and my USB stops working, probably because it misses a service call or doesn't get reset (so never TMR0 interrupt never occurs again). If I comment out all the lines in the 'days' routine, and replace it with a fixed line like RETLW 0x1f, all is well. It appears it is the table lookup method using the line: addwf _pcl,F ; Number of days per month that is the problem. Is fiddling with the pcl during an interrupt routine a bad thing? Could it be bollocksing up something that interferes with everything else? Is there an alternative to using a PCL/RETLW sequence to do a table lookup? I've looked at using the TBLRD opcode, but have no experience with using table reads and am looking for some advice on setting them up. I don't think it's a timing issue, because if I replace all the RETLW and addwf PCL,F statements with loads of nops everything works. eg: asm { nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop retlw 0x1f } presumably this would actually take longer to execute than the original code. So the only thing I can think of is adding something to the pcl during the interrupt routine (servicing TMR1..the RTC) then TMR0 goes off. So I suppose there are a couple of questions: - what can I replace my ADDWF PCL,F/RETLW sequence with that is safe during an interrupt, can anyone provide a short code fragment please. - what is causing the problem in the first place. Obviously making calls during an interrupt is OK (though not advised)...it's just the PCL fiddling. Thanks Phil
  12. Hi All, I'll leave it to others to check out the method russ has come up with (I've got my solution and moved on to other stuff)...it does look interesting however. I don't think rounding (in terms of just the multiplication) will be an issue if the numbers are limited to the precision of the accumulator (ie for 16bit signed limit the additional samples to 4096 ie 16-sign bit-divisor bits)....however the division gives me a bit of a worry, as there will nearly always be a remainder...that's probably the main area where loss of accuracy will occur. Note that we're doing an average and effectively throwing away bits to smooth things out anyway...so maybe its a case of try it and see. My main concern is that a divide by seven operation (or divide by one less than the multiplication for other selections) will by it's nature never be a clean shifting operation, hence you'll need a progressive subtraction routine (ie division) added to the code...increasing the code length and processing time. Alternatively if you keep the division a multiple of 2 (eg 8) you'll need a progressive addition (ie multiplication) to do the +1....either way it's not an even multiply or divide. If the 8x8bit multiply opcode on the 18F4450 is used, maybe you can multiply by 9 using the opcode and divide by 8 using shifts and not call a separate routine....in which case you'd have a 9 tap filter....of course that only works with 8 bit numbers. Phil
  13. Thanks for the heads up on how compilers deal with signed ints.......it's sure to be a trap for the unwary. I was hoping there'd be a generic bit fiddling algorithm that'd handle that sort of thing. Anyway I've taken everyones advice, subtracting the old history value before adding the new one to quickly calculate the totals as well as converting negative ints to positive (and back again) before doing the division. Thanks to all who've helped. Below is my contribution to the sourceboost world.....a 12 bit precision, signed, eight channel, eight tap finite impulse response filter....aka moving average on eight inputs. Any comments on stripping more bytes from the code would be appreciated, I'm sure there are more efficient ways of doing some things like converting negative to positive numbers, dividing and adding 16bit ints etc. In any case it compiles to 164 bytes, which is fine for my application, and a lot less than I thought it would be. Phil #define SENSOR_CHANNELS 8 #define TAP_COUNT 8 #define NEXT_ROW_OFF 16 #define TAP_MASK 0eh int aSamples[SENSOR_CHANNELS * TAP_COUNT ]@100; int aSensors[SENSOR_CHANNELS]@0x20; int aTotals[SENSOR_CHANNELS]@0x40; unsigned char nSamplePos; //FUNCTION to implement an eight channel eight tap finite impulse response filter (aka Moving average filter) //eight separate sensor inputs can be averaged //INPUTS: // aSensors := array of eight int sensor inputs (will be modified after the function) //OUTPUTS: // aSensors := averaged sensor inputs based on previous inputs //USES: // aSamples := array of histories of each sensor // aTotals := array of total values of the histories, used in calculating the averages // nSamplePos := byte position in the history array, nb we keep it at 0,2,4..14 not element (eg 0,1,2,3) //RESTRICTIONS: // TAP_COUNT must be a multiple of 2 // ints are used, so precision is 16bits - sign bit - divisor bit count, eg 16-1-3 = 12 bits for 8 tap // so this code can only be used for values +-4096 max // need to manually modify division section to do division if 8 tap is not used // TAP_MASK should be defined as SENSOR_CHANNELS*2-1 void av_samples(void) { unsigned char nChannel; asm { //Setup the indirect registers movlw LOW(_aSamples) ;fsr0 := aSamples + nSamplePos movwf _fsr0l movlw HIGH(_aSamples) movwf _fsr0h movf _nSamplePos,W addwf _fsr0l,F ;add the nSamplePos to get to old history sample btfsc _status,C incf _fsr0h,F movlw LOW(_aTotals) ;fsr1 := aTotals movwf _fsr1l movlw HIGH(_aTotals) movwf _fsr1h movlw LOW(_aSensors) ;fsr2 := aSensors movwf _fsr2l movlw HIGH(_aSensors) movwf _fsr2h movlw SENSOR_CHANNELS ;we are doing eight channels movwf _nChannel AV_SAMP_LOOP: movf _postinc0,W ;get old sample value, low byte subwf _postinc1,F ;and subtract from total, low byte movf _postdec0,W ;ie Total := Total - OldSample subwfb _postdec1,F movf _postinc2,W ;Insert new sample into history array movwf _postinc0 movf _postdec2,W movwf _postdec0 ;aSamples[nSamplePos/2,nChannel] := aSensor[nChannel] movf _postinc2,W addwf _postinc1,F ;add new sensor value to totals movf _postdec2,W ;nTotal := Total + NewSample addwfc _postdec1,F movf _postinc1,W movwf _postinc2 movf _postdec1,W ;gee it'd be nice if microchip implemented a predecX operand movwf _indf2 ;move total to samples array btfss _postdec2,7 ;check if value is negative bra AV_SAMPLES_SHIFT comf _postinc2,F comf _postdec2,F ;invert value movlw 1 addwf _postinc2,F clrf _wreg,F addwfc _postdec2,F AV_SAMPLES_SHIFT: bcf _status,C ;now everything is positive so we can divide by eight rrcf _preinc2,F movf _postdec2,W rrcf _indf2,F ;divide by 2 bcf _status,C ;divide by 2 rrcf _preinc2,F movf _postdec2,W rrcf _indf2,F bcf _status,C rrcf _preinc2,F movf _postdec2,W rrcf _indf2,F ;divide by 2 for a total division of 8 movf _postinc1,W btfss _postdec1,7 ;check if total was negative again bra AV_SAMPLES_NEXT comf _postinc2,F ;invert back to negative if the original total was negative comf _postdec2,F movlw 1 addwf _postinc2,F clrf _wreg,F addwfc _postdec2,F AV_SAMPLES_NEXT: movlw 2 addwf _fsr1l,F ;move the aTotals pointer along to next int movlw 0 addwfc _fsr1h,F movlw 2 addwf _fsr2l,F ;move the aSensors pointer along to next int movlw 0 addwfc _fsr2h,F movlw NEXT_ROW_OFF addwf _fsr0l,F movlw 0 addwfc _fsr0h,F ;move to the next row of samples for next channel decfsz _nChannel,F bra AV_SAMP_LOOP ;do next channel movlw TAP_MASK incf _nSamplePos,F incf _nSamplePos,F andwf _nSamplePos,F ;increment and mask nSamplePos to do circular buffer } } void main() { unsigned char i; int k; //value we're putting in the sensors array. Note when it goes above/below +-4096 the code explodes int n; //a counter to see how many passes we've done k = 0; n = 0; while (1) { for(i=0;i<SENSOR_CHANNELS;i++) aSensors[i] = k; k = k - 1; n = n + 1; av_samples(); } }
  14. Hi, I've got a nice little asm code adding sensors, performing array totals etc and doing everything up to the dividing by 8 to take the average. Rather than figure it out for myself, I thought I'd just re-use the code that sourceboost generates to do a divide by 8 on a signed int. However the results were not as expected. eg, compile and step through this in the debugger int k; int z; k = 0; z = 0; while(1) { k = k - 1; z = k / 8; } for different values of k, I get the following results for z k=-1,z=-1 k=-2,z=-1 k=-3,z=-1 k=-4,z=-1 k=-5,z=-1 k=-6,z=-1 k=-7,z=-1 k=-8,z=-1 k=-9,z=-2 etc k=-16,z=-2 k=-17,z=-3 etc etc Am I missing something here.....it looks like the optimisation to do a divisor that is a multiple of 2 isn't handling signed ints very well. I would assume for k=-1...-7 -> k/8=0 , k=-8...-15 -> k/8=-1, k=-16...-24 ->k/8=-2 etc Does anyone know asm code sequence that will do a signed int division by eight properly? It works for k>0...I don't want to add extra code to check the sign and artificially add one to correct the division. Phil
×
×
  • Create New...