dsPic erasing array faster - Page 1
Forum section: electronics / general

author: @rt
author_rank: Super Contributor
timestamp: April 01, 2018, 08:07:17 am
content: Hi GuysI have been clearing a large array of bytes, byte at a time, in 512 separate writes to RAM, like so:Code:unsigned char bigarray;int counter = 0;while (counter < 512) {bigarray = 0;counter++;}It has occurred to me that since the dsPic is a 16 bit processor, had I declared the same RAM array as unsigned integer types,a similar loop could have deleted 16 bits at a time, resulting in a quicker pass, only needing 256 writes.Code:unsigned int bigarray;int counter = 0;while (counter < 256) {bigarray = 0;counter++;}Is there a way to alias this array so that such an operation can be performed, while the array is still defined as unsigned char?Cheers, Brek.

author: Geoff_S
author_rank: Regular Contributor
timestamp: April 01, 2018, 08:09:39 am
content: You could consider a C union.But do you really need to ? Is this erase loop critical in your overall application ?

author: @rt
author_rank: Super Contributor
timestamp: April 01, 2018, 08:12:27 am
content: absolutely!It’s a bitwise monochrome frame buffer for graphics, and is erased every frame.If the time for erasing was halved, the frame rate increase might be perceivable.Similarly, there are times the frame buffer is copied back & forth from an image buffer,where a new way to handle clearing the array would probably also translate to an improvement there as well.

author: @rt
author_rank: Super Contributor
timestamp: April 01, 2018, 08:14:19 am
content: Wow thanksI think I’ll see a result if that works the way I think it does.

author: hans
author_rank: Super Contributor
timestamp: April 01, 2018, 08:27:48 am
content: Have you tried?Code:memset(bigarray, 0, sizeof(bigarray));I think the stdlibs should be pretty optimized (in speed) to handle these operations, as they are used literally everywhere.Another way, if you want to keep doing it manually, is unroll your loop e.g. 8 times manually:Code:#include <stdint.h> // for uint16_t, otherwise adjust type..unsigned char bigarray;int counter = 0;uint16_t* bigarray16 = (uint16_t*) bigarray;while (counter < 32) {*bigarray16++ = 0;*bigarray16++ = 0;*bigarray16++ = 0;*bigarray16++ = 0;*bigarray16++ = 0;*bigarray16++ = 0;*bigarray16++ = 0;*bigarray16++ = 0;counter++;}This way we only got the overhead of increasing counter, comparison and branching 1 out of 8 times.You could also modify the counter to be down counting, as comparison with 0 is faster than comparison with a constant, but after unrolling that is just nitpicking.If you're also dealing with slow copying times, I would definitely recommended memcpy, as some implementations use a peculiar C-style switch-case fallthrough:https://en.wikipedia.org/wiki/Duff%27s_deviceNot sure if that is implemented in XC16 stdlib, though. It is also only effective if you're dealing with odd-sized number of bytes, e.g. if you want to unroll the loop 8 times, but then need 4.25 loop iterations.

author: andersm
author_rank: Super Contributor
timestamp: April 01, 2018, 08:44:38 am
content: Quote from: blueskull on April 01, 2018, 08:11:18 amDo it the other way around. Otherwise there is no guarantee that bigarray is correctly aligned.EDIT: If the chip has a suitable peripheral, you can also try using DMA. IIRC the 16-bit PICs also have some zero-overhead loop instructions that the C compiler probably won't generate on its own (but might be used in memset).

author: @rt
author_rank: Super Contributor
timestamp: April 01, 2018, 08:51:03 am
content: Hard to tell, but will be easier to measure. It is working correctly though,and I see no reason to do something slower.The real delay is in the function that sends to the LCD hardware itself.The only function with dead delays between toggling pins.Maybe the dead delays could be replaced with code to erase the part of the frame that was just written.@andersm,Do you just mean swap the two lines will prevent the actual array being offset by a byte?

author: andersm
author_rank: Super Contributor
timestamp: April 01, 2018, 08:56:39 am
content: Quote from: @rt on April 01, 2018, 08:51:03 amNo, I mean make the array uint16_t and the alias a uint8_t*. An uint8_t array may be allocated on an odd address, which is invalid for word operations on PIC24/dsPIC.

