Author Topic: PIC FIFO Assembly Code  (Read 2071 times)

0 Members and 1 Guest are viewing this topic.

Offline DabbotTopic starter

  • Regular Contributor
  • *
  • Posts: 198
  • Country: au
PIC FIFO Assembly Code
« on: June 22, 2019, 09:01:16 am »
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: [Select]

; 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 = unchanged

DATA_IN = h'70'
TEMP = h'7F'

ENQF MOVIW 1 [FSR0] ; Do nothing if FIFO is full. Z will be set.
SUBWF INDF0, W
XORLW d'8' ; 4: 4-byte, 8: 8-byte, 16: 16-byte, 32: 32-byte etc.
BTFSC STATUS, Z
RETURN

MOVF INDF0, W ; Store pointer
ANDLW d'7' ; 3: 4-byte, 7: 8-byte, 15: 16-byte, 31: 32-byte etc.
MOVWF TEMP

ADDWF FSR0L, F ; Move to next free spot
;BTFSC STATUS, C ; Uncomment for 2^8 boundary crossing
;INCF FSR0H, F

MOVF DATA_IN, W ; Enqueue data
MOVWI 2 [FSR0]

MOVF TEMP, W
SUBWF FSR0L, F
;BTFSS STATUS, C ; Uncomment for 2^8 boundary crossing
;DECF FSR0H, F

INCF INDF0, F ; Increment pointer

BCF STATUS, Z
RETLW 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 = unchanged

DATA_OUT = h'71'
TEMP = h'7F'

DEQF MOVIW 1 [FSR0] ; Do nothing if FIFO is empty. Z will be set.
XORWF INDF0, W
BTFSC STATUS, Z
RETURN

MOVIW ++ FSR0 ; Increment FSR position and store pointer
ANDLW d'7' ; 3: 4-byte, 7: 8-byte, 15: 16-byte, 31: 32-byte etc.
MOVWF TEMP

ADDWF FSR0L, F ; Move to next item
;BTFSC STATUS, C ; Uncomment for 2^8 boundary crossing
;INCF FSR0H, F

MOVIW 1 [FSR0] ; Dequeue data
MOVWF DATA_OUT

MOVF TEMP, W
SUBWF FSR0L, F
;BTFSS STATUS, C ; Uncomment for 2^8 boundary crossing
;DECF FSR0H, F

INCF INDF0, F ; Increment pointer

ADDFSR FSR0, -1 ; Decrement FSR position

BCF STATUS, Z
RETLW 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 structure

TEMP = h'40'

ENQF CLRW ; 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.
RETURN

MOVF INDF, W ; Store current item count in W
INCF INDF, F ; Increment item count register

INCF FSR, F ; Advance to pointer register
; Replace this line with "ADDFSR FSRn, d'1'" on supported chips

ADDWF INDF, W ; Calculate the offset to the next available spot in the queue
ANDLW 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 structure

DEQF MOVF INDF, W ; Do nothing if FIFO is empty. Z will be set.
BTFSC STATUS, Z
RETURN

DECF INDF, F ; Decrement item count

INCF 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 queue
ANDLW d'7' ; 3: 4-byte, 7: 8-byte, 15: 16-byte, 31: 32-byte etc.
ADDLW d'1'

INCF INDF, F ; Increment pointer

ADDWF 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 boundary

SWAPF 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.
« Last Edit: June 24, 2019, 08:29:33 am by Dabbot »
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3547
  • Country: ca
Re: PIC FIFO Assembly Code
« Reply #1 on: June 22, 2019, 01:59:50 pm »
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.
 

Offline DabbotTopic starter

  • Regular Contributor
  • *
  • Posts: 198
  • Country: au
Re: PIC FIFO Assembly Code
« Reply #2 on: June 22, 2019, 11:18:27 pm »
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.
« Last Edit: June 23, 2019, 12:35:05 pm by Dabbot »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

-->