;------------------------------------------------------------------------------ ; Manchester University ARM/Xilinx board software ; Flash ROM programming code ; ; J. Garside November 2001 ; To do: ; Locking ; Do not corrupt: ; R9 contains lowest modifiable address ; R10 contains base address (if needed) ; R11 contains pointer to UART flash_size EQU &00200000 ; 2 Mbytes flash_protected EQU &00004000 ; 16 Kbytes - can't modify own ROM ; Protects up to boot_table Flash_load_buffer EQU &800 ; Safely above code if in RAM Flash_comm_buff_len EQU 16 ; NB Reserve 18 bytes Erase_timeout EQU &02000000 ; About 6s if ALL at 40MHz Write_timeout EQU &120 ; 50us if ALL at 40MHz Flash_Challenge EQU &FEA51B1E ; Flash_Response EQU &FEE1900D ; ;------------------------------------------------------------------------------ Flash_check bl Prog_Host_in ; Get first byte cmp r0, #(Flash_Challenge :SHR: 24) :AND: &FF bne Flash_check ; Fail, keep listening bl Prog_Host_in ; Get second byte cmp r0, #(Flash_Challenge :SHR: 16) :AND: &FF bne Flash_check ; bl Prog_Host_in ; etc. cmp r0, #(Flash_Challenge :SHR: 8) :AND: &FF bne Flash_check ; bl Prog_Host_in ; cmp r0, #Flash_Challenge :AND: &FF bne Flash_check ; mov r0, #(Flash_Response :SHR: 24) :AND: &FF bl Prog_Host_out ; Send reply ... mov r0, #(Flash_Response :SHR: 16) :AND: &FF bl Prog_Host_out ; mov r0, #(Flash_Response :SHR: 8) :AND: &FF bl Prog_Host_out ; mov r0, #Flash_Response :AND: &FF bl Prog_Host_out ; prog_loop bl Prog_Host_in ; Get command and r0, r0, #&7F ; Clumsy dispatcher! cmp r0, #"R" ; beq prog_read ; cmp r0, #"W" ; beq prog_write ; (Programme) cmp r0, #"E" ; beq prog_erase ; cmp r0, #"P" ; beq prog_ping ; cmp r0, #"L" ; beq prog_lockout ; cmp r0, #"C" ; beq prog_check_lock ; cmp r0, #"I" ; beq prog_dev_ID ; ; Else not recognised bl Prog_Host_out ; Echo character b prog_loop ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - prog_read bl Prog_Host_get_word ; mov r1, r0 ; Address bl Prog_Host_get_word ; movs r2, r0 ; Length beq prog_read_out ; Quit if zero bytes prog_read_1 mov r3, #Flash_comm_buff_len; prog_read_2 bl flash_r_byte ; Get byte add r1, r1, #1 ; Increment address bl Prog_Host_out ; Send character subs r2, r2, #1 ; Overall count beq prog_read_out ; All done - exit subs r3, r3, #1 ; Count on this `line' bhi prog_read_2 ; Repeat <= "Flash_comm_buff_len" ; times bl Prog_Host_in ; Then await an "A" cmp r0, #"A" ; beq prog_read_1 ; Another buffer load b prog_loop ; or give up! prog_read_out bl Prog_Host_in ; Then await an "A" (etc.) b prog_loop ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Write a sequence to the Flash ; No real verification included prog_write bl Prog_Host_get_word ; Programme flash mov r1, r0 ; Address bl Prog_Host_get_word ; movs r2, r0 ; Length beq prog_write_out ; Quit if zero bytes mov r0, #"N" ; Not a real acknowledge cmp r1, r9 ; Protected area at bottom blo prog_write_error ; add r3, r1, r2 ; Find last address cmp r3, #flash_size ; 2Mbyte flash size bhi prog_write_error ; mov r6, r1 ; Copy of address (in case odd) tst r6, #1 ; Odd address? beq prog_write_1 ; no bic r1, r1, #1 ; Deal with odd start address mov r4, #Flash_load_buffer ; Temp. buffer space bl flash_r_byte ; Read previous address [R1] strb r0, [r4] ; into first buffer location prog_write_1 mov r4, #Flash_load_buffer ; Temp. buffer space mov r3, #Flash_comm_buff_len; tst r6, #1 ; Odd address? addne r4, r4, #1 ; yes - start one place in prog_write_2 bl Prog_Host_in ; Get byte strb r0, [r4], #1 ; Save byte subs r2, r2, #1 ; Total count beq prog_write_tidy ; Got last one subs r3, r3, #1 ; Line count bhi prog_write_2 ; Repeat <= through buffer mov r4, #Flash_load_buffer ; Temp. buffer space mov r3, #Flash_comm_buff_len/2 ; Now in half words prog_write_3 ldrh r0, [r4], #2 ; bl flash_write ; No verification yet @@@ add r1, r1, #2 ; Destination address subs r3, r3, #1 ; bhi prog_write_3 ; Buffer output loop tst r6, #1 ; Odd start address? ldrneb r0, [r4] ; then move last byte to start strneb r0, [r4, #-Flash_comm_buff_len] ; Next pass must get this one mov r0, #"A" ; bl Prog_Host_out ; Send Acknowledge b prog_write_1 ; and repeat prog_write_tidy sub r3, r4, #Flash_load_buffer; Count remaindered bytes tst r3, #1 ; Odd end address movne r0, #&FF ; if so, pad with FF strneb r0, [r4] ; (Does no damage & not tested for) addne r3, r3, #1 ; movs r3, r3, lsr #1 ; Now in half words beq prog_write_out ; None extra mov r4, #Flash_load_buffer ; Temp. buffer space prog_write_4 ldrh r0, [r4], #2 ; bl flash_write ; No verification yet @@@ add r1, r1, #2 ; Destination address subs r3, r3, #1 ; bhi prog_write_4 ; Buffer output loop (II) prog_write_out mov r0, #"A" ; bl Prog_Host_out ; Send Acknowledge b prog_loop ; and relax! prog_write_error cmp r2, #Flash_comm_buff_len; Must be at least one byte movhi r2, #Flash_comm_buff_len; Only accept one `line' prog_write_error1 bl Prog_Host_in ; Get byte subs r2, r2, #1 ; bhi prog_write_error1 ; Repeat for `line' mov r0, #"N" ; bl Prog_Host_out ; Send Non-acknowledge b prog_loop ; and relax! ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Erase block ; Does not check block for blank; ; such would need table of block sizes for different devices. prog_erase bl Prog_Host_get_word ; mov r2, r0 ; Address into R2 mov r0, #"N" ; Not a real acknowledge cmp r2, r9 ; Protected area blo prog_erase_error ; cmp r2, #flash_size ; 2Mbyte flash size bhs prog_erase_error ; Address too big mov r0, #&AA ; Data #1 mov r1, #&55 ; Address orr r1, r1, #&5500 ; mov r1, r1, lsl #1 ; Flash A0 is our A1 bl flash_w_hword ; mov r0, #&55 ; Data #2 mov r1, #&AA ; Address orr r1, r1, #&2A00 ; mov r1, r1, lsl #1 ; Flash A0 is our A1 bl flash_w_hword ; mov r0, #&80 ; Data #3 mov r1, #&55 ; Address orr r1, r1, #&5500 ; mov r1, r1, lsl #1 ; Flash A0 is our A1 bl flash_w_hword ; mov r0, #&AA ; Data #4 bl flash_w_hword ; mov r0, #&55 ; Data #5 mov r1, #&AA ; Address orr r1, r1, #&2A00 ; mov r1, r1, lsl #1 ; Flash A0 is our A1 bl flash_w_hword ; mov r1, r2 ; Restore address to R1 mov r0, #&30 ; Data #6 bl flash_w_hword ; mov r0, #&FF ; Expected value mov r5, #Erase_timeout ; bl flash_wtr ; Could check R5 for timeout tst r5, r5 ; moveq R0, #"N" ; Timeout indicated movne r0, #"A" ; Acknowledge if no timeout prog_erase_error bl Prog_Host_out ; Timeout is not, necessarily b prog_loop ; the only fault. ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - prog_ping mov r0, #"A" ; Just Acknowledge bl Prog_Host_out ; b prog_loop ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - prog_lockout ; Nothing for now @@@ bl Prog_Host_out ; Echo character @@@ b prog_loop ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - prog_check_lock bl Prog_Host_get_word ; bic r1, r0, #&FF ; Address bic r1, r1, #&1F00 ; Clear (most) lower bits orr r1, r1, #4 ; Lockout check bl flash_ID ; tst r0, #1 ; moveq r0, #"N" ; Not locked movne r0, #"L" ; Locked bl Prog_Host_out ; b prog_loop ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - prog_dev_ID mov r1, #0 ; Manufacturer code bl flash_ID ; bl Prog_Host_out ; mov r1, #2 ; Part code bl flash_ID ; bl Prog_Host_out ; b prog_loop ; ;------------------------------------------------------------------------------ Prog_Host_get_word stmfd sp!, {r1,lr} ; bl Prog_Host_in ; mov r1, r0 ; bl Prog_Host_in ; orr r1, r1, r0, lsl #8 ; bl Prog_Host_in ; orr r1, r1, r0, lsl #16 ; bl Prog_Host_in ; orr r0, r1, r0, lsl #24 ; ldmfd sp!, {r1,pc} ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Remote flash memory write routine ; Halfword data in R0, address in R1 ; On exit R5 = 0 indicates timeout; R5 <> 0 indicates (probably) okay flash_write stmfd sp!, {r0-r3, lr} ; mov r0, #&AA ; Data #1 mov r1, #&55 ; Address orr r1, r1, #&5500 ; mov r1, r1, lsl #1 ; Flash A0 is our A1 bl flash_w_hword ; mov r0, #&55 ; Data #2 mov r1, #&AA ; Address orr r1, r1, #&2A00 ; mov r1, r1, lsl #1 ; Flash A0 is our A1 bl flash_w_hword ; mov r0, #&A0 ; Data #3 mov r1, #&55 ; Address orr r1, r1, #&5500 ; mov r1, r1, lsl #1 ; Flash A0 is our A1 bl flash_w_hword ; ldmfd sp, {r0-r1} ; Restore data (NOT a pop) bl flash_w_hword ; and r0, r0, #&FF ; Just test one byte mov r5, #Write_timeout ; bl flash_wtr ; Wait for written (or timeout) ldmfd sp!, {r0-r3, pc} ; ;; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ;; Substitutes for monitor read routines to remote flash ; ;flash_read_w stmfd sp!, {r0, r2-r3, lr} ; ; bic r2, r2, #3 ; Align anyway ; mov r3, #0 ; Accumulator ; mov r1, r2 ; Address ;1 bl flash_r_byte ; Fetch R0 ; orr r3, r0, r3, ror #8 ; Data into acc. ; add r1, r1, #1 ; Inc. address ; tst r1, #3 ; Bottom addr. bits clear again? ; bne %b1 ; no ; mov r1, r3, ror #8 ; Realign for output ; ldmfd sp!, {r0, r2-r3, pc} ; ; ;flash_read_b stmfd sp!, {r0, lr} ; ; mov r1, r2 ; Address ; bl flash_r_byte ; ; mov r1, r0 ; Data ; ldmfd sp!, {r0, pc} ; ;------------------------------------------------------------------------------ ; Read flash ID from address R1 into R0 ; 00000 = Manufacturer ; 00002 = Part code ; xx004 = Sector lockout state flash_ID stmfd sp!, {r1-r3, lr} ; mov r0, #&AA ; Data #1 mov r1, #&55 ; Address orr r1, r1, #&5500 ; mov r1, r1, lsl #1 ; Flash A0 is our A1 bl flash_w_hword ; mov r0, #&55 ; Data #2 mov r1, #&AA ; Address orr r1, r1, #&2A00 ; mov r1, r1, lsl #1 ; Flash A0 is our A1 bl flash_w_hword ; mov r0, #&90 ; Data #3 mov r1, #&55 ; Address orr r1, r1, #&5500 ; mov r1, r1, lsl #1 ; Flash A0 is our A1 bl flash_w_hword ; ldmfd sp, {r1} ; Restore R1 (address) bl flash_r_byte ; mov r1, r0 ; Keep answer mov r0, #&F0 ; Leave ID mode bl flash_w_hword ; Address irrelevant mov r0, r1 ; Reorganise registers ldmfd sp!, {r1-r3, pc} ; ;------------------------------------------------------------------------------ END