Shop OBEX P1 Docs P2 Docs Learn Events
Basic Question about cog memory — Parallax Forums

Basic Question about cog memory

Mad AMad A Posts: 17
edited 2012-07-31 09:30 in Propeller 1
So I have successfully generated a sin wave, output on 8 pins and sent to a DAC. I am trying to cycle through the wave once and store the values to COG memory so I can then spit them out faster because I won't be reading from hub memory every time. I cannot get this to work and would appreciate any direction/corrections.


CON
  _clkmode        = xtal1 + pll16x
  _xinfreq        = 5_000_000


VAR
long index
word angles[360]




PUB Get_samples
 
 repeat index from 0 to 360
   angles[index] := index*8192/360


 cognew(@begin,@angles[0])




DAT


begin   mov address, par
        mov dira,#$FF
:loop   rdword sin, address
        add address, #2
        call #getsin
        mov time,cnt
        add time,delay
        waitcnt time,delay
        sar sin, #9
        add sin, #$80 
        mov cogmem, sin  'write sin cog memory rather than output pins
        'mov outa,cogmem
        add cogmem, #2    'increment memory location 
        djnz ctr,#:loop'decrement counter and restart loop
        'mov ctr,#360     'Initialize counter.
        'mov address,par
        'jmp #:loop  


        
        mov :rdmem,start
        mov ctr,#360
:rdmem  mov cogmem,0-0
        mov outa,cogmem
        add cogmem, #2
        djnz ctr,#:rdmem
        mov :rdmem,start
        mov ctr,#360
        jmp #:rdmem  
      


getsin  test sin,sin_90 wc       'get quadrant 2|4 into c  
        test sin,sin_180 wz      'get quadrant 3|4 into nz
        negc sin,sin             'if quadrant 2|4, negate offset
        or sin,sin_table         'or in sin table address >> 1
        shl sin,#1               'shift left to get final word address
        rdword sin,sin           'read word sample from $E000 to $F000
        negnz sin,sin            'if quadrant 3|4, negate sample
getsin_ret      ret             ' (this subroutine adapted from Prop manual)


sin_90        long      $0800
sin_180       long      $1000
sin_table     long      $E000 >>1
sin           long 0 
ptr           long 0
ctr           long 360
cntstart      long 360
address       long 0
cogmem        long $03C0
start         long $03C0                       
delay         long 9
time          res 1


