Shop OBEX P1 Docs P2 Docs Learn Events
Help with SPIN bidirectional SPI > 32 bits — Parallax Forums

Help with SPIN bidirectional SPI > 32 bits

I looked through the obex and nothing jumped out at me so I thought I'd ask for some help. What I need to do is shift synchronous serial data out, LSB first, while simultaneously shifting data in, LSB first. (I'm pretty sure the data in is LSB, the code I have now shifts in MSB but pretty sure it does the reversal in the calling method.) It needs to be variable number of bits and handle multi-long transfers. The other must have is pure spin.

One of the problems I'm having is with endian-ness.. I still need to handle how ASCII hex string is parsed into the longs and since they're both up in the air right now I just keep getting lost. I'm probably not describing the problem 100% so here's an example. The original code is able to shift variable number of bits in/out, up to 1 long.
PRI Shift_Array(array, num_bits) : ret_value | i 
{
    Shifts an array of bits into the TAP while reading data back out.
    This method is called when the TAP state machine is in the Shift_DR or Shift_IR state.
}
  ret_value := 0
  
  repeat i from 1 to num_bits
    outa[TDI] := array & 1    ' Output data to target, LSB first
    array >>= 1 

    ret_value <<= 1     
    ret_value |= ina[TDO]     ' Receive data, shift order depends on target
     
    if (i == num_bits)        ' If at final bit...
      outa[TMS] := 1            ' Go to Exit1
      
    TCK_Pulse

I think this code could work, just compute how many times to loop. What I really want to do is pass a pointer to the array. Not sure how it should return the data though. This returned data needs to be optionally compared against an expected output data, with a mask. That should be farily simple once I figure out how to get data in and out. Understanding this shift problem should help with writing the parsing code.

