Programming a PIC processor is much easier if you have an understanding of the internal architecture, especially as it varies from "normal" computer systems that you might already seen.

Von-Neumann Architecture

This, if you like, is the "normal" way to build a computer system. The address and data buses are common for all memory access. Program-code and data exist in the same memory range, along with any memory-mapped peripherals.


  • Simplifies hardware (both circuit-design and layout) - routing multiple data-buses can be awkward on compact layouts.
  • Easier to generate re-locatable code, which makes multi-tasking easier to implement. Perhaps not an issue here, but consider a complicated product such as a digital set-top-box...


  • Instructions must be multiples of the data bus-width - can be inefficient.
  • Variable number of cycles required for instructions. For example, an instruction that requires data from memory must wait at least another cycle before it can complete, whereas some instructions execute much faster. This can be a problem for time-critical applications.

Harvard architecture

This architecture overcomes the memory bottle-neck by splitting the memory into instruction (code) and data areas. These are accessed via separate data and address busses...


  • Data and address busses can be different widths. This means the program memory word can be wide enough to incorporate an instruction and a literal (fixed data) in a single instruction.
  • A built-in two-stage pipeline overlaps fetch and execution of instructions, meaning most instructions execute in a single clock cycle.


  • Slightly more confusing at first.
  • Hardware is more complicated. Luckily, Microchip have taken care of that!

To summarise, program memory is different to data memory - in the PIC16FXX series, the program memory is 14 bits wide, whereas the data memory is a more "conventional" 8 bits wide. Remember - PICs are essentially an 8-bit processors. We'll see much more about this later.

Registers and Memory

Like any computer system, the PIC processor has a memory-map - as you can see, there are 256 different locations shown here - this suggests that 8 bits are required to address all the locations. The memory map has a 2-dimensional aspect - 7 bits will give you 0 to 127, moving you up and down the map, and the eighth bit will move you across the map, giving the total of 256 possible address locations. This is called paging or banking. As you'll see later, this 8th bit is extremely important - it's called RP0 and is contained in the STATUS register. A question to ponder - you'll note that the STATUS register is available on both "sides" of the map. Why is this essential?

  Bank 0 Bank 1  
00h (0) Indirect addr Indirect addr 80h (128)
01h (1) TMR0 OPTION 81h (129)
02h (2) PCL PCL 82h (130)
03h (3) STATUS STATUS 83h (131)
04h (4) FSR FSR 84h (132)
05h (5) PORTA TRISA 85h (133)
06h (6) PORTB TRISB 86h (134)
07h (7)     87h (135)
08h (8) EEDATA EECON1 88h (136)
09h (9) EEADR EECON2 89h (137)
0Ah (10) PCLATH PCLATH 8Ah (138)
0Bh (11) INTCON INTCON 8Bh (139)
0Ch (12) 68 GPR'S
Mapped to Bank 0 8Ch (140)
7Fh (127)     FFh (255)
  - Not implemented  

The important point is that Data Memory contains a mixture of RAM (GPRs in Microchip-speak), special registers and peripherals.

Not shown is the Working Register, or "W". If you have studied other microprocessors, you may be familiar with the term "Accumulator". The Working Register is equivalent.

Using Peripherals

Before you can use a peripheral, you must configure it. Predictably, this varies in complexity, but can be very straightforward. For example:

Port B - 8 bit wide bi-directional port

Individual bits of PORTB (address 05h) translate directly to physical pins on the IC. But, before the port can be used, you must program TRISB (the data-direction register). This tells the microcontroller whether the individual bits of PORTB are inputs or outputs.


Bits 0 to 3 are connected to LEDs so we want them to be outputs. Bits 4 to 7 are connected to switches, so these need to be inputs. Setting a bit in TRISB makes the corresponding bit of PORTB an input. Therefore, TRISB needs to be programmed with 11110000

Having done this, we can now read and write to PORTB. Writing 15 ("00001111" in binary) to PORTB will light all the LEDs. Clearing PORTB (by writing "00000000") will extinguish them. Reading PORTB and masking the lower bits will reveal the state of the switches.


These are a powerful way to ensure that a peripheral is able to demand the attention of the CPU immediately! On receiving the Interrupt Request (IRQ), the following happens:

  1. The current Program-Counter is stored on the stack.
  2. Program flow is diverted to the Interrupt Vector.
  3. The users program (optionally) stores certain key variables, such as W, STATUS, etc.
  4. The source of the interrupt is determined (again, optionally) and the appropriate code can be executed.
  5. Key variables are restored, prior to...
  6. Ending the ISR and resuming normal program flow.

Note: Red steps are automatically performed by the microcontroller.

Some applications are entirely interrupt-driven, and do nothing until receiving an interrupt. In this case, there's no need to perform the optional steps.

The PIC16F84 has 4 interrupt sources:

  1. External interrupt RB0/INT (bit 0 of PORTB)
  2. PORTB change interrupt (bits 4-7 of PORTB)
  3. TMR0 overflow
  4. Data EEPROM write complete interrupt

Typical examples include:

  • Infra-red receiver input.
  • Keypad inputs (in conjunction with the internal PORTB weak pull-ups).
  • Maintaining a real-time clock.
  • To allow the PIC to continue doing other things while the processor writes to the the EEPROM memory.

It is important to learn about interrupts, as they are essential to almost any real application. Fortunately, they are relatively easy to use on the PIC processors.


This simple overview of the PIC architecture will hopefully help your understanding during the next few pages, where we start to write our first program.