Comments

  • David BetzDavid Betz Posts: 14,516
    edited 2012-07-26 19:12
    One problem is that you need to use movs instead of just mov to set the source address at your :rdmem label.
  • pogerttpogertt Posts: 33
    edited 2012-07-26 19:19
    Could you generate your array of ANGLES in Main Ram as you are doing, then sequentially read them to COG Ram as part of the initalizing of the cog?
    Once read to COG Ram, no need for HUB calls.

    It looks like you are only using 29 of the 492 memory locations available in cog ram, so the 360 values should fit.
  • Mad AMad A Posts: 17
    edited 2012-07-27 11:16
    I switched the mov to movs, but no luck. I get a sawtooth on the scope like it's reading the locations not the contents. As for reading them to RAM as part of initializing the cog, how would I do that? Thanks both of you!
  • ersmithersmith Posts: 6,096
    edited 2012-07-27 13:44
    This code:
            mov cogmem, sin  'write sin cog memory rather than output pins
            'mov outa,cogmem
            add cogmem, #2    'increment memory location 
            djnz ctr,#:loop'decrement counter and restart loop
    
    does not do what the comments indicate. "cogmem" is just a single cog memory location. So while "mov cogmem, sin" will indeed move the sin value into cogmem, the subsequent "add cogmem, #2" will just corrupt the value you just put into cogmem by adding two to it.

    Also note that COG memory is always addressed in longs, not bytes.

    So the code above should look roughly like (note that I give no guarantees, just giving a flavor!):
            movd cogmem, :wrsin   ' set up the indirection
            add  cogmem,#1         ' go to next longword in cog memory (and wait a cycle so code at :wrsin will be fetched with proper modification)
    :wrsin  mov  0-0, sin  'write sin cog memory rather than output pins
            djnz ctr,#:loop'decrement counter and restart loop
    

    There's a similar problem in the loop:
             mov :rdmem,start
             mov ctr,#360
     :rdmem  mov cogmem,0-0
             mov outa,cogmem
             add cogmem, #2
             djnz ctr,#:rdmem
    

    I gather your intention there is to copy the 360 sin values starting at "start" into outa. For that you would have to do something like:
             mov cogmem,#start     ' initialize COG memory pointer to start of 360 long array
             mov ctr,#360
    :loop  movs :rdmem,cogmem ' change the source of the instruction at :rdmem from 0-0 to whatever is stored in cogmem
             add cogmem,#1           ' there must be one instruction between when we modify :rdmem and :rdmem actually executes
    :rdmem  mov outa,0-0
             djnz ctr,#:loop
    
    You can optimize this a bit by actually incrementing the instruction at :rdmem directly, since the source is in the low bits of the instruction, but that's an advanced topic :-).

    Finally note that you will need to reserve 360 long words, not 720 bytes, for the array, since in COG memory the smallest addressable unit is a long word (4 bytes). You could get fancy and do two samples at once by shifting and masking, but that's a bit more work.

    Eric
  • Mad AMad A Posts: 17
    edited 2012-07-27 17:25
    ersmith,

    I tried your code for the first loop and I no longer get a sinusoid on the scope when I uncomment mov outa,cogmem and the instructions at the end to keep the loop going. Rather I get a sawtooth wave much like the lower loop gives me. Any ideas?
  • AribaAriba Posts: 2,690
    edited 2012-07-27 17:56
    movd cogmem, :wrsin   ' set up the indirection
    
    should be:
    movd :wrsin, cogmem   ' set up the indirection
    

    Andy
  • Mad AMad A Posts: 17
    edited 2012-07-27 18:03
    hmm, with Andy's change I still get a sawtooth, but it looks more digitized. Also there is a long delay before it shows up on the oscilloscope which is very very strange.
  • kuronekokuroneko Posts: 3,623
    edited 2012-07-28 00:31
    @Mad A: See if this gives you a better understanding. It's deliberately not a modified version of your code. It
    • prepares a hub array in SPIN
    • starts a PASM cog passing the hub array address as a parameter
    • reads the hub array into a cog (long) array (size limit!), processing each element before storing it
    • repeatedly sends the array elements to I/O pins
    CON
      _clkmode = XTAL1|PLL16X
      _xinfreq = 5_000_000
    
    CON
      wcnt = 256
      
    VAR
      word data[wcnt]
    
    PUB selftest : index
     
      repeat constant(wcnt/2)
        data[index++] := data[wcnt - 1 - index] := index
    
      cognew(@begin, @data{0})
    
    DAT             org     0
    
    begin           mov     addr, par               ' grab r/w copy of par
                    mov     dira, mask              ' drive outputs
    
                    movd    :keep, #cogmem          ' initialise start of local array ([COLOR="blue"]0-0[/COLOR])
                    mov     ecnt, #wcnt             ' adapt if > 511
    
    :loop           rdword  temp, addr              ' load array element
                    add     addr, #2                ' advance hub address
                    call    #process                ' process array element (temp - temp)
    
    :keep           mov     [COLOR="blue"]0-0[/COLOR], temp               ' cache processed value
                    add     $-1, dst1               ' advance move destination
                    djnz    ecnt, #:loop            ' for all elements
    
    ' All values have been processed. Now send them to the LED bar.
    
    endless         movs    :loop, #cogmem          ' start with first element ([COLOR="orange"]0-0[/COLOR])
                    mov     ecnt, #wcnt             ' adapt if > 511
                    
    :loop           mov     outa, [COLOR="orange"]0-0[/COLOR]               ' send byte
                    add     $-1, #1                 ' advance move source
                    djnz    ecnt, #:loop            ' for all elements
    
                    jmp     #endless                ' nothing else to do ...
                    
    ' The value is stored in the LSB of the data word. For now simply
    ' shift it up 16 more bits to become visible on the LED bar (demoboard).
    
    process         shl     temp, #16
    process_ret     ret
    
    ' initialised data and/or presets
    
    mask            long    $00FF0000
    dst1            long    1 << 9                  ' dst +/-= 1
    
    ' uninitialised data and/or temporaries
    
    addr            res     1
    ecnt            res     1
    temp            res     1
    
    cogmem          res     wcnt                    ' local cog array
    
    tail            fit
    
    DAT
    
  • ersmithersmith Posts: 6,096
    edited 2012-07-28 05:07
    As Andy mentioned I mixed up the order of the operands in the "movd" instruction (I warned you that there were no guarantees!). Also, in the modified version of the loop "cogmem" holds a pointer to cog memory, and "sin" holds the actual sin data, so if you want to output the sinusoid you'll have to do "mov OUTA, sin" rather than "mov OUTA, cogmem".

    Eric
  • Mad AMad A Posts: 17
    edited 2012-07-28 10:56
    Hi Eric,

    I did try doing outa, sin and it worked but my goal is to get the sin data into the memory location cogmem so I can access it later (in the second loop). Essentially I want to read a full period into memory and then just spit out samples. So how do I get the DATA into "cogmem" rather than having it just be a pointer? Sorry if I'm being dense, I appreciate the help. I am also about to look at the other post from kuroneko about a different approach.
  • AribaAriba Posts: 2,690
    edited 2012-07-28 12:36
    Mad A wrote: »
    Hi Eric,

    I did try doing outa, sin and it worked but my goal is to get the sin data into the memory location cogmem so I can access it later (in the second loop). Essentially I want to read a full period into memory and then just spit out samples. So how do I get the DATA into "cogmem" rather than having it just be a pointer? Sorry if I'm being dense, I appreciate the help. I am also about to look at the other post from kuroneko about a different approach.

    You can not store 360 values into a single cogmem location. You need to reserve 360 longs for cogmem, eighter with res as kuroneko has shown or with:
    cogmem  long 0[360]
    
    Then these two loops can store and replay the sine values into/from this data-table:
    movd :wrsin, #cogmem   ' set up the indirection
            mov  ctr, #360
    :wrsin  mov  0-0, sin  'write sin cog memory rather than output pins
            add  :wrsin, incrDest
            djnz ctr,#:loop'decrement counter and restart loop
     
    
    sinout  movs :rdmem,#cogmem    'mov startaddress to source field of :rdmem
            mov ctr,#360
    :rdmem  mov outa,0-0           'read indirect
            add :rdmem, #1         'increment source field to next address
            djnz ctr,#:rdmem       'for all sine values
            jmp #sinout            'start again for next periode
    
    
    incrDest  long  1<<9           'constant for increment destination field
    
    
    cogmem  long 0[360]
    
    Andy
  • Mad AMad A Posts: 17
    edited 2012-07-28 17:25
    Thanks Andy. I do know I can't fit 360 values in one location, in my original code I thought that add cogmem, #2 advanced the address. Anyway, I integrated that code into mine and I commented every line. It still doesn't work so I was hoping someone could tell me where I am going wrong in the comments.
    CON
      _clkmode        = xtal1 + pll16x
      _xinfreq        = 5_000_000
    
    
    VAR
    long index
    word angles[360] 'set up a word array to hold angles
    
    
    
    
    PUB Get_samples
     
     repeat index from 0 to 360
       angles[index] := index*8192/360 'fill angle array
    
    
     cognew(@begin,@angles[0])  'launch cog
    DAT
    begin   mov address, par 'point to angles array
            mov dira,#$FF     'set pins 0-7 as outputs
            mov ctr,#360       'initialize counter
    :loop   rdword sin, address  'read angle into sin
            add address, #2      'increment to next array location
            call #getsin          'call getsin, which will return the sin in sin
            mov time,cnt           'delay
            add time,delay            
            waitcnt time,delay
            sar sin, #9          'shift sin so it fits in 8 bits
            add sin, #$80           'offset
            movd :wrsin, #cogmem   ' set up the indirection
            'mov ctr, #360        'I think if this is here it resets on every loop iteration?
            nop                    'time for self modifying code to complete
    :wrsin  mov  0-0, sin  'write sin cog memory rather than output pins
            add  :wrsin, incrDest 'increment memory location
            djnz ctr,#:loop 'decrement counter and restart loop
            
    
    
    sinout  movs :rdmem,#cogmem    'mov start address to source field of :rdmem
            mov ctr,#360
    :rdmem  mov outa,0-0           'read indirect
            add :rdmem, #1         'increment source field to next address
            djnz ctr,#:rdmem      'for all sine values
            jmp #sinout            'start again for next periode
    
    
    getsin  test sin,sin_90 wc       'get quadrant 2|4 into c  
            test sin,sin_180 wz      'get quadrant 3|4 into nz
            negc sin,sin             'if quadrant 2|4, negate offset
            or sin,sin_table         'or in sin table address >> 1
            shl sin,#1               'shift left to get final word address
            rdword sin,sin           'read word sample from $E000 to $F000
            negnz sin,sin            'if quadrant 3|4, negate sample
    getsin_ret      ret
    
    
    sin_90        long      $0800
    sin_180       long      $1000
    sin_table     long      $E000 >>1
    sin           long 0 
    incrDest      long  1<<9           'constant for increment destination field
    delay         long 9
    ctr           long 0
    cogmem        long 0[360]
    address       long 0
    time          res 1
    
  • Mad AMad A Posts: 17
    edited 2012-07-28 17:27
    Also why is incrDest left shifted? (stupid question probably) :blank:
  • kuronekokuroneko Posts: 3,623
    edited 2012-07-28 17:59
    Mad A wrote: »
    Also why is incrDest left shifted? (stupid question probably) :blank:
    The destination field of an instruction lives at insn[17..9]. As we are modifying the instruction itself we have to relocate the +1 by shifting it left. As for post #13, you reset the initial array location everytime you run through the loop. IOW, you should move the movd :wrsin, #cogmem to somewhere before :loop, then you can also get rid of the nop.

    Before I forget, your SPIN loop should stop at 359!
  • Mad AMad A Posts: 17
    edited 2012-07-31 09:30
    this is working now, thanks all! Have 12.5 kHz rather than the 2 kHz I had before!
Sign In or Register to comment.