dsPIC Mandelbrots in Java
Posted: February 9, 2014
While working on my first dsPIC project (an R/C car controlled by IR) I realized that I could have used a cheaper PIC24 since the dsPIC is the same instruction set plus a bunch of DSP instructions I didn't take advantage of. After looking at the extra DSP instructions they seemed to have Mandelbrots written all over them, so I decided to make a Mandelbrot generator using the dsPIC. My original plan was to write the whole thing in flat assembly language to 1) get the most speed out of it and 2) test out the DSP instructions with naken_asm.
So a few weeks ago I started a project that I've been calling Java Grinder. The idea was to have an "earlier than just in time" compiler that could read in Java class files and recompile the Java byte-code into assembly for different microcontrollers. The new assembly code could then be assembled with naken_asm. My first two chips I've implemented for this are the MSP430 and dsPIC so I decided to do the Mandelbrots in Java to test both Java Grinder and naken_asm.
Related Projects @mikekohn.net
I started out with a Java program (testing/LCD.java) that I wrote to test the SPI feature of Java Grinder. I got one of these Nokia 6100 LCD shields from SparkFun and wrote code that bounces a box around the display for the MSP430. I renamed this file testing/LCDDSP.java and made a couple changes to deal mostly with pin differences in the dsPIC and got the same program working with a dsPICF33FJ06GS101A. I was having a bit of trouble with the SPI so I wrote a flat assembly language program to talk to the LCD first: lcd_dspic.asm. This chip was a bit different than other chips I've used since the SPI pins are configurable. Any of the RPx pins can become whatever SPI pin they are configured for. I was having an issue getting the SPI pins working at first and with a bit of Googling I found others with the same problem. It ends up that JTAG is configured by default causing the pins I wanted to use for SPI not to work. To fix it I added the .org __FICD dc32 0xffcf code at the bottom of my program to turn off JTAG.
The next step was to add a mandel() function that would draw a Mandelbrot on the same display. I wrote some integer based Mandelbrot generator on my computer and basically just pasted the code into my LCDDSP.java program. I then commented out regular integer routines and replaced them with static methods from a DSP class I made that give me access to the dsPIC's DSP instructions. Java Grinder ends up replacing these method calls with some inline assembly. A nice feature I added was the DSP instructions are actually implemented in my DSP class file. So before attempting to run my compiled code on the dsPIC, I could run it first on my Linux box (after adding a couple debug System.out.println()'s) using the standard Java runtime. This saved me a ton of time debugging.
Here's a small snippit of code from LCDDSP.java along with the assembly
code (according to naken_asm each instruction
is just 1 cycle)
//tr = ((zr * zr) >> 8) - ((zi * zi) >> 8); DSP.squareToA(zr); DSP.squareToB(zi); DSP.subABAndStoreInA(); > mov [w14+10], w5 (moves local variable 5 into w5) > mpy w5*w5, A (square w5 and store in A) > mov [w14+8], w5 (moves local variable 4 into w5) > mpy w5*w5, B (square w5 and store in B) > sub A (A = A - B) //ti = 2 * ((zr * zi) >> 8); DSP.mulToB(zr,zi); DSP.shiftB(-1); > mov [w14+10], w5 (moves local variable 5 into w5) > mov [w14+8], w4 (moves local variable 4 into w4) > mpy w4*w5, B (multiply w4*w5 and store in B) > mov #0xffff, w5 > sftac B, w5 (shift left B by w5 which is -1) //zr = tr + rs; //zi = ti + is; zr = DSP.getLowerA() + rs; zi = DSP.getLowerB() + is; > sac A, #-8, w5 (shift A left by 8 and grab bits [31:16] store in w5) > mov [w14+0], w4 (moves local variable 0 (a param) to w4) > add.w w5, w4, w5 (sdd w4 to w5 and store in w5) > mov.w w5, [w14+10] (move w5 to local variable 5) > sac B, #-8, w5 (shift B left by 8 and grab bits [31:16] store in w5) > mov [w14+4], w4 (moves local variable 2 (a param) to w4) > add.w w5, w4, w5 (add w4 to w5 and store in w5) > mov.w w5, [w14+8] (move w5 to local variable 4)
Looking at this code I can see possiblity for a few optimizations, maybe just leaving things in registers longer, but for the most part it's really not that bad. I plan on adding an optimizer for Java Grinder soon for other things so maybe in the future I can get this running a bit faster.
Speaking of speed, there are a number of things I didn't do which I could have done to make this run faster. First, the routines that write to SPI do a chip select, send 9 bits, wait for them to be sent, chip unselects. I could have sped that whole process up by leaving chip select selected and wrote all the bytes without waiting on the SPI. The time it takes to calculate a pixel in the mandel code would be for sure much longer than the time it takes to clock out the SPI. The other thing I'm doing is running the chip at the default frequency which is .. I'm not sure what it is actually. It seems from the documentation that I'm using the internal fast RC (FRC) oscillator with post scaler. The FRC runs at 7.37 MHz. I always thought by default that PIC CPU's divide by 4, so that would put me at 1.8MHz but I'm not 100% sure. This chip can actually run at much higher clock rates also not sure what the top speed is.
Available as testing/LCDDSP.java on the Java Grinder page.
Copyright 1997-2014 - Michael Kohn