; ------------------------------------------------------ ; progger.asm ; ; ICS programmer for Microchip devices ; ; (c) 2001 L. Hollevoet ; ; ------------------------------------------------------ ; Revision history: ; ------------------------------------------------------ ; $Log: progger.asm,v $ ; Revision 1.5 2002/02/25 12:07:49 hollevo ; Adapted code to new PCB ; ; Revision 1.4 2002/01/10 16:59:01 hollevo ; Fixed _remove_cp and _bulk_erase routines ; ; Revision 1.3 2002/01/10 13:46:16 hollevo ; Changed lots of stuff. ; - Serial routines are speed optimized. Pic can now read a line of ; data @ 9600bps. ; - Added CRC check, progger goes in error if CRC is not correct ; ; TBD: erase, remove code protection ; ; Revision 1.2 2002/01/02 13:10:55 hollevo ; First working version ; ; Revision 1.1.1.1 2001/11/29 15:36:28 hollevo ; Initial import ; ; ------------------------------------------------------ #include "P16F84.INC" LIST p=PIC16F84 __CONFIG _CP_OFF & _PWRTE_ON & _WDT_OFF & _XT_OSC __IDLOCS 0x5210 #include "progger.h" ; -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- ; Reset/interrupt vectors ; -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- org 0x00 ; reset vector bank0 ; set up I/O ports A & B clrf PORTA clrf PORTB goto _init org 0x04 ; interrupt vector goto _isr ; ---------------- _init ---------------------------------------- ; ; Initialisation of the I/O ports and several variables that are ; used throughout the program. ; ; --------------------------------------------------------------- _init bank1 clrf TRISA ; A = unused (set all as output) clrf TRISB bsf TRISB, 0 ; RB0 = input bsf OPTION_REG, RP0 ; enable pull-ups bcf OPTION_REG, INTEDG ; interrrupt on falling edge of RB0 (startbit) bank0 _initb bsf PORTB, TxD ; pull TxD line high call _serDelay clrf RxChar ; Clear RxChar movlw 0x90 ; enable interrupt (INT/RB0) movwf INTCON _connected call _intro _main movf RxChar, F ; check if null btfsc STATUS, Z goto _main ; nothing received movlw 'R' ; read contents from pic xorwf RxChar, W btfsc STATUS, Z call _read_dev movlw 'I' ; check device type xorwf RxChar, W btfsc STATUS, Z call _get_id movlw '1' xorwf RxChar, W btfsc STATUS, Z bsf PORTA, MCLR movlw '0' xorwf RxChar, W btfsc STATUS, Z bcf PORTA, MCLR movlw 'X' ; bulk erase device xorwf RxChar, W btfsc STATUS, Z call _bulk_erase movlw 'P' ; program mode (dm, pm, eeprom) xorwf RxChar, W btfsc STATUS, Z call _prog_dev movlw 'T' xorwf RxChar, W btfsc STATUS, Z call _test_mode movlw 'H' xorwf RxChar, W btfsc STATUS, Z call _remove_cp clrf RxChar ; clear the RxChar call _send_lf movlw 'O' ; and tell host we're ready for next call _send ; command movlw 'K' call _send call _send_lf goto _main ; receive next command ;;;;;;; TEST_ROUTINE ;;;;; ; Check the serial receive routine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; _test_mode bcf flags, 2 _t_m_loop call _read_hex_line movlw 0x31 call _send btfsc flags, 2 goto _t_m_loop return ;;;;;;; Debug ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Dump dm to serial port ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; _debug call _send_lf movlw 0x10 movwf dptr movlw 0x10 movwf dsize call _debug_lp call _send_lf movlw 0x20 movwf dptr movlw 0x10 movwf dsize call _debug_lp call _send_lf movlw 0x30 movwf dptr movlw 0x10 movwf dsize call _debug_lp call _send_lf movlw 0x40 movwf dptr movlw 0x10 movwf dsize call _debug_lp call _send_lf clrf RxChar ; Catch the '$' in RxChar return _debug_lp movf dptr, W movwf FSR movf INDF, W call _dump_hex incf dptr, F decfsz dsize, F goto _debug_lp return ; ----- _inc_pc ---------------------------------------------------- ; increment the local copy of the program counter ; ------------------------------------------------------------------ _inc_lpc movf pLpc, W xorlw 0xFF btfsc STATUS, Z incf pHpc, F incf pLpc, F return ; ----- _rst_pc ---------------------------------------------------- ; reset the local copy of the program counter ; ------------------------------------------------------------------ _clr_lpc clrf pHpc clrf pLpc return ; ----- _sync_pc --------------------------------------------------- ; increment the program counter so it matches the location ; where the next command has to be programmed ; ------------------------------------------------------------------ _sync_pc ; The idea is to compare the current copy of the pc with the ; program target (in laddr & haddr) ; If it is not equal, it will have to be incremented until ; we have a match (do you have a match ;-) movf haddr, W ; check high byte xorwf pHpc, W btfss STATUS, Z goto __s_pc_incr movf laddr, W ; check low byte xorwf pLpc, W btfss STATUS, Z goto __s_pc_incr return ; if we get here, we are synced __s_pc_incr movlw inc_addr ; increment the physical pc call _send_cmd call _inc_lpc ; increment the local copy goto _sync_pc ; ----- _conv_MPASM_addr ------------------------------------------- ; Convert the MPASM hex address to real physical location ; (divide haddr & laddr by 2) ; ------------------------------------------------------------------ _conv_MPASM_addr bcf STATUS, C ; Clear carry bit rrf haddr, f ; divide haddr rrf laddr, f ; and shift carry bit into laddr return ; ----- _prog_dev -------------------------------------------------- ; Program data into the DUP ; ------------------------------------------------------------------ _prog_dev clrf RxChar clrf pCRC ;call _remove_cp ; recommended in data sheet ;call _prog_menu _p_d_lp movf RxChar, F btfsc STATUS, Z goto _p_d_lp movlw 'C' ; we want to program config memory xorwf RxChar, W btfsc STATUS, Z goto _prog_conf movlw 'P' xorwf RxChar, W bsf flags, 1 call _init_part call _clr_lpc ; clear local pc copy _p_d_rd call _read_hex_line ; get next hex line from host btfss flags, 2 goto _pgm_done ; Now do the real programming call _sync_pc ; sync the pc with target movlw 0x31 ; reset read pointer movwf ptr __p_d_ll ; program device line loop: takes next word from RAM and prog it into ; the DUP movlw ld_pm call _send_cmd movf ptr, W ; Get next low byte movwf FSR movf INDF, W movwf lDreg incf ptr, F ; Increment read pointers movf ptr, W movwf FSR movf INDF, W ; And get the next high byte movwf hDreg call _send_dta ; send it to DUP movlw e_pgm ; and program it call _send_cmd call _delay_10m ; wait for completion incf ptr, F ; increment read pointer call _inc_lpc ; increment local pc copy movlw inc_addr ; and also the real one! call _send_cmd decf pSize, F ; check if there is more where that came from decfsz pSize, F goto __p_d_ll movlw 0x31 ; Send ACK to host call _send goto _p_d_rd ; Read the next line from the serial port _pgm_done call _reset_part return ; ----- _read_hex_line --------------------------------------------- ; Read a line from the hex file ; ------------------------------------------------------------------ _read_hex_line ; Read the data from the serial port: ; 1 byte: number of bytes to be read (size) ; 2 bytes: the high and low byte ; of the target adress (haddr, laddr) ; x bytes: the real data bytes ; =+=+=+= WARNING: do not add/remove code: speed optimized =+=+=+= movlw size call _get_hex movf size, W movwf pSize movlw haddr ; address of haddr call _get_hex movlw 0x02 ; compensate for field and CRC addwf size, F movlw laddr ; address of laddr call _get_hex movlw recv_ptr ; init ptr movwf ptr _r_s_lp call _get_hex ; read 2 chars incf ptr, F ; increment pointer movf ptr, W decfsz size, F ; check if there is more goto _r_s_lp ; =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= movf recv_ptr, F ; check if we have an EOF record (01) btfsc STATUS, Z ; if 00 : normal data record goto _not_done decf recv_ptr, F ; if 01 : end of file btfss STATUS, Z goto _error ; else : PEEEEUUUT ; If we're here we received an EOF record, the next byte ; should be FF (CRC) -> test it incfsz recv_ptr+1, F goto _error bcf flags, 2 ; clear status flag return _not_done ; Check the CRC of the received data clrf pCRC movf pSize, W ; check size, but refresh it first movwf size ; 'cause it was decremented in ; the receive loop addwf pCRC, F movf haddr, W ; check haddr addwf pCRC, F movf laddr, W ; check laddr addwf pCRC, F movlw recv_ptr+1 ; now CRC the rest of the data movwf ptr ; skip *recv_ptr, since it was 00 incf size, F ; increment size, we want to read CRC too _n_d_loop movf ptr, W ; get next byte from memory movwf FSR movf INDF, W addwf pCRC, F ; and add it to pCRC incf ptr, F ; increment pointer decfsz size, F ; check if loop done goto _n_d_loop movf pCRC, F ; CRC should be zero by now btfss STATUS, Z goto _error ; if not: EJECT -- EJECT bsf flags, 2 ; else continue call _conv_MPASM_addr ; convert address to physical location return ; ----- _prog_conf ------------------------------------------------- ; Program the devices configuration word ; ------------------------------------------------------------------ _prog_conf clrf RxChar ; Read the 5 words (4 IDlocs, 1 sysconfig) from host call _read_hex_line ; IDlocs & config register are now in 0x31-0x3a ; Init DUP call _init_part ; Increment PC to config memory movlw ld_cfg call _send_cmd ; Load dummy data call _send_dta call _delay_10m ; Program the IDlocs ; ID0 movf 0x31, W movwf lDreg movf 0x32, W movwf hDreg movlw ld_pm call _send_cmd call _send_dta ; send it to DUP movlw e_pgm ; and program it call _send_cmd call _delay_10m ; wait for completion movlw inc_addr ; increment address counter call _send_cmd ; ID1 movf 0x33, W movwf lDreg movf 0x34, W movwf hDreg movlw ld_pm call _send_cmd call _send_dta ; send it to DUP movlw e_pgm ; and program it call _send_cmd call _delay_10m ; wait for completion movlw inc_addr ; increment address counter call _send_cmd ; ID2 movf 0x35, W movwf lDreg movf 0x36, W movwf hDreg movlw ld_pm call _send_cmd call _send_dta ; send it to DUP movlw e_pgm ; and program it call _send_cmd call _delay_10m ; wait for completion movlw inc_addr ; increment address counter call _send_cmd ; ID3 movf 0x37, W movwf lDreg movf 0x38, W movwf hDreg movlw ld_pm call _send_cmd call _send_dta ; send it to DUP movlw e_pgm ; and program it call _send_cmd call _delay_10m ; wait for completion movlw inc_addr ; increment address counter call _send_cmd ; Config movlw inc_addr ; increment address counter call _send_cmd movlw inc_addr ; increment address counter call _send_cmd movlw inc_addr ; increment address counter call _send_cmd movf 0x39, W movwf lDreg movf 0x3a, W movwf hDreg movlw ld_pm call _send_cmd call _send_dta ; send it to DUP movlw e_pgm ; and program it call _send_cmd call _delay_10m ; wait for completion call _reset_part movlw 0x31 call _send return ; ----- _bulk_erase ------------------------------------------------ ; Bulk erase device ; ------------------------------------------------------------------ _bulk_erase call _init_part movlw ld_pm call _send_cmd movlw 0xFF movwf lDreg movwf hDreg call _send_dta movlw erase_pm call _send_cmd movlw pgm call _send_cmd call _delay_10m call _reset_part return ; ----- _remove_cp ------------------------------------------------- ; Disables the code protection of the DUP (will erase both PM and DM) ; ------------------------------------------------------------------ _remove_cp call _init_part movlw ld_cfg call _send_cmd movlw 0xFF movwf lDreg movwf hDreg call _send_dta movlw inc_addr ; increment address counter call _send_cmd movlw inc_addr ; increment address counter call _send_cmd movlw inc_addr ; increment address counter call _send_cmd movlw inc_addr ; increment address counter call _send_cmd movlw inc_addr ; increment address counter call _send_cmd movlw inc_addr ; increment address counter call _send_cmd movlw inc_addr ; increment address counter call _send_cmd movlw 0x01 call _send_cmd movlw 0x07 call _send_cmd movlw e_pgm call _send_cmd call _delay_10m call _delay_10m movlw 0x01 call _send_cmd movlw 0x07 call _send_cmd call _reset_part return ; ----- _read_dev -------------------------------------------------- ; Reads the contents of the pm and dumps it to serial port ; ------------------------------------------------------------------ _read_dev ; Read the target address from the serial port movlw haddr ; Address of haddr call _get_hex movlw laddr ; Address of laddr call _get_hex _read_dev_int clrf haddrc clrf laddrc ; DEBUG call _send_lf ; DEBUG movlw 0x01 ; Increment address by one addwf laddr, F ; for the loop, else the btfsc STATUS, C ; last address will not incf haddr, F ; be read (see loop code) call _init_part _r_d_lp ; Read data back from device movlw rd_pm call _send_cmd call _read_dta movf hDreg, W call _dump_hex movf lDreg, W call _dump_hex movlw inc_addr call _send_cmd ; Increment address counter movlw 0x01 addwf laddrc, F btfsc STATUS, C incf haddrc, F ; Check if finished movf haddr, W xorwf haddrc, W btfss STATUS, Z goto _r_d_lp movf laddr, W xorwf laddrc, W btfss STATUS, Z goto _r_d_lp bsf flags, 2 call _reset_part return ; ----- _get_id ---------------------------------------------------- ; Get the device id from configuration memory ; ------------------------------------------------------------------ _get_id call _init_part movlw 0x00 movwf hDreg movlw 0x00 movwf lDreg ; Increment PC to config memory movlw ld_cfg call _send_cmd ; Load dummy data call _send_dta call _delay_10m movlw 0x08 movwf iCounter _get_id_lp ; Read data back from device movlw rd_pm call _send_cmd call _read_dta movf hDreg, W call _dump_hex movf lDreg, W call _dump_hex movlw inc_addr call _send_cmd decfsz iCounter, F goto _get_id_lp call _reset_part return ; ----- _isr ------------------------------------------------------- ; ; FUNCTION: Takes care of interrupt calls. ; ; In case of a RB0/INT change interrupt -> reads the next bit ; ; USES: _shuffle ; ; FALLS INTO: nothing ; ; ------------------------------------------------------------------- _isr ; Interrupt service routine _push movwf W_Temp ; save W swapf STATUS, W ; save status movwf Status_Temp ; without changing flags ; we arrived here due to the RB0 interrupt ; since it's the only not-masked irq in INTCON ; so we don't check it... ; Interrupt on falling edge of RB0 means we have a start bit ; start receiving btfss PORTB, 4 goto _dbg_s1 bcf PORTB, 4 goto _dbg_end _dbg_s1 bsf PORTB, 4 _dbg_end call _recv ;movf RxChar, W ;call _send ; Trap debug command movlw '$' xorwf RxChar, W btfsc STATUS, Z call _debug ; interrupt has been serviced, clear interrupt flag bcf INTCON, INTF _pop swapf Status_Temp, W ; get original status back movwf STATUS ; into status register swapf Status_Temp, F ; old no flags trick again swapf Status_Temp, W ; to restore W retfie ; finished ; ----- _init_part -------------------------------------------- ; Init the pic by taking it out of reset and bringing the RB3 hi ; ------------------------------------------------------------------ _init_part clrf PORTA ; Make sure the device is in reset call _delay_10m bsf PORTA, MCLR ; Device out of reset state bsf PORTA, PGM ; Enter PGM mode return ; ----- _reset_part -------------------------------------------- ; Place and keep the pic in reset mode ; ------------------------------------------------------------------ _reset_part clrf PORTA return ; ----- _send_cmd -------------------------------------------------- ; Send command to DUP ; Requires 6 bits to be sent to the device (6 LSBs from W) ; ------------------------------------------------------------------ _send_cmd movwf lCreg ; save command in lDreg movlw 0x06 ; init pCounter movwf pCounter _s_c_loop bcf STATUS, C ; clear status reg rrf lCreg, F ; rotate next LSB into status btfss STATUS, C goto _s_c_send0 bsf PORTA, DTA goto _s_c_clk _s_c_send0 bcf PORTA, DTA nop _s_c_clk call _cycle_clk decfsz pCounter, F goto _s_c_loop return ; ----- _send_dta -------------------------------------------------- ; Send data to DUP ; Requires 14 bits to be sent to the device (14 LSB from Dregs) ; ------------------------------------------------------------------ _send_dta ; send start bit bcf PORTA, DTA call _cycle_clk movlw 0x08 movwf pCounter _s_d_loop bcf STATUS, C ; clear status reg rrf lDreg, F ; rotate next LSB into status btfss STATUS, C goto _s_d_send0 bsf PORTA, DTA goto _s_d_clk _s_d_send0 bcf PORTA, DTA nop _s_d_clk call _cycle_clk decfsz pCounter, F goto _s_d_loop ; Now get the remaining 6 bits out of the hDreg movlw 0x06 movwf pCounter _s_d_loop2 bcf STATUS, C ; clear status reg rrf hDreg, F ; rotate next LSB into status btfss STATUS, C goto _s_d_send02 bsf PORTA, DTA goto _s_d_clk2 _s_d_send02 bcf PORTA, DTA nop _s_d_clk2 call _cycle_clk decfsz pCounter, F goto _s_d_loop2 ; And send the final stop bit bcf PORTA, DTA call _cycle_clk return ; ----- _cycle_clk ------------------------------------------------- ; Cycle the PGM clock line ; ------------------------------------------------------------------ _cycle_clk bsf PORTA, CLK nop nop nop nop nop bcf PORTA, CLK return ; ----- _read_dta -------------------------------------------------- ; Read data from DUP ; Requires 14 bits to be sent to the device (14 LSB from Dregs) ; ------------------------------------------------------------------ _read_dta ; Change the port direction on porta, dta bank1 bsf TRISA, DTA bank0 nop ; Get timing right ; Get start bit call _cycle_clk ; Get the first 8 bits from the DUP movlw 0x08 movwf pCounter _r_d_loop ; Make clk high bsf PORTA, CLK nop btfss PORTA, DTA goto _r_sto_0 bsf STATUS, C ; set carry goto _r_sto_1 _r_sto_0 bcf STATUS, C _r_sto_1 rrf lDreg, F ; rotate next LSB into status bcf PORTA, CLK ; Make CLK 0 decfsz pCounter, F goto _r_d_loop ; Now get the remaining 6 bits in the hDreg movlw 0x06 movwf pCounter _r_d_loop2 ; Make clk high bsf PORTA, CLK nop btfss PORTA, DTA goto _r_sto_02 bsf STATUS, C ; set carry goto _r_sto_12 _r_sto_02 bcf STATUS, C _r_sto_12 bcf PORTA, CLK ; Make CLK 0 rrf hDreg, F ; rotate next LSB into status decfsz pCounter, F goto _r_d_loop2 ; Do 2 additional shifts to get bits in place bcf STATUS, C rrf hDreg, F rrf hDreg, F ; And get the final stop bit call _cycle_clk ; Reset dta port as output bank1 bcf TRISA, DTA bank0 bcf PORTA, DTA ; Make sure to clear the DTA line return ; Error handler _error ; Disable IRQ bcf INTCON, 7 ; Reset DUP call _reset_part ; Set error flag bsf PORTB, 5 ; Core dump call _debug ; Dump 1st 40 words of device movlw 00 movwf haddr movlw 40 movwf laddr call _read_dev_int _error_lp goto _error_lp ; Include various libs #include "serial.asm" ; Serial Rx/Tx routines #include "strings.asm" ; Contains intro, program menu, ... END