author: @rt
author_rank: Super Contributor
timestamp: April 01, 2018, 09:03:52 am
content: Ok, I understand. I should make the alias the real name of the array then, which everything is already using.This works also. I haven’t measured anything yet speed-wise.Code:memset(framebuffer, 0, sizeof(framebuffer));Doesn’t memset copy to the array byte at a time though?

author: JPortici
author_rank: Super Contributor
timestamp: April 01, 2018, 09:14:11 am
content: Quote from: andersm on April 01, 2018, 08:44:38 amprecisely.-using the __aligned attribute one can make the buffer aligned to whatever number of bytes (2,4,8,16,32,..)-example of asm code for zero-overhead loopCode:asm("MOV #_bigarray,W0"); //Put the base address of your variable in W0asm("Repeat #255");asm("CLR [W0++]"); //Clear the content of the 16 bit ram word at address W0. Post-Increment W0 by 2

author: hans
author_rank: Super Contributor
timestamp: April 01, 2018, 09:17:41 am
content: Quote from: @rt on April 01, 2018, 09:03:52 amI've taken a look at the stdlib implementation of Microchip. If you go to the XC16 folder, it's in src/libpic30.zipI couldn't find memset, but memcpy is doing 1 byte or word copy at a time, depending on pointer alignment. It's written in assembly, so it's not going to be touched by the compiler for unrolling.That is quite a disappointing implementation, but I guess if it works that's worth something.In that case, you could enjoy a speedup by doing a manual implementation, like the many alternatives that have presented itself here

author: Twoflower
author_rank: Frequent Contributor
timestamp: April 01, 2018, 09:18:08 am
content: Have you actually looked if you see really 512 RAM accesses? Your loop is very simple so there's a chance the compiler changed that to 16 bit accesses anyway if the compiler is setup to optimize for speed. You should check the disassembled result to have a look (or just run speed tests).Before try to optimize your code and make the code harder to understand for the compiler and yourself just check if what the compiler actually did. And you should run some performance tests to see if manual fiddling around improves the situation or worsen it. This might happen if you try to optimize the code and the compiler fails to 'understand' the function of it. Also loop-unrolling is probably done if you optimize for speed but not for size (for example gcc -O3 vs. gcc -Os).Some compilers can create a log file that tells you what he did and were some issues are.And in general: If the function is run fast enough, don't waste too much time. If the function is called very seldom, don't waste too much time. The exception might be battery powered devices as unnecessary compute/memory accesses waste precious energy.

author: JPortici
author_rank: Super Contributor
timestamp: April 01, 2018, 09:22:59 am
content: aah if only the DMA engine could do memory-memory transferts...

author: @rt
author_rank: Super Contributor
timestamp: April 01, 2018, 09:25:23 am
content: I’m up for trying as many approaches as possible.Right now this:Code:unsigned char framebuffer__attribute__((aligned (2)));unsigned char imagebuffer__attribute__((aligned (2)));unsigned int *framebufferwide=(unsigned int*)framebuffer;unsigned int *imagebufferwide=(unsigned int*)imagebuffer;If I do it back to front, a fair bit more program memory is consumed.

author: C
author_rank: Super Contributor
timestamp: April 01, 2018, 01:28:39 pm
content: Change the direction of your loop.Start at Max size and go to 0 on index.A down to loop is ofter much faster.First think Binary not human and then as high a level as you can get and see what you get from compiler.

author: NorthGuy
author_rank: Super Contributor
timestamp: April 01, 2018, 02:25:24 pm
content: If you want to force the C compiler to produce some particular assembler code, it's easier to do it directly. The commands that JPortici has suggested will do the cleaning in 258 instruction cycles. Any code without "repeat" will be at least 5-7 times slower (depending on which dsPIC you use). If memset() is not using "repeat" (either as a direct compiler-emitted code or through a function where "repeat" takes a register as an argument), then no amount of C "wizardry" will help - XC!6 is not good at this. If you really need it to be fast, no reason to waste your time with C, just use assembler - it's only 3 lines - much more clear and much less work than anything you can do in C.Then again, why do you need it to be fast? May be what C does by default is fast enough for you. Then you don't need to worry at all.Or perhaps, you can alter your algorithm to avoid cleaning at all - this is often possible.

