Shop OBEX P1 Docs P2 Docs Learn Events
SPI Engine faster ? — Parallax Forums

SPI Engine faster ?

BTXBTX Posts: 674
edited 2007-01-02 16:04 in Propeller 1
Hi all .
I was looking for an SPI engine object, and I've found, Beau Schwabe·code object.
Beau said...
Notes:
Consecutive SHIFTIN/SHIFTOUT updates through Spin at 80 MHz is about 14kHz (Bit rate about 112k)

Consecutive SHIFTIN/SHIFTOUT updates through Assembly at 80 MHz is about 310kHz (Bit rate about 2.5M)
How to call SPI through assembly faster ?.
Is avoiding the "setcommand(cmd, argptr)" method in the engine, and passing data by command directly·?
Or are you refering, another way, to get 2.5M in assembly ?
If I could do a caller in assembly....how to call SPI Object ?? If I'll do in spin, just your demo code !! it's did·...sorry that's confusing me.

This is a part of the engine for reference:
CON
  #1,_SHIFTOUT,_SHIFTIN
VAR
    long     cog, command, Flag
PUB SHIFTOUT(Dpin, Cpin, Mode, Bits, Value)             'Once called from Spin, SHIFTOUT remains running in its own COG.
    if Flag == 0                                        'If SHIFTOUT is called with 'Bits' set to Zero, then the COG will shut
       start                                            'down.  Another way to shut the COG down is to call 'stop' from Spin.
    setcommand(_SHIFTOUT, @Dpin)
PUB SHIFTIN(Dpin, Cpin, Mode, Bits)|Value               'Once called from Spin, SHIFTIN remains running in its own COG.
    if Flag == 0                                        'If SHIFTIN is called with 'Bits' set to Zero, then the COG will shut
       start                                            'down.  Another way to shut the COG down is to call 'stop' from Spin.
    setcommand(_SHIFTIN, @Dpin)
    result := Value
'------------------------------------------------------------------------------------------------------------------------------
PUB start : okay
'' Start SPI Engine - starts a cog
'' returns false if no cog available
    stop
    Flag := 1
    okay := cog := cognew(@loop, @command) + 1
PUB stop
'' Stop SPI Engine - frees a cog
    Flag := 0
    if cog
       cogstop(cog~ - 1)
    command~
PRI setcommand(cmd, argptr)
    command := cmd << 16 + argptr                       'write command and pointer
    repeat while command                                'wait for command to be cleared, signifying receipt
'################################################################################################################
DAT           org
'  
' SPI Engine - main loop
'
loop          rdlong  t1,par          wz                'wait for command
        if_z  jmp     #loop
              movd    :arg,#arg0                        'get 5 arguments ; arg0 to arg4
              mov     t2,t1                             '    &#9474;
              mov     t3,#5                             '&#61626;&#9472;&#9472;&#9472;&#9496; 
:arg          rdlong  arg0,t2
              add     :arg,d0
              add     t2,#4
              djnz    t3,#:arg
              mov     address,t1                        'preserve address location for passing
                                                        'variables back to Spin language.

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔

Comments

  • asterickasterick Posts: 158
    edited 2006-12-31 05:52
    My recommendation would be to do it asynchronously.

    have the cog always running, and give it a status byte to determine it's current status (you can use locks as well if you wish to have halting calls)


    Something like

    VAR
        byte count, status
        byte buffer[noparse][[/noparse]data]
    
    



    Have the cog running the SPI routines poll count... if it is non zero, set the lock, and set status to logical true. Have it progressively shift out the buffer, while shifting in to the same buffer. After it's done, release the lock and set status to zero.

    That way, you main application can do other things while it's running, and you can spi out multiple bytes.

    Also, avoiding generic SPI calls would aid you in avoiding large delays. You my also consider just writting it in spin. Although the execution time is relatively low, you would avoid wasting a cog and there is no need for excessive load times.

    Remember that booting a cog takes ~8000 cycles, so spin would most likely be faster.
  • BTXBTX Posts: 674
    edited 2006-12-31 15:37
    Dear asterick.
    First·of all, thanks so much fo the suggests.
    I'm totally agree with you, about to have the cog always running, I don't know that, but 8000 clocks is too much time while booting a cog.
    Just I need to send packets of data (of 192 Bit each) to a SPI peripheral, it is important to do that, and maintain those packets in a buffer, so you're poiting exactly in my future application.
    Perhaps, I can not express me correctly, and could be a missunderstanding in my questions (or I don't understand your explanation...).
    Tha byte that you name "status" ....is not the same as Flag, in the example that I post. ?? (Supoosing the cog is running and the "start' line is not write there....)
    If I have the cog running all time, How to pass the values to the SPI engine ?? or.. how to get the comand variable actualized, with a new value from outside the SPI driver..?
    PRI setcommand(cmd, argptr)
        command := cmd << 16 + argptr                       'write command and pointer
        repeat while command                                'wait for command to be cleared, signifying receipt
    
    

    Could I avoid this part of spin code ?? and be replaced with some using command := buffer[noparse][[/noparse]data1] etc. ?

    Regards and Happy New Year !!

    Alberto.



    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
  • asterickasterick Posts: 158
    edited 2006-12-31 20:19
    It's really just a matter of preference. You can really implement it however you want...

    You just want to have the cog that would be doing the SPI code constantly polling a value (command), and when it sees a non-zero command, have it start it's shift in / shift out code. flags / status would just be a method of determining the status.

    You would also want the cog to clear the command value so it doesn't auto repeat after everything is said and done.
  • asterickasterick Posts: 158
    edited 2006-12-31 20:53
    Bearing in mind that I'm not great with parallax assembly just yet (I just discovered the chip a couple of days ago)...
    I went ahead and slapped this together. It's basically the most efficient method of doing SPI transfers if the buffers are largeish.

    The SPI code is missing because I'm not 100% sure on the asm syntax, but it should at least give you an idea of what I'm thinking of.

    CON
      CLK_PIN       = %0000_0000_0000_0001
      DIN_PIN       = %0000_0000_0000_0010
      DOU_PIN       = %0000_0000_0000_0100   
    
    PUB start
      command := 0                                          ' Initalize to zero bytes to copy
      spi_lock := LOCKNEW                                   ' Create a new lock
      spi_cog := COGNEW( @entry, spi_stack )                ' Start our cog             
    
    PUB stop
      COGSTOP( spi_cog )
    
    PUB shift(bytes, count) | i
      repeat until not LOCKSET( spi_lock )
      repeat i from 0 to count
        buffer[i] := byte[noparse][[/noparse] bytes ][noparse][[/noparse] i ]
      command := count    
    
    PUB getStatus
      return LOCKSET( spi_lock )
    
    DAT           ORG
    
    entry         MOV             outa, 0
                  MOV             dira, #(DOU_PIN | CLK_PIN)                        ' Set output pins              
    wait_loop     RDBYTE          bytes_copy, command   WC                          ' Loop until we have a buffer to scan out
                  IF_Z  JMP       #wait_loop
    spi_out       LOCKSET         spi_lock
                  MOV             buff_start, @buffer
    start_byte    MOV             byte_in, 0
                  MOV             bits_left, 8
                  RDBYTE          byte_out, 0
                  ADD             buff_start, #1
    shift_out     ' TODO: SPI OUT              
                  DJNZ            bits_left, shift_out              
                  DJNZ            bytes_copy, #start_byte                                 
                  LOCKCLR         spi_lock
                                                                   
    ' Local memory for the SPI cog
    bytes_copy    LONG            0
    byte_temp     LONG            0
    bits_left     LONG            0
    buff_start    LONG            0
    
    ' Global memory for the SPI cog
    command       LONG            0 
    spi_cog       LONG            -1
    spi_lock      LONG            -1
    buffer        BYTE            $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
                  BYTE            $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
                  BYTE            $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
                  BYTE            $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
                  BYTE            $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
                  BYTE            $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
                  BYTE            $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
                  BYTE            $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
                  BYTE            $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
                  BYTE            $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
                  BYTE            $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
                  BYTE            $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
                  BYTE            $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
                  BYTE            $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
                  BYTE            $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF
                  BYTE            $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF                                          
    spi_stack     LONG            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0          
    [/i]
    
  • rokickirokicki Posts: 1,000
    edited 2007-01-01 23:22
    You might download the code in the SD read/write thread (or get it at http://tomas.rokicki.com/fat16work/)
    because this includes some assembly SPI code that is for the secure digital cards. There is one Spin version
    and two assembly versions. They are not the absolute fastest but they are not bad.
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2007-01-02 04:58
    BTX,

    Sorry, I haven't replied sooner, I have been busy over the Christmas and New Year holidays.



    The two statements...

    Consecutive SHIFTIN/SHIFTOUT updates through Spin at 80 MHz is about 14kHz (Bit rate about 112k)
    Consecutive SHIFTIN/SHIFTOUT updates through Assembly at 80 MHz is about 310kHz (Bit rate about 2.5M)


    ...are simply indicating a measured thru-put based on an 8-byte data value. If you are using Spin and send
    data in a continuous stream. You can expect a thru-put of about 14kHz. If you strip down the assembly code
    essentials and use the assembly portion that performs SHIFTIN/SHIFTOUT routines and never leave the assembly
    environment, then the thru-put increases to about 310kHz.



    http://forums.parallax.com/showthread.php?p=602056

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.
  • BTXBTX Posts: 674
    edited 2007-01-02 16:04
    Thanks everybody for the replies.
    I'll be trying your suggests, after a new post.

    Regards.
    Alberto.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Sign In or Register to comment.