I’ve been working with the PIC18F4620, and am impressed by how easy it is to program, compared to the smaller PICs. I think I’m in love. Or at least in lust.
Here are some of the improvements from an embedded developer’s point of view, as contrasted to the (still very cool) 16F887.
32MHz max internal clock speed instead of 8MHz
This alone would be worth it — a PIC running at 8MIPS! It will go to 10MIPS with an external 10MHz clock, but 32MHz with no external parts needed is very cool. For us PIC16 afficionados, it’s like finding out that your new car can go 400km/hr on the freeway, legally, when the old one only did 100.
MULWF and MULLW instructions
You can tell the PIC16 programmers in the audience; they just started drooling. Hardware multiply — and not only that, but hardware multiply that executes in a single instruction cycle. I see a re-writing of some of my math libraries in the cards!
(almost) NO MORE PCLATH!
Ding, dong, the witch is (almost) dead! GOTOs and CALLs on PIC18s access the entire memory space directly. So, unless you’re doing a computed GOTO, you don’t have to worry anymore about the value of the high bits of the program counter. No more save-the-old-PCLATH-value-then-set-the-new-one-then-call-the-routine-then-return-then-restore-the-old-PCLATH business. Just GOTO or CALL your routine! And with PIC18s, you really don’t want to do computed GOTOs anyway, because now, you have…
TBLRD* and TBLWRT* instructions
PICs are Harvard architecture parts — meaning they have separate memory spaces for instructions and data. Before the PIC18s, there was no way to access the program memory at runtime — lookup tables were handled by a series of RETLW statements, whether specific or produced by the assembler from “dt” statements. The TBLRD* and TBLWRT* instructions allow PIC18s to read from and write to program memory. One use of this is to use the (relatively huge) 64kB of program space Flash memory (32 kWords, but addressable as bytes for data use). Large, verbose diagnostic messages can now be programmed relatively easily, with just a little more work than calling printf() from C.
Add W to F with Carry. This is a bigger deal than it sounds. It will speed up the ADD32 and SUB32 libraries quite a bit, I think.
With MOVFF (which takes two instruction cycles), you can copy a byte directly from one memory location to another, without going through the W register. This doesn’t always save time (although it’s always at least as fast as the old way), but not having to go through the W register can be very helpful.
Branch-if-zero. Branch-if-not-zero. Branch-if-carry. Branch-if-not-carry. Branch-if-the-moon-is-in-the-seventh-house, for all I know. Well, maybe not that last, but PICs now have more branch statements than just BTFSS and BTFSC. This doesn’t quite rank up there with MULWF or TBLRD as far as I’m concerned, but I’m told it makes the compiler guys happy.
RRNCF and RLNCF
Rotates that don’t go through carry. Huh. And RRF/RLF are now RRCF/RLCF, just to break older code, I suppose.
Test F, Skip if Zero. Sounds like it could be useful.
DCFSNZ and INFSNZ
Now you can increment or decrement and skip if *not* zero. I’m confused now; this is backwards from how these are normally used. This smells like something a compiler would like to have available.
Compare F with W, Skip if equal, greater-than, or less-than. Hmm. Equality tests sound useful.
Bit Toggle, to go with BSF and BCF. Well, I guess it was the missing option…
Decimal Adjust W. Something to do with BCD math. Sounds possibly useful.
PUSH and POP
More to the point, there’s a real, honest-to-goodness stack now! This is a good thing. Low-range and mid-range 8-bit PICs have an 8-entry hardware stack. Except in simulation, it was anybody’s guess just how deep into the stack a program already was when a particular subroutine was called. This made for very conservative use of the CALL statement — and has prompted me more than once to do things with macros that I really shouldn’t have.
Relative Call. This sounds like a segfault waiting to happen. I hope it’s not what I think it is…
Two NOP opcodes
To quote Bob from Frontier: First Encounters, “My, oh my, what happened here?”
There’s probably a legit reason for this, but it escapes me at the moment. Why not just GOTO 0×0000?
Move Literal to BSR? *looks up what BSR is, anyway*
Move Literal to FSR? *looks up what FSR is, too*
OK, now for some of the drawbacks:
Nonhomogenous instruction size
In other words, some instructions are two words long, and others are one. This means that you can’t just cavalierly do a GOTO $-5 to jump back five instructions anymore, without at least thinking about which instructions are which. If you inadvertently point to a location in the middle of an instruction, the assembler will tell you. If it happens to line up on another one, it won’t know anything is wrong until you run it and it doesn’t work as intended. Best is to come up with a consistent labeling scheme and make liberal use of good, mnemonic labels, aiming the GOTOs at them instead of using relative jumps. This will also stop you from breaking the code when you add more lines in the middle of the loop, later. (Anyone else been bit by this one?)
They’re a bit more expensive
Waah. They’re worth it.