author: JanJansen
author_rank: Frequent Contributor
timestamp: April 01, 2018, 02:31:34 pm
content: Hi, take a look at my 12864B display code i posted at Microchip :http://www.microchip.com/forums/m966941.aspxI copy 32 bit zeros in the array RAM, works fastest i found.extern unsigned char screen[ 64 ][ 16 ];extern unsigned char lcdtemp;extern unsigned long*lcdptr;//------------------------------------------------------------------------------// clear graphical display backbuffer//------------------------------------------------------------------------------#define ClearScreen lcdtemp = 0; \lcdptr = ( unsigned long * )screen; \do{ *(lcdptr++) = 0; }while( lcdtemp++ != 255 );

author: @rt
author_rank: Super Contributor
timestamp: April 01, 2018, 03:44:25 pm
content: I did try the asm, and it compiled, but crashed at runtime.Since the array is really called frame buffer, and is really 1024 bytes, it looked like this:Code:asm("MOV #_framebuffer,W0");asm("Repeat #511”);asm("CLR [W0++]”);Twoflower, I can measure the speed in one way or another. I have a frame counter for example,but it’s easier for me to have as many working examples first.I’m not really complaining about speed, but the method I originally asked about, if it were possible,seemed as though it would be faster for free, so I thought why not ask.

author: SiliconWizard
author_rank: Super Contributor
timestamp: April 01, 2018, 03:54:04 pm
content: Did it crash with a properly aligned array?

author: @rt
author_rank: Super Contributor
timestamp: April 01, 2018, 04:02:57 pm
content: If the example in post 14 constitutes a properly aligned array, yes.There’s nothing unusual about the RCON register, so not really a crash,but a lot goes wrong. The screen is blank, an LED output that is supposedto be on, turns off, which is the LCD backlight, so I don’t see anything else.EDIT,,Maybe I jumped the gun there, and tried it before adding the suffix to those declarations to align them.It appears to be working now.I don’t know anything about this particular assembler, but assuming W0 is half of an accumulator,why isn’t it destroyed by interrupts?

author: SiliconWizard
author_rank: Super Contributor
timestamp: April 01, 2018, 04:32:03 pm
content: You could take a look at the generated assembly after the compilation stage (-S option of GCC) to make sure it's exactly what you wrote.One thing you could try is add the keyword 'volatile' after the asm keyword. You can see this "trick" at several places in some Microchip headers and explained in the compiler's manual.Code:asm volatile ("MOV #_framebuffer,W0");asm volatile ("Repeat #511”);asm volatile ("CLR [W0++]”);The 'volatile' keyword guarantees that the C compiler will insert the assembly instruction as is. Otherwise it may rearrange or even discard some inlined asm code if it thinks it can, usually as part of the optimization stage.

author: SiliconWizard
author_rank: Super Contributor
timestamp: April 01, 2018, 05:09:36 pm
content: Quote from: @rt on April 01, 2018, 04:02:57 pmThe dsPIC line have 16 so-called working registers, W0-W15. They can be used for a lot of purposes, including indirect addressing, such as in this example.You can take a look at this if interested: ww1.microchip.com/downloads/en/DeviceDoc/70157C.pdfIt's the responsibility of ISRs to preserve context, including registers. C compiled code will use working registers as well.

author: @rt
author_rank: Super Contributor
timestamp: April 01, 2018, 05:20:06 pm
content: Ok, I added the volatile words, and it doesn’t compile with any more or less words, and still works the same,so I’ll leave it that way.. ThanksI’d know if it was a problem because if the frame buffer wasn’t cleared, the screen would simply gradually turn black,never clearing anything drawn to it, just as occurs if I comment out the function.I was proficient with asm for 8 bit pics, such that I can still read and modify reasonable code, but do I really want to go there again?My debugging hasn’t evolved far either. I still serial debug this with a serial routine in code, a Commodre Amiga, and terminal program, and CRT monitor!To measure this loop I’d output a pulse once per call to the function that updates the display, and use an actual frequency counter to count framerate.I am aware this is funny for these times, but given my understanding of DSPs and the repeat, I trust this is going to be faster that anything else.

author: JPortici
author_rank: Super Contributor
timestamp: April 01, 2018, 05:20:11 pm
content: did you also check INTCON1?if the buffer is located at the end of ram it's possible that an address trap was generated after the last CLR instruction..After the CLR W0 would point to a location outside of the data memory.. maybe you could replace the sequence asCode:asm volatile ("MOV #framebuffer,W0");asm volatile ("REPEAT #510");asm volatile ("CLR [W0++]");asm volatile ("CLR ");or maybe what happened is that you altered the value of W0 without saving it before (push W0 -> sequence -> pop W0) or without telling the compiler to reload its value after the operation (Register clobber)

