PIC FIFO Assembly Code - Page 1
Forum section: electronics / general

author: Dabbot
author_rank: Regular Contributor
timestamp: June 22, 2019, 09:01:16 am
content: Here's some code for a software FIFO / circular buffer that'll work on a PIC12 and up.I was messing around with the EUSART peripheral on a 12F1572, and hit a couple of corner cases where the hardware buffers weren't quite enough. So I came up with this.Let me know what you think.Code:; Two subroutines to move data on and off a FIFO.; These can be configured for a FIFO size from; 2^1 bytes to 2^7 bytes.; In this example, the FIFO size is 8 bytes (10 bytes).;; These subroutines can be used to move data between; the main program and the interrupt service routine,; as long as it is one way per FIFO. i.e.:; main => FIFO => ISR; or; ISR => FIFO => main;; The FIFO data structure occupies a contiguous block; totalling 2^X + 2 bytes:; Byte 0 is a pointer to the end of the queue.; Byte 1 is a pointer to the start of the queue.; The remaining bytes are the FIFO data registers.;; To initialise or reset a queue, point the FSR to the address; of the FIFO data structure and clear bytes 0 and 1.;; Take care when setting FSR(n)(L) to ensure the data structure; does not cross the 2^8 boundary.; If boundary crossing is required, uncomment the carry checking lines.; Enqueues data to FIFO / circular buffer.; 16 instructions, 2 registers, 1 FSR, no loops.; Works on PIC12 and up.; Interrupt safe, if used one way only per FIFO.;; Initial state:; FSR0 = address of FIFO data structure; DATA_IN = data to be enqueued; WREG = ignored; TEMP = ignored;; Final state (FIFO not full):; WREG = 0, Z clear; FSR0 = address of FIFO data structure; DATA_IN = unchanged; TEMP = offset value;; Final state (FIFO full):; WREG = 0, Z set; FSR0 = address of FIFO data structure; DATA_IN = unchanged; TEMP = unchangedDATA_IN = h'70'TEMP = h'7F'ENQFMOVIW 1 ; Do nothing if FIFO is full. Z will be set.SUBWF INDF0, WXORLW d'8'; 4: 4-byte, 8: 8-byte, 16: 16-byte, 32: 32-byte etc.BTFSC STATUS, ZRETURNMOVF INDF0, W; Store pointerANDLW d'7'; 3: 4-byte, 7: 8-byte, 15: 16-byte, 31: 32-byte etc.MOVWF TEMPADDWF FSR0L, F; Move to next free spot;BTFSC STATUS, C; Uncomment for 2^8 boundary crossing;INCF FSR0H, FMOVF DATA_IN, W; Enqueue dataMOVWI 2 MOVF TEMP, WSUBWF FSR0L, F;BTFSS STATUS, C; Uncomment for 2^8 boundary crossing;DECF FSR0H, FINCF INDF0, F; Increment pointerBCF STATUS, ZRETLW d'0'; Done. Z will be clear.; Dequeues data from FIFO / circular buffer.; 16 instructions, 2 registers, 1 FSR, no loops.; Works on PIC12 and up.; Interrupt safe, if used one way only per FIFO.;; Initial state:; FSR0 = address of FIFO data structure; DATA_OUT = ignored; WREG = ignored; TEMP = ignored;; Final state (FIFO not full):; WREG = 0, Z clear; FSR0 = address of FIFO data structure; DATA_OUT = dequeued data; TEMP = offset value;; Final state (FIFO full):; WREG = 0, Z set; FSR0 = address of FIFO data structure; DATA_OUT = unchanged; TEMP = unchangedDATA_OUT = h'71'TEMP = h'7F'DEQFMOVIW 1 ; Do nothing if FIFO is empty. Z will be set.XORWF INDF0, WBTFSC STATUS, ZRETURNMOVIW ++ FSR0; Increment FSR position and store pointerANDLW d'7'; 3: 4-byte, 7: 8-byte, 15: 16-byte, 31: 32-byte etc.MOVWF TEMPADDWF FSR0L, F; Move to next item;BTFSC STATUS, C; Uncomment for 2^8 boundary crossing;INCF FSR0H, FMOVIW 1 ; Dequeue dataMOVWF DATA_OUTMOVF TEMP, WSUBWF FSR0L, F;BTFSS STATUS, C; Uncomment for 2^8 boundary crossing;DECF FSR0H, FINCF INDF0, F; Increment pointerADDFSR FSR0, -1; Decrement FSR positionBCF STATUS, ZRETLW d'0'; Done. Z will be clear.; ***** PIC10 compatible code below *****; Two subroutines to move data on and off a FIFO.; These can be configured for a FIFO size from; 2^1 bytes to 2^7 bytes.; In this example, the FIFO size is 8 bytes (10 bytes).;; These subroutines are NOT interrupt safe.;; The FIFO data structure occupies a contiguous block; totaling 2^X + 2 bytes:; Byte 0 is a count of the number of items queued.; Byte 1 is a pointer to the start of the queue.; Unused bits of byte 1 are masked out and should be ignored.; The remaining bytes are the FIFO data registers.;; To initialise or reset a queue, point the FSR to the address; of the FIFO data structure and "CLRF INDF". This simply; clears the item count.;; Take care when setting FSR(n)(L) to ensure the data structure; does not cross the 2^8 boundary.; If boundary crossing is required, modify each subroutine with; the following directly after "ADDWF FSR(n)L, F":; BTFSC STATUS, C; INCF FSR(n)H, F; Enqueues data to FIFO / circular buffer.; 13 instructions, 1 register, 1 FSR, no loops.; Works on PIC10 and up.;; Initial state:; TEMP = data to be enqueued; FSR(n)(L) = address of FIFO data structure;; Final state (FIFO not full):; WREG = 0, Z clear; FSR(n)(L) = address of enqueued item on FIFO;; Final state (FIFO full):; WREG = 0, Z set; FSR(n)(L) = address of FIFO data structureTEMP = h'40'ENQFCLRW; Do nothing if FIFO is full. Z will be set.BTFSC INDF, 3; 2: 4-byte, 3: 8-byte, 4: 16-byte, 5: 32-byte etc.RETURNMOVF INDF, W; Store current item count in WINCF INDF, F; Increment item count registerINCF FSR, F; Advance to pointer register; Replace this line with "ADDFSR FSRn, d'1'" on supported chipsADDWF INDF, W; Calculate the offset to the next available spot in the queueANDLW d'7'; 3: 4-byte, 7: 8-byte, 15: 16-byte, 31: 32-byte etc.ADDLW d'1'ADDWF FSR, F; Move to the end of the queue. Clears Z.; Add CARRY to FSR high byte on supported chips, if crossing 2^8 boundary.SWAPF TEMP, W; Enqueue data. Use SWAPF to leave Z clear.MOVWF INDF; Optionally, this can be done elsewhere, leaving the subroutine; to simply point the FSR at the appropriate location.RETLW d'0'; Done. Z will be clear.; Dequeues data from FIFO / circular buffer.; 12 instructions, 1 FSR, no loops.; Works on PIC10 and up.;; Initial state:; FSR(n)(L) = address of FIFO data structure;; Final state (FIFO not empty):; WREG = dequeued data, Z clear; FSR(n)(L) = address of dequeued item on FIFO;; Final state (FIFO empty):; WREG = 0, Z set; FSR(n)(L) = address of FIFO data structureDEQFMOVF INDF, W; Do nothing if FIFO is empty. Z will be set.BTFSC STATUS, ZRETURNDECF INDF, F; Decrement item countINCF FSR, F; Advance to pointer register; Replace this line and the following MOVF; with a pre-increment MOVIW on supported chips.MOVF INDF, W; Calculate the offset to the next item off the queueANDLW d'7'; 3: 4-byte, 7: 8-byte, 15: 16-byte, 31: 32-byte etc.ADDLW d'1'INCF INDF, F; Increment pointerADDWF FSR, F; Move to the next item off the queue. Clears Z.; Add CARRY to FSR high byte on supported chips, if crossing 2^8 boundarySWAPF INDF, W; Dequeue data. Use SWAPF to leave Z clear.; Optionally, this can be done elsewhere, leaving the subroutine; to simply point the FSR at the appropriate location.RETURN; Done. Z will be clear.Edit 1: Added code for PIC12F and up including NorthGuy's suggestions for interrupt access and FSR instructions.Dabbot.

author: NorthGuy
author_rank: Super Contributor
timestamp: June 22, 2019, 01:59:50 pm
content: Usually you would want your FIFO to be accessed through interrupt on one end and main code on the other. For example, interrupt writes and main code reads. Or main code writes and interrupt reads. Thus, IMHO the FIFO code must be written so that read and write parts can interrupt each other at any point.Newer PICs have much better instructions to deal with FSR, and they're substantially different between PIC16 and PIC18. Your code could greatly benefit from these instructions. But of course, you would need several versions for different PICs.

author: Dabbot
author_rank: Regular Contributor
timestamp: June 22, 2019, 11:18:27 pm
content: I did intend to use this between normal code and an ISR, but temporarily disable interrupts when accessing it in normal code.What I wanted with this approach, using a pointer and a count, is a trivial way to determine the number of items queued. But this code is certainly not interrupt proof.I suspect I'll need to use two pointers in an interrupt proof implementation. I'll give it some thought and see what I can come up with.Thanks!Edit: And done. Added code with your suggestions.Dabbot.