Right now I have this written to theoretically work with 32 bit data. SIR is never more than 8 bits for this chip though.
   ifnot str.stringCompareCS(c, @SIR)                   ' sir always 8 bits
{
     if ck > 32        ' SIR > 32 bits not supported, only 8 bit SIR testing
       pst.str(string(13,10,"SIR > 32 Clocks"))
       return
}
     tdi := nu.FromStr(long[p][2], nu#HEX)
     isTdo   := false
     isMask  := false
{
     pst.str(String(13,10))
     pst.str(@commandbuffer)
     pst.str(String(13,10,"parameters = "))
     pst.dec(n)
     pst.str(String(13,10)) 
     pst.dec(t)
}{          
     repeat i from 1 to t    
       pst.str(long[p][(i*2)])    
       pst.hex(nu.FromStr(long[p][(i*2)], nu#HEX),2)
       pst.str(String(" "))
}
     if n == 5                 
       ifnot str.stringCompareCS(long[p][3], string("TDO"))
         tdo := nu.FromStr(long[p][4], nu#HEX) 
         isTdo := true        
     if n == 7                  
       ifnot str.stringCompareCS(long[p][3], string("TDO"))
         tdo := nu.FromStr(long[p][4], nu#HEX)
         isTdo := true
         ifnot str.stringCompareCS(long[p][5], string("MASK"))
           mask := nu.FromStr(long[p][6], nu#HEX)
           isMask := true           
       elseifnot str.stringCompareCS(long[p][5], string("TDO"))
         tdo := nu.FromStr(long[p][6], nu#HEX)         
         isTdo := true     
     if n == 9
       if str.stringCompareCS(long[p][3], string("SMASK"))
         pst.str(string("bad data format"))
         return
       if str.stringCompareCS(long[p][5], string("TDO"))
         pst.str(string("bad data format"))
         return
       if str.stringCompareCS(long[p][7], string("MASK"))
         pst.str(string("bad data format"))
         return
       tdo := nu.FromStr(long[p][6], nu#HEX) 
       mask := nu.FromStr(long[p][8], nu#HEX)
       isTdo := true
       isMask := true 

     '' lets debug all this stuff above for a basic sanity check
{
     pst.str(string(13,10,"CLOCKS "))
     pst.dec(ck)                     
     pst.str(string(" TDI"))
     pst.str(nu.ToStr(tdi, nu#HEX))  
     if isTdo
       pst.str(string(" TDO"))
       pst.str(nu.ToStr(tdo, nu#HEX))        
     if isMask
       pst.str(string(" MASK"))
       pst.str(nu.ToStr(mask, nu#HEX))
}
     result := jtag.Send_Instruction(tdi ,ck)
     if isTdo
       result ^= tdo
       if isMask
         result &= mask
          ifnot result          '' DEBUG SUCESS OR FAIL HERE!!
            pst.str(String("SIR COMPARE FAIL"))

Comments

  • msrobotsmsrobots Posts: 3,709
    edited 2019-02-15 01:19
    how about this
    
    PRI Shift_Array(inarrayaddress, outarrayaddress, num_bits) : ret_value | i, j 
    {
        Shifts an array of bits into the TAP while reading data back out.
        This method is called when the TAP state machine is in the Shift_DR or Shift_IR state.
    }
      ret_value := 0
      
      repeat i from 1 to num_bits
        j :=(( i - 1) >> 32) << 2
        outa[TDI] := long[inarrayaddress + j] & 1    ' Output data to target, LSB first
        long[inarrayaddress + j] >>= 1 
    
        long[outarrayaddress + j] <<= 1     
        long[outarrayaddress + j] |= ina[TDO]     ' Receive data, shift order depends on target
         
        if (i == num_bits)        ' If at final bit...
          outa[TMS] := 1            ' Go to Exit1
          
        TCK_Pulse        ....
    
  • cheezuscheezus Posts: 298
    edited 2019-02-15 01:46
    I knew I was overthinking it!! Thanks!

    *edit

    Looking at that again, this doesn't seem quite right...:
        j :=( i >> 32) << 2   
    
    I think the divide is 32, so the right shift would be 6? Not sure about the left shift...

    The math is escaping me at the moment. Perhaps when the twins go to sleep.


  • cheezuscheezus Posts: 298
    edited 2019-02-15 02:08
    Okay, I think this should do it. Now to follow the logic back to the caller...

    PRI Shift_Large_Array(inarrayaddress, outarrayaddress, num_bits) | i, j, n 
    {
        Shifts an array of bits into the TAP while reading data back out.
        This method is called when the TAP state machine is in the Shift_DR or Shift_IR state.
    }
    
      n := num_bits -1 
    
      repeat i from 0 to n
      
        j :=(i >> 5)  
    
        outa[TDI] := long[inarrayaddress] [j] & 1    ' Output data to target, LSB first
        long[inarrayaddress] [j] >>= 1 
    
        long[outarrayaddress] [j] <<= 1     
        long[outarrayaddress] [j] |= ina[TDO] ' Receive data, shift order depends on target
         
        if (i == n)                 ' If at final bit... 
          outa[TMS] := 1            ' Go to Exit1
          
        TCK_Pulse
    
  • yeah I was just winging the J value.

    my thought was every 32 bit I need a new long. but then I got confused with you starting at 1 not 0. And then things got out of plan.

    I guess you are right with >>5

    So yeah - that's what I MEANT to say...

    but basically if you provide a address of an input array of size n and the address of an output array of the same size it should do it.

    In fact, if you do not need the data in the send buffer again, you could even use the same array, and it will replace bit by bit the 'to send' bit with the 'received' bit

    Enjoy!

    Mike
  • msrobots wrote: »
    yeah I was just winging the J value.

    my thought was every 32 bit I need a new long. but then I got confused with you starting at 1 not 0. And then things got out of plan.

    I guess you are right with >>5

    So yeah - that's what I MEANT to say...

    but basically if you provide a address of an input array of size n and the address of an output array of the same size it should do it.

    In fact, if you do not need the data in the send buffer again, you could even use the same array, and it will replace bit by bit the 'to send' bit with the 'received' bit

    Enjoy!

    Mike

    It was a lot closer than where I was going. I do need the data in the send buffer again because once the TDI is sent, the receive is optionally compared against TDO (with a mask). I know I'm just overthinking things and trying to make it harder than it is. I haven't tested this method yet but it does look like it should work. I ran the edge cases through a calc and got the expected results. Basic sanity checks. I'm not sure how much overhead precomputing n saves.

    I can't remember what else I was looking at but there was something that used bidirectional spi. Hopefully when I run across it again this will work for that too!
  • Yeah, the protocol allows bidirectional, but mostly it is used half duplex and you send zero bits out to receive the answer.

    Mike
Sign In or Register to comment.