author: @rt
author_rank: Super Contributor
timestamp: April 01, 2018, 05:26:23 pm
content: JPortici,I think now, it only played up before I aligned the arrays when defining them.As I said I don’t know much about asm for this, but there’s no other assembler in the program, so it’s not me ever writing to W0 myself.The program does have two interrupt routines that appear to be working ok with the code now. One for hardware serial interrupt,and the other using timer 1 to keep real time in the absence of GPS.

author: NorthGuy
author_rank: Super Contributor
timestamp: April 01, 2018, 05:31:35 pm
content: Quote from: @rt on April 01, 2018, 04:02:57 pmAll the registers are supposed to be preserved by the ISR.There is a RCOUNT register which holds the counter for "repeat". If you're in the middle of "repeat" and an interrupt happens, then, if your ISR also uses "repeat", it'll destroy your RCOUNT. In such case, RCOUNT must be preserved by the ISR too. Whether the C-handled ISR can correctly figure out if it needs to save RCOUNT or not, I don't know. Thus it's a good idea to check, and possibly add preservation of RCOUNT to the ISR code.

author: SiliconWizard
author_rank: Super Contributor
timestamp: April 01, 2018, 05:44:21 pm
content: Quoting the manual:Quote

author: @rt
author_rank: Super Contributor
timestamp: April 01, 2018, 05:52:10 pm
content: Sounds good then thanksI vaguely remember having to do it myself.It must have been w, and I guess wherever the program counter was at, and maybe a bank or page bit. That’s about how clear it is to me now.I’m really hoping not to have to get into assembler again. Maybe unless there’s another big simple freebie like this,but it really took me some 18 months to think of this (the question I asked in the OP in the first place).

author: rstofer
author_rank: Super Contributor
timestamp: April 01, 2018, 10:45:20 pm
content: The fact that dsPic includes dedicated hardware registers for looping makes it a very powerful chip. I suppose you need to see if RCOUNT is available (or save the contents) but you simply won't find a faster way to clear memory in code.Rather than shying away from assembler, I think I would try to maximize my use and knowledge of the features. It's a very powerful chip

author: Fredderic
author_rank: Regular Contributor
timestamp: April 02, 2018, 02:20:30 am
content: Did anyone check what assembly memset() is producing? If it's not in the standard library, could it be being special-cased by the compiler to produce optimal code? (I've seen many C/C++ compilers doing things like that.)Rolling your own memset() and memcpy() functions were all the rage back in the DOS days when computers were slower than a modern 8-bitter. Had a really nice one memorised — because I'd used it so often — that rather elegantly handled unaligned byte buffers (and would then sometimes roll out aligned variants as needed by clipping off the entry and exit alignment ramps when it was guaranteed safe)… a good fast clear or value set, really can make a big difference, especially where graphics is concerned.… but that was about two decades ago now. These days, compilers really should be MUCH better, and there should at least be a memset() on offer that'll do it pretty much as fast as it can get (though once or twice it's not actually been in the stdlib, but was off in a compiler-specific library for some stupid reason).

author: @rt
author_rank: Super Contributor
timestamp: April 02, 2018, 04:25:53 am
content: I did unzip the library mentioned, and also only found memcpy().This is probably misplaced effort.Using the original code in the OP dealing with 1024 bytes (it was changed to 512 for the forum for some reason) to clear the frame buffer,overall, the program draws 353 frames every 10 seconds, and I get one more frame every 10 seconds with the assembler loop.Thinking about it, drawing text and such things is much more time consuming, but I’ll keep it.It does also happen to be a program memory optimisation too.

author: JPortici
author_rank: Super Contributor
timestamp: April 02, 2018, 06:03:58 am
content: memset is referenced in <strings.h> and not in <stdlib.h> but i can't seem to find the source. i'll write a small program and check the disassembly listing file..it wasn't in the disassembly fileso i looked at the generated assemblyCode:#include <string.h>#include <xc.h>unsigned char framebuffer__attribute__((aligned (2)));int main() {memset(framebuffer,0,sizeof(framebuffer));while(1) {Nop();}return 0;}before calling the functions the working registers are loaded with the call argumentsW0 with the address of framebuffer (0x1000)W1 with the value, in this case 0W2 with the size of framebuffer, 0x400the generated assembly for memset, using -O0. On the left the address, on the right the assemblyCode:02E4 MOV W0,W302E6 BRA 0x2EC ;This points to three lines lower, 0x2EC is the absolute address02E8 MOV.B W1, [W3++]02EA DEC W2,W202EC CP0 W202EE BRA NZ, 0x2E8 ;This points to three lines UP02F0 RETURNwith -O1 (@rt, it's included in free mode however. and it arelady makes TONS of useful optimizations. in this case the code generated with -O0 is especially slow because of the two branches. you have many cycles penalities on branches on dspic33e because you have to recreate the pipeline, which dspic33F and dspic30F don't have, as they are slower cores)Code:02E4 MOV #0x1000, W002E6 REPEAT #0x1FF02E8 CLR [W0++]this was also visible in the disassembly listing fileBONUS: memset to another valueCode:;memset(framebuffer,1,sizeof(framebuffer));02E4 MOV #0x0101, W202E6 MOV #0x1000, W002E8 REPEAT #0x1FF02EA MOV W2, [W0++]instead of clearing, the repeated instruction is a copy from one register to the memory location pointed to by W0. W2 is loaded to 0x0101 because (of course) the memory in the dsPIC is word addressable so one 16bit move takes 1 cycle, 8 bit move takes 1 cycle (so better combine them) 32 bit move however takes 2 cycles.BONUS2: and what happens if the memset target has an odd number of bytes?yep, what you expectCode:;char framebuffer;;memset(framebuffer,2,sizeof(framebuffer));02E4 MOV #0x0202, W202E6 MOV #0x1000, W002E8 REPEAT #0x1FF02EA MOV W2, [W0++]02EC MOV.B W2, [W0++]same code, with a single MOV.B added. don't have to modify W2 because with byte instructions (.B) only the lower byte of the accumulator is used

author: @rt
author_rank: Super Contributor
timestamp: April 02, 2018, 06:33:46 am
content: NiceMy main.c file is size optimised with pro version.It might be worth pulling apart sprintf since I use it a lot. Especially where format specifiers are used, but I guess it’s going to be efficient.It’s usually program memory space that I’m more concerned about.

author: JPortici
author_rank: Super Contributor
timestamp: April 02, 2018, 06:40:04 am
content: i don't use s/printf so i can't comment on that, but if it's blocking code no matter how optimizied it is, it will stall the program until the hardware has finished transfer.

author: Twoflower
author_rank: Frequent Contributor
timestamp: April 02, 2018, 08:02:35 am
content: You mentioned that your compiler is set to size optimization. Give the best speed optimization a try. Depending on the code and compiler the size difference isn't very large, but the code is much faster.

author: @rt
author_rank: Super Contributor
timestamp: April 02, 2018, 08:13:07 am
content: I started out that way, but the chip is full, and it will no longer compile for speed.There’s no pin for pin replacement. It would be nice if there were a dsPic33FJ256GP802.JPortici, Where did you get the generated assembler? I assume you put NOPs in there to help find it?Some of this is becoming worthwhile for the program memory if nothing else.Code:asm volatile ("MOV #_framebuffer,W0"); // clear framebufferasm volatile ("Repeat #511");asm volatile ("CLR [W0++]");asm volatile ("MOV #_imagebuffer,W0"); // clear image buffersasm volatile ("Repeat #1023");asm volatile ("CLR [W0++]");/*clrfb = 0;while (clrfb < 512) { // clear entire frame bufferframebuffer = 0; // clear framebufferimagebufferwide = 0; // clear first imageimagebufferwide[clrfb+512] = 0; // clear second imageclrfb++;} // clrfb*/

author: hans
author_rank: Super Contributor
timestamp: April 02, 2018, 09:02:39 am
content: You can try to pick O3 by file in MPLAB, or in GCC (which XC16 is) even by function:Code:int foo(void) __attribute__((optimize("-O3")));int foo(void) {// do stuff}However, this level of handpicked optimization is not all-reaching. Sometimes the compiler refuses to go all out on the optimization.

author: @rt
author_rank: Super Contributor
timestamp: April 02, 2018, 09:11:24 am
content: I have already used different optimisation levels by file. I have left Microchip’s SD Card (MDDFS) alone,but it’s news to me that you can use it for a given function. ThanksI suppose I can tell by the change in the memory gauge after compiling whether something like that worked.

author: JPortici
author_rank: Super Contributor
timestamp: April 02, 2018, 08:10:52 pm
content: Quote from: @rt on April 02, 2018, 08:13:07 amnope, i compiled the project then looked at the program memory view.with -O0 i there was a call to a memset label.. which wasn't present anywhere in the codeso i decoded the call address and looked from there. crazy i know but i'm familliar with dsPIC assembly, and it was a very simple programi want to remind you that the best code came from using the C libraries.. memsed was compiled to the exact same assembly i suggested, which (amazing!!!) used the dsPIC instruction set at its advantage.. so i suggest you use memset to avoid any possible issue. it was even inlined, no calls to external functions!anyway,window -> debug -> output -> disassembly listing file for the listing file.window -> pic memory views -> program memory, if the compiler doesn't show everything in the listing.at optimizations higher than -O1 the listing will be harder to follow... you have to keep track of the addresses..

author: SiliconWizard
author_rank: Super Contributor
timestamp: April 02, 2018, 08:33:32 pm
content: @JPortici: good catch!Actually on a lot of platforms, a lot of the C standard library functions (such as memcpy, memset, strcpy, strcmp, etc) get compiled as efficient inline code by GCC once you activate optimizations. Seen that on x86 platforms. But I wasn't necessarily expecting this on a dsPIC

author: JPortici
author_rank: Super Contributor
timestamp: April 02, 2018, 08:34:34 pm
content: yes, that was a very pleasant surprise

author: @rt
author_rank: Super Contributor
timestamp: April 09, 2018, 10:52:04 pm
content: Hi againCan I dodge anding a larger variable type in C to access a single bit flag in assembler for dsPic as easy as it would be done for 8 bit RISC?In the assembler I’m used to, I could test any bit in “configbits” in a single instruction, and conditionally execute the next instruction.Code:if ((configbits & 0b00010000) == 0b00000000) {bit flag was clear} else {bit flag was set}I imagine this might compile to some ideal assembler, but if not, it might save program memory.Cheers, Brek.

author: JPortici
author_rank: Super Contributor
timestamp: April 19, 2018, 06:08:02 am
content: Hi, don't know how i missed the reply.did you ever get to check the instruction set?the ideal assembly outcome would be a BTSS/BTSC instruction (Bit Test, Skip if Set/Clear) but i don't know if or how the compiler detects that you are testing a single bit.when using bitfields (check xc16 manual on how to write them) and writing on single bits, such as for example LATAbits.LATA0 it tends to use bit instructions so BSET,BCLR,BTGso i *think* that using bitfields will tell the compiler to use bit instructions

author: @rt
author_rank: Super Contributor
timestamp: April 21, 2018, 06:27:25 pm
content: HiI’m familiar with anything from 8 bit, and btfss/sc work until I try using it with a bit of any C defined unsigned char.It could just be that this has to be a 16 bit variable.The problems for me are probably more to do with using it in a C project than just using asm.I’ve been looking at examples and Microchip docs for specific things.I’m sure a jump table would work, but I haven’t found anywhere practical for it in the particular project.A delay loop can’t be more speed efficient, but based on your example, it can take little memory for a delay:Code:asm volatile ("Repeat #4999"); // number of repeats - 1asm volatile ("nop"); // dead delayIt costs the same as the C call to a C delay function, to just write small delays.I have tried a zero overhead DOSTART loop. I think this probably works, but doesn’t like C code inside it.It would have been to replace something like this:Code:int i = 0;while (i < 1024) {imagebuffer = framebuffer;i++; // increment count}so after the DOSTART, I would still increment i, and address the arrays with i.It’s only the actual while loop and braces that would be replaced.I’m not sure what happens there. It freezes, but doesn’t reset.

author: NorthGuy
author_rank: Super Contributor
timestamp: April 21, 2018, 07:46:17 pm
content: Quote from: @rt on April 21, 2018, 06:27:25 pmYou still can do it with the repeat:Code:asm volatile ("MOV #_framebuffer,W0");asm volatile ("MOV #_imagebuffer,W1");asm volatile ("REPEAT #1023");asm volatile ("MOV [W0++],[W1++]");

author: @rt
author_rank: Super Contributor
timestamp: April 22, 2018, 06:57:26 am
content: HiI wouldn’t have known to type that, but think I see how it works.If the first two instructions copy the locations, and the last line copies the contents, then increments both locations.It compiles, and saves a couple of words, but doesn’t work however.The dsPic doesn’t reset, but since this is all copying image data, I just see nothing on the display.This is a simpler example that should work, but doesn’t work to replace the C code above it.I’d like to do more, such as xor a value from one buffer with a literal, and copy the result to the other,but it’s hard to get adventurous when the simple stuff can break it.Code:int clibuf = 0; // clear the first image bufferwhile (clibuf < 1024) {imagebuffer = 0; clibuf++;}/*asm volatile ("MOV #_imagebuffer,W0");asm volatile ("Repeat #511");asm volatile ("CLR [W0++]");*/

author: NorthGuy
author_rank: Super Contributor
timestamp: April 22, 2018, 02:14:51 pm
content: Quote from: @rt on April 22, 2018, 06:57:26 amIt's hard to tell why. If I want assembler, I prefer to write it directly. When you write from C, it's always a danger that you may destroy something. Try saving/restoring W1:Code:push w1... your code herepop w1Also you can inspect the disassembly and then you will see what the complier does around your code.Quote from: @rt on April 22, 2018, 06:57:26 amRepeat can do this too.You can do it on the byte level, for example for 2048 bytes:Code:mov.b #literal,w2repeat #2047xor.b w2,[w0++],[w1++]Or, you can do it on the word level, for example for 1024 words:Code:mov #literal,w2repeat #1023xor w2,[w0++],[w1++]

author: @rt
author_rank: Super Contributor
timestamp: April 22, 2018, 05:04:18 pm
content: Hello againEverything I tried that didn’t work this morning, is now working, in fact everything I’ve tried is now successful.If any of those small routines did not work in a given location, they do if I replace W0,W1 with W1,W2. So I guess C was still doing something with W0.It seems to be likely if the assembler is going inside a C loop that is incrementing a private variable.A not yet properly tested hypothesis, but true so far:I can always use W0,W1 if the assembler is the first thing in a void C function.So I guess I can try the XOR now, thanksA silly thing I did on my own:Code:// copy the selected image buffer to the frame buffer// imageselect can be zero, or offset for image b 1024.void copyimgtoframebuf(int imageselect) {int i = 0;while (i < 1024) {framebuffer = imagebuffer[i+imageselect];i++;} // i}…// copy the selected image buffer to the frame buffervoid copyimgtoframebuf() {asm volatile ("MOV #_framebuffer,W1"); //asm volatile ("MOV #_imagebuffer,W0"); //asm volatile ("BTSC _imageselect,#10"); //asm volatile ("MOV #_imagebuffer+1024,W0"); //asm volatile ("REPEAT #511"); //asm volatile ("MOV [W0++],[W1++]"); //}It might be better to copy the 1024 value out of imageselect, and add it to the image buffer location, instead of the BTSC here.

author: NorthGuy
author_rank: Super Contributor
timestamp: April 22, 2018, 05:21:09 pm
content: Quote from: @rt on April 22, 2018, 05:04:18 pmSaving all used registers (with push) and then restoring them (with pop) should solve all such problems.If you want to add imageselect to w0:Code:mov _imageselect,w2add w0,w2,w0However, if imageselect is passed as a parameter to the function, it may be passed in w0. If you bust w0 then it is no longer valid.

author: @rt
author_rank: Super Contributor
timestamp: April 22, 2018, 05:29:02 pm
content: It does seem reliable if I use a function with no arguments or return valueCode:void invertframe() {asm volatile ("MOV #_framebuffer,W1");asm volatile ("MOV #_imagebuffer,W0");asm volatile ("MOV.B #255,W2");asm volatile ("REPEAT #1023");asm volatile ("XOR.B W2,[w0++],[w1++]");}That sort of thing has never broken yet. This function can be called from inside a C loop as well.EDIT,,,let’s not do that...Code:void invertframe() {asm volatile ("MOV #_framebuffer,W1");asm volatile ("MOV #_imagebuffer,W0");asm volatile ("MOV #0xFFFF,W2");asm volatile ("REPEAT #511");asm volatile ("XOR W2,[W0++],[W1++]");}

author: NorthGuy
author_rank: Super Contributor
timestamp: April 22, 2018, 05:47:52 pm
content: Quote from: @rt on April 22, 2018, 05:29:02 pmIf you align your buffers at word boundary then using words will be almost twice as fast:Code:asm volatile ("MOV #_framebuffer,W1");asm volatile ("MOV #_imagebuffer,W0");asm volatile ("MOV #0xffff,W2");asm volatile ("REPEAT #511");asm volatile ("XOR W2,[w0++],[w1++]");There's a special command for inverting, although it doesn't make much difference:Code:asm volatile ("MOV #_framebuffer,W1");asm volatile ("MOV #_imagebuffer,W0");asm volatile ("REPEAT #511");asm volatile ("COM [w0++],[w1++]");

author: @rt
author_rank: Super Contributor
timestamp: April 22, 2018, 06:00:14 pm
content: I should have known that, it was comf.Probably compliment file register.

author: @rt
author_rank: Super Contributor
timestamp: April 24, 2018, 04:34:47 am
content: My first DO loop worked, which is a software bit bang for SPI write. MSB goes out first.The device select pin has already been pulled low before calling this to send a byte.I remembered no way to copy a bit directly, so I don’t like that with the use of BTSS/BTSC,the data pin would be set faster than it is cleared, so a serial waveform would look odd, despite working.That is why the input byte is rotated where it is (for an extra instruction delay after the data pin possibly being sent low).I’m wondering if I should AND the portb latch pin with the data bit for even timing?So it would take the same time to set or clear the data pin before the latch pin is set?Code:void WriteSPIFast(unsigned char data_out) {SPICLOCKLAT = 0; SPIOUTLAT = data_out >> 7; SPICLOCKLAT = 1;SPICLOCKLAT = 0; SPIOUTLAT = data_out >> 6; SPICLOCKLAT = 1;SPICLOCKLAT = 0; SPIOUTLAT = data_out >> 5; SPICLOCKLAT = 1;SPICLOCKLAT = 0; SPIOUTLAT = data_out >> 4; SPICLOCKLAT = 1;SPICLOCKLAT = 0; SPIOUTLAT = data_out >> 3; SPICLOCKLAT = 1;SPICLOCKLAT = 0; SPIOUTLAT = data_out >> 2; SPICLOCKLAT = 1;SPICLOCKLAT = 0; SPIOUTLAT = data_out >> 1; SPICLOCKLAT = 1;SPICLOCKLAT = 0; SPIOUTLAT = data_out; SPICLOCKLAT = 1;SPICLOCKLAT = 0; SPIOUTLAT = 0;}void WriteSPIFaster(unsigned char data_out) {srbyte = data_out;asm volatile ("BITBANG: DO #7, ENDBANG");asm volatile ("BCLR LATB,#8");asm volatile ("BTSC.b _srbyte,#7");asm volatile ("BSET LATB,#7");asm volatile ("BTSS.b _srbyte,#7");asm volatile ("BCLR LATB,#7");asm volatile ("RLNC.b _srbyte");asm volatile ("ENDBANG: BSET LATB,#8");asm volatile ("BCLR LATB,#8");asm volatile ("BCLR LATB,#7");}

author: NorthGuy
author_rank: Super Contributor
timestamp: April 24, 2018, 03:22:43 pm
content: Quote from: @rt on April 24, 2018, 04:34:47 amYou can do even timing and a little bit faster:Code:// This assumes that LATB.7/8 are cleared at the entryasm volatile ("LSR.B _srbyte,WREG");asm volatile ("XOR.B _srbyte,WREG"); // W0 holds differences between consecutive bitsasm volatile ("BITBANG: DO #7, ENDBANG");asm volatile ("BTSC w0,#7");asm volatile ("BTG LATB,#7");asm volatile ("SL w0,w0");asm volatile ("BSET LATB,#8");asm volatile ("ENDBANG: BCLR LATB,#8");asm volatile ("BCLR LATB,#7");However, the best way is to use an SPI module.

author: @rt
author_rank: Super Contributor
timestamp: April 25, 2018, 05:13:53 am
content: ThanksSome instructions there I’m unfamiliar with. It will give me something to chew on.I am using the hardware SPI for an SD card, and a few different software routines to match variable clock speed of another device.I didn’t think I’d be as interested, but every time something comes back to me, significant program memory is saved.