Shop OBEX P1 Docs P2 Docs Learn Events
Ideas needed - fast ADC sampling — Parallax Forums

Ideas needed - fast ADC sampling

Andrey DemenevAndrey Demenev Posts: 377
edited 2010-04-03 01:42 in Propeller 1
I have two 10-bit ADCs connected to propeller. There is a ring buffer in HUB memory. Imagine that we are given time interval Ts = 96 clocks. Within this interval, I need to find out what was maximum and minimum ADC output, for each of two ADCs separately, and store them in the buffer, then advance the buffer pointer, and start finding min and max again. The process must be interrupted when certain pin changes from 0 to 1. I need the samples (used to find min and max) to be taken as often as possible. I have 4 cogs for this. My goal is taking the samples each 16 clocks, but I am out of ideas how to achieve this ... And there is one thing that makes things even worse - ADC pins are arranged in a way that they are most easily connected to Prop in reverse order. First ADC's bits 0-9 are connected to Prop pins 9-0, second ADC's bits 0-9 are connected to Prop pins 20-11 (pin 10 is ADC clock). Ts of 96 cycles is given as an example, the smaller minimum possible Ts - the better. I can rearrange the pins to be in direct order, but that is not desirable from PCB routing POV.



Any ideas?
2626 x 169 - 7K

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2010-04-01 05:19
    You can reverse the order of bits in one instruction (4 system clocks) with the REV instruction.

    You ought to be able to use the cog counters to generate the ADC clock

    There are instructions to determine MAX and MIN in one instruction (each).

    Do you mean Ts = 96 system clock cycles? That's about one sample every 1.2 us

    You really have to give more detail on the timing and what you want to accomplish.
  • Timothy D. SwieterTimothy D. Swieter Posts: 1,613
    edited 2010-04-01 05:22
    Andrey - I haven't done any work on reading ADC and speed optimizing like you are discussing, however I find the topic interesting and will throw out a couple ideas.
    • You can probably have one cog dedicate to each ADC for reading and recording samples.
    • If it is the same object for the ADC, then you may want to code the object in such a way that the pins can be defined easily.
    • Of course to get identical code performance with minimal overhead both ADC should be connected in the same manner.
    • You didn't mention the ADCs you are using but it sounds like they are parallel to the Prop, right?
    • I am not sure what is required to take an ADC reading, but you can possible interleave two cogs on each ADC to get more samples. While one cog is writing to hub ram the other one is taking note of the reading from the ADC.
    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Timothy D. Swieter, E.I.
    www.brilldea.com - Prop Blade, LED Painter, RGB LEDs, 3.0" 16:9 LCD Composite video display, eProto for SunSPOT, PropNET, PolkaDOT-51
    www.tdswieter.com
  • Andrey DemenevAndrey Demenev Posts: 377
    edited 2010-04-01 10:52
    @Mike :System clock frequency is not important. All time is measured in system clock cycles. In my case 96 cycles = 1 us, but that actually does not matter. Your notes about REV, MIN/MAX and counters are obvious.

    The task is to examine waveforms (kinda oscilloscope), and the problem I am trying to solve is usualy referred as "aliasing". Attachment illustrates the problem and possible improvement to sampling procedure.
    1010 x 500 - 45K
  • Dave HeinDave Hein Posts: 6,347
    edited 2010-04-01 13:11
    So you want to store a min and max for every 16 samples, which reduces the buffer size by a factor of 8.· I'm assuming your writing that part in assembly, and the min and max are stored in hub memory.· I don't understand what the question is?

    Edit:· I re-read your first post, and I assume the problem is that the assembly takes too many cycles in one cog.· You should be able to use multiple cogs to do this.· The first cog reads the first 16 samples, the second cog reads the next 16 samples, and so on.· After reading 16 sample, a cog will then do the bit reversals and min and max computations.· It then stores the results in hub memory, and then starts reading another 16 samples.· The multiple cogs have to coordinate so they work on the proper samples.

    Edit 2: I'm guessing you can do this with two cogs.· You could use a lock to synchronize them.

    Post Edited (Dave Hein) : 4/1/2010 1:53:53 PM GMT
  • kwinnkwinn Posts: 8,697
    edited 2010-04-01 13:13
    If you are only interested in the min/max values you could use a dual comparator as a peak detector to trigger the acquisition at the min and max points on the waveform.

    An alternative would be to trigger multiple acquisitions at a threshold value (like scope triggering) so the readings occur at the same point in each cycle.

    In both cases it would probably be necessary to store some timing information with the adc data.
  • pgbpsupgbpsu Posts: 460
    edited 2010-04-01 15:22
    Andrey-

    I'm with the other posters that a bit more info would help.

    If the time between consecutive samples from one ADC is 96 clocks, that's 24 ASM instructions (as long as they are not hub instructions or wait or jmp). If you are able to read the 10bit sample (on 10 different pins I assume) in one instruction then reverse it as per Mike's suggestion it seems you should have time to put it into HUB memory and (maybe) compare it to the current Max and Min. If there isn't time to do this part, it seems like you have a free cog to do that.

    1 cog for ADC 1
    1 cog for ADC 2
    1 cog for keeping track of Max/Min

    Do you need to keep track of the max and min for each ADC channel or just the combined max/min.

    Do you have a (commented) first draft of the code for us to look at?

    Peter

    After reading your post it's clear that you need to keep track of the max and min for each ADC. If a value is not a new max or min do you need to keep it?

    Post Edited (pgbpsu) : 4/1/2010 3:40:24 PM GMT
  • Andrey DemenevAndrey Demenev Posts: 377
    edited 2010-04-01 15:32
    Another try [noparse]:)[/noparse] I have an arbitrary waveform at ADC input. Within given time interval Ts, I need to take as many ADC samples as possible, calculate minimum and maximum, and store them in HUB RAM buffer. After each Ts, memory pointer is advanced, and if the buffer is full, move the pointer to beginning of the buffer (ring buffer). Min and max must be reset after each memory write. Moments when ADC samples are read must be equally spaced on time axis . Min and max must be calculated separately for each ADC channel

    Ts of 96 cycles is given just an example - in the following code you can see that it depends on num_cycles variable

    This is the best draft I have so far:

    
    :loop               MOV     :sample, INA                        ' 75
                        REV     :sample, #32-21                     ' 79
                        SHR     :sample, #11                        ' 83
                        MIN     :minvalue, :sample                  ' 87
                        MAX     :maxvalue, :sample                  ' 91
                        TEST    :stopmask, INA              WC      ' 95
            IF_C        JMP     #:stop                              ' 99
                        SUB     :num_cycles, #1             WZ      ' 103
                                                                    ' 
                        MOV     :sample, INA                        ' 107
                        REV     :sample, #32-21                     ' 111
                        SHR     :sample, #11                        ' 115
                        MIN     :minvalue, :sample                  ' 119
                        MAX     :maxvalue, :sample                  ' 123
                        MOV     :minmax, :maxvalue                  ' 127
                        SHL     :minmax, #10                        ' 131
                        OR      :minmax, :minvalue                  ' 135
                                                                    ' 
                        MOV     :sample, INA                        ' 139
                        REV     :sample, #32-21                     ' 143
                        SHR     :sample, #11                        ' 147
                        MIN     :minvalue, :sample                  ' 151
                        MAX     :maxvalue, :sample                  ' 155
            IF_Z        ADD     :address, #8                        ' 159
                        WRLONG  :address, :minmax                   ' 7 / 167
                                                                    ' 
                        MOV     :sample, INA                        ' 11
            IF_Z        MOV     :minvalue, :ten_ones                ' 15
            IF_Z        MOV     :maxvalue, #0                       ' 19
                        REV     :sample, #32-21                     ' 23
                        SHR     :sample, #11                        ' 27
                        MIN     :minvalue, :sample                  ' 31
                        MAX     :maxvalue, :sample                  ' 35
            IF_Z        SUB     :counter, #1                WZ      ' 39
                                                                    '
                        MOV     :sample, INA                        ' 43
                        REV     :sample, #32-21                     ' 47
                        SHR     :sample, #11                        ' 51
                        MIN     :minvalue, :sample                  ' 55
                        MAX     :maxvalue, :sample                  ' 59
            IF_Z        MOV     :counter, :counter_reload           ' 63
            IF_Z        MOV     :address, :start_address            ' 67
                        JMP     #:loop                              ' 71
    
    
    
    



    Samples to calculate MIN/MAX are taken each 32 cycles. MIN/MAX is only calculated for one ADC. MIN/MAX is written to memory each (num_cycles * 160) cycles. When running this on 2 interleaved cogs, it would get samples each 16 cycles, and write to memory each Ts = (num_cycles * 80) cycles. The code for secong COG should be modified a bit, so samples are taken with 16 clock cycles delay relative to first COG (basically, that is moving WRLONG to a different place). And another 2 cogs would do the same for another ADC

    The code has a problem though: last sample within each Ts interval is lost (the one labelled 139)


    The main question is: is it possible to take samples at least as often as each 16 cycles? Remember they must be equally spaced
  • Andrey DemenevAndrey Demenev Posts: 377
    edited 2010-04-01 15:35
    Aha, it has another problem - num_cycles is not reloaded
  • pgbpsupgbpsu Posts: 460
    edited 2010-04-01 15:56
    Be careful using MIN and MAX. They don't do what I expected them to. They limit value1 to value2. You may want to look at MINS and MAXS.

    I promise I'm not trying to be boneheaded about this but I'm still not following. For the moment let's leave out the second ADC channel. Do you want:
    1. to read the input from and ADC as fast as possible and only pass up to HUB new MAX/MIN values
    OR
    2. to read a certain number of samples from the ADC (say 16- this is determined by Ts) and pass up the max and min of those 16, then reset the max/min within the ADC cog and read another 16 evenly spaced samples? I assume that the delta T between last sample in first group and first sample in second group needs to match the delta between samples within a group. Am I starting to get the right picture?

    It sounds like you want to do 2.
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2010-04-01 16:08
    Andrey Demenev,

    Is the MIN/MAX a successive approximation attempt to fine tune the result? Just trying to see the picture here.

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

    IC Layout Engineer
    Parallax, Inc.
  • BeanBean Posts: 8,129
    edited 2010-04-01 16:20
    I think what he is trying to do is this:

    ADC sample rate is let's say 1uSec.

    Because of memory limitations, he stores every 16th sample as a display point. (16uSec rate)

    Because the display points could get aliased, he stores the highest and lowest ADC sample for each display point. (2 values for each display point).

    When he draws the display, instead of a single point he will draw a vertical line to show the lowest and highest sampled value for that display point (which consists of 16 ADC sample points).

    He then uses only 1/8 the memory required had he stored ALL the ADC samples.

    That is my best guess anyway. Neat idea.

    Bean

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Use BASIC on the Propeller with the speed of assembly language.
    PropBASIC thread http://forums.parallax.com/showthread.php?p=867134

    March 2010 Nuts and Volts article·http://www.parallax.com/Portals/0/Downloads/docs/cols/nv/prop/col/nvp5.pdf
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    There are two rules in life:
    · 1) Never divulge all information
  • pgbpsupgbpsu Posts: 460
    edited 2010-04-01 16:40
    Andrey-

    To keep all your sampling even, I think you'll need to use a waitcnt or two. Maybe something like the following:

    :loop                                                              ' repeat over this loop endlessly
           mov      loopCnt, numSamps                      ' setup number of samples to read in this group
    
    :readNsamps
           waitcnt  nextLoop, cntsBtwSamps                ' use this to start the two ADC cogs either together or interleaved
                                                                          ' also allows you to keep dT between samples and loops even
           MOV     :sample, INA                                 ' read in a sample 
           REV      :sample, #32-21                            ' reverse sample
           SHR     :sample, #11                                 ' shift it into place
           MIN     :minvalue, :sample                 wc    ' check to see if current sample is new min
    if_c   wrlong :minvalue, minValuePtr                   ' we have a new min so write it to HUB
    if_c   add     :minValuePtr, #4                            ' increment pointer to HUB
           MAX    :maxvalue, :sample                wc    ' check to see if current sample is new max
    if_c  wrlong :maxvalue: maxValuePtr                  ' we have a new max so write it to HUB
    if_c  add     :maxValuePtr, #4                            ' increment HUB pointer
    
          djnz    loopCnt, #:readNsamps                   ' we've read all the samples in this group; now zero out max and min
          mov    :minvalue, #0                                 ' zero out min
          mov    :maxvalue, #0                                ' zero out max
    
          jmp     #:loop                                             ' jump back and do it again
    
    



    This is a start. It doesn't deal with wrap around on your ring buffer and you'll have to do some math to figure out what cntsBtwSamps needs to be but this is how I'd start. I suppose you could keep maxValPtr and minValPtr updated by another cog. Then you'd have to insert a rdlong for each of those into this loop. If not, you'll need to do the addition here and wrap around. I think 3 more instructions would do it.

         cmp  :maxValuePtr, endOfBufferAddress  wz
    if_z  mov  :maxValuePtr, maxValuePtrStart
    if_z  mov  :minValuePtr, minValuePtrStart
    
    



    Is this even close?
  • pgbpsupgbpsu Posts: 460
    edited 2010-04-01 16:44
    Bean seems to be onto something.

    Keep in mind that, unless you keep track of when your MAX and MIN samples arrived, you'll lose timing info. To put it another way, the spacing on the time axis no longer has to be even. You may capture amplitude information but at the expense of time. That's certainly a trade off worth making in some situations.

    p
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2010-04-01 19:09
    Andrey Demenev,

    What is the datasheet on the 10-bit ADCs that you are using? I was thinking something along a pipeline architecture, but each stage would be a COG staggered in time.

    The other idea with MIN/MAX would involve more intimate communication with the ADC in a successive approximation mode. There are about 3 different successive approximations architectures that I know of. linear ramping, binary ramping, and predictive ramping the latter heavily relying on MIN MAX values to determine the next 'pivot' point for a comparator.

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

    IC Layout Engineer
    Parallax, Inc.
  • Dave HeinDave Hein Posts: 6,347
    edited 2010-04-01 19:34
    I misunderstood your original description.· I thought the decimation factor was 16 to 1, but it is actually 6 to 1 (96 cycles per group, versus 16 cycles per sample).· The operations for each pair of samples is as follows:

    ··· mov samplea, ina
    ··· mov sampleb, samplea
    ··· rev samplea, #32-10
    ··· shr sampleb, #11
    ··· rev sampleb, #32-10
    ··· min maxvala, samplea
    ··· max minvala, samplea
    ··· min maxvalb, sampleb
    ··· max minvalb, sampleb

    This is 36 cycles per samples, so you will need 3 cogs to achieve 16 cycles per sample.· There might be a way to squeeze this into two cogs, but I think the loop overhead and hub acceses will push you into 3 cogs.

    You could interleave the three cogs on a sample-by-sample basis, which means that each cog would be 16 cyles behind the previous cog.· However, I think it would be easier if you interleaved them in groups of 6 samples.

    A cog would grab 6 pairs of samples and perform part of the processing while acquiring samples, and then perform the rest of the processing after getting all 6 samples.· The next cog would start acquiring samples while the previous cog is working on the previous 6 samples.· The third cog would follow the second.· It would look something like the following:

    loop

    '·· Acquire and partially process 6 pairs of samples
    ··· mov samp01a, ina
    ··· mov samp01b, samp01a
    ··· rev samp01a, #32-10
    ··· shr samp01b, #11

    ··· mov samp02a, ina
    ··· mov samp02b, samp01a
    ··· rev samp02a, #32-10
    ··· shr samp02b, #11

    ··· mov samp03a, ina
    ··· mov samp03b, samp01a
    ··· rev samp03a, #32-10
    ··· shr samp03b, #11

    ··· mov samp04a, ina
    ··· mov samp04b, samp01a
    ··· rev samp04a, #32-10
    ··· shr samp04b, #11

    ··· mov samp05a, ina
    ··· mov samp05b, samp01a
    ··· rev samp05a, #32-10
    ··· shr samp05b, #11

    ··· mov samp06a, ina
    ··· mov samp06b, samp01a
    ··· rev samp06a, #32-10
    ··· shr samp06b, #11

    '·· Complete processing of 6 pairs of samples
    ··· rev samp01b, #32-10
    ··· min maxvala, samp01a
    ··· max minvala, samp01a
    ··· min maxvalb, samp01b
    ··· max minvalb, samp01b

    ··· rev samp02b, #32-10
    ··· min maxvala, samp02a
    ··· max minvala, samp02a
    ··· min maxvalb, samp02b
    ··· max minvalb, samp02b

    ··· rev samp03b, #32-10
    ··· min maxvala, samp03a
    ··· max minvala, samp03a
    ··· min maxvalb, samp03b
    ··· max minvalb, samp03b

    ··· rev samp04b, #32-10
    ··· min maxvala, samp04a
    ··· max minvala, samp04a
    ··· min maxvalb, samp04b
    ··· max minvalb, samp04b

    ··· rev samp05b, #32-10
    ··· min maxvala, samp05a
    ··· max minvala, samp05a
    ··· min maxvalb, samp05b
    ··· max minvalb, samp05b

    ··· rev samp06b, #32-10
    ··· min maxvala, samp06a
    ··· max minvala, samp06a
    ··· min maxvalb, samp06b
    ··· max minvalb, samp06b

    .·· Write the min and max to the output buffer
    ··· shl minmaxa, maxvala
    ··· or· minmaxa, minvala
    ··· wrlong minmaxa, bufptra
    ··· add bufptra, #4

    ··· shl minmaxb, maxvalb
    ··· or· minmaxb, minvalb
    ··· wrlong minmaxb, bufptrb
    ··· add bufptrb

    ··· jmp #loop

    I didn't include the code that syncs to the ADC or the other two cogs.· There are various ways to handle that.

    Dave
  • Andrey DemenevAndrey Demenev Posts: 377
    edited 2010-04-02 00:12
    Beau Schwabe said...
    Is the MIN/MAX a successive approximation attempt to fine tune the result? Just trying to see the picture here.
    This is not approximation. This is exactly what I wrote - an attempt to solve the aliasing problem.

    If my description is not very clear, read Bean's post - he describes the basic idea pretty well.

    I'll try to re-phrase. We have 2 time intervals: sampling interval t and storing interval T. Each t, we take ADC sample. Each T we calculate min and max of samples aquired during this T, and store them into ring buffer. T/t is not constant, it should be configurable - see num_cycles in my example code. The smaller t - the better. The smaller minimum possible T/t - the better. "Sample as often as possible, store as often as buffer space permits"
  • Andrey DemenevAndrey Demenev Posts: 377
    edited 2010-04-02 00:58
    Dave Hein: your last code could probably work, and I did think about something similar. It has a problem though - fixed T/t. And no overflow check. And no Stop condition chek. Adding these will increase T/t up to 7, if not 8 - and it still will be fixed
  • Andrey DemenevAndrey Demenev Posts: 377
    edited 2010-04-02 12:26
    I think the following will do what I need:

    :take_sample        
    
                        MOV     :sampleA1, INA                  '  
                        REV     :sampleA1, #32 - 21             '
                        MOV     :sampleB1, :sampleA1            '
                        AND     :sampleB1, :con_0x03FF          ' 16
                        
                        MOV     :sampleA2, INA                  '  
                        REV     :sampleA2, #32 - 21             '
                        MOV     :sampleB2, :sampleA2            '
                        AND     :sampleB2, :con_0x03FF          ' 16
                        
                        MOV     :sampleA3, INA                  '  
                        REV     :sampleA3, #32 - 21             '
                        MOV     :sampleB3, :sampleA3            '
                        AND     :sampleB3, :con_0x03FF          ' 16
                        
                        MOV     :sampleA4, INA                  '  
                        REV     :sampleA4, #32 - 21             '
                        MOV     :sampleB4, :sampleA4            '
                        AND     :sampleB4, :con_0x03FF          ' 16
                                                                ' 
                                                                ' 
                        MAX     :maxvalueA, :sampleA1           '
                        MIN     :minvalueA, :sampleA1           '
                        MAX     :maxvalueB, :sampleB1           '
                        MIN     :minvalueB, :sampleB1           ' 16
    
                        MAX     :maxvalueA, :sampleA2           '
                        MIN     :minvalueA, :sampleA2           '
                        MAX     :maxvalueB, :sampleB2           '
                        MIN     :minvalueB, :sampleB2           ' 16
    
                        MAX     :maxvalueA, :sampleA3           '
                        MIN     :minvalueA, :sampleA3           '
                        MAX     :maxvalueB, :sampleB3           '
                        MIN     :minvalueB, :sampleB3           ' 16
    
                        MAX     :maxvalueA, :sampleA4           '
                        MIN     :minvalueA, :sampleA4           '
                        MAX     :maxvalueB, :sampleB4           '
                        MIN     :minvalueB, :sampleB4           ' 16
    
                        SHL     :maxvalueB, #10                 ' 136
                        OR      :maxvalueB, :minvalueB          ' 140
                        AND     :maxvalueA, :con_0x1FF100       ' 144
                        SHR     :minvalueA, #10                 ' 148
                        OR      :maxvalueA, :minvalueA          ' 152
                        SHR     :maxvalueA, #1                  ' 156
                        TEST    :stopmask, INA              WC  ' 160
                        SUB     :num_cycles, #1             WZ  ' 164
                        WRLONG  :maxvalueA, :addressA           ' 171 - 186
            IF_C        JMP     :stop                           ' 175 - 190
            IF_Z        ADD     :addressA,  #32                 ' 179 - 194
                        WRLONG  :maxvalueB, :addressB           ' 183 - 198
            IF_Z        ADD     :addressB, #32                  ' 187 - 202
            IF_Z        CMP     :addressA, :max_address     WZ  ' 191 - 206
            IF_Z        MOV     :addressA, :start_addressA      ' 195 - 210
            IF_Z        MOV     :addressB, :start_addressB  WZ  ' 199 - 214
            IF_Z        MOV     :num_cycles, :num_cycles_reload ' 203 - 218
            IF_Z        MOV     :minvalueA, :con_0x03FF         ' 207 - 222
            IF_Z        MOV     :maxvalueA, #0                  ' 211 - 226
            IF_Z        MOV     :minvalueB, :con_0x1FF1FF       ' 215 - 230
            IF_Z        MOV     :maxvalueB, #0                  ' 219 - 234
                        WAITCNT :next_loop, #256                ' 0
                        JMP     #:take_sample                   ' 4
    
    
    
    



    I do not clear lower bits when finding min/max for ADC bits that are in higher bits - since this does not change final result. This saves few instructions though. Complete loop is 234 clock cycles max, and WAITCNT extends it to 256 clocks. When run on 4 COGs, this gives samples spaced exactly 16 clock cycles from each other, and memory write each (64*num_cycles_reload) clock cycles

    Any comments? Would this work? Or I am missing something?

    Post Edited (Andrey Demenev) : 4/2/2010 12:40:30 PM GMT
  • Dave HeinDave Hein Posts: 6,347
    edited 2010-04-02 13:31
    I think you have the min and max backwards.· If I read the manual correctly the min instruction limits the minimum value to the max of the two arguments.· This definition is confusing, and I think Parallax should have named them the other way around, or used names like·limitlow and limithigh instead of min and max.

    I'm confused about the definitions of T and t.· I assume they are the number of cycles between min/max values and the number of cycles between raw samples.· In your original post these were 96 and 16, which gives a ratio of T/t = 6.· In your latest code you show a T/t = 4.· You said you wanted this to be variable, but your latest code is fixed.· The only way to make it variable is to use self-modifying code.· You could use a base code image that handles the largest T/t you would use, and then overwrite it with jumps to reduce it to a smaller T/t.

    Your code doesn't synchronize multiple cogs, or accurately synchronize to the ADC.· You should be able to use I/O pins and waitpeq/waitpne to do the synchronization.· Can you provide information on the ADC your using?· It would help to determine how you should synchronize to it.

    Dave

    Post Edited (Dave Hein) : 4/2/2010 1:38:35 PM GMT
  • Andrey DemenevAndrey Demenev Posts: 377
    edited 2010-04-02 13:51
    Dave, thanks for your note about MIN/MAX instructions - I indeed believed that they do what the names say [noparse]:)[/noparse]

    T/t is variable. Note this:
    SUB     :num_cycles, #1             WZ
    



    and then lots of IF_Z - these do the trick. Although the min/max are written to hub memory, the buffer pointers are not advanced if --num_cycles != 0. After it becomes zero, pointers are advanced, min/max reset, and num_cycles reloaded.

    This is just the part of code doint the actual sampling. Before the sampling starts, I will start counter in one of 4 COGs to generate the clock. Synchronization is not a problem - that is just a matter of entering the loop at right time. All 4 COGs will be waiting for 5th COG to write 1 co certain memory location, then waitcnt with "personal" delay for each COG will do the trick.

    I was hoping someone can have an idea how to make this to sample each 12 clock cycles, keeping minimum T/t reasonable (6 at most), but looks like it is not possible ...
  • Andrey DemenevAndrey Demenev Posts: 377
    edited 2010-04-02 13:55
    Just noticed - these should be moved to the very end of cycle, immediately before WAITCNT
            IF_Z        CMP        :addressA, :max_address        WZ
            IF_Z        MOV        :addressA, :start_addressA
            IF_Z        MOV        :addressB, :start_addressB
    
    
  • lonesocklonesock Posts: 917
    edited 2010-04-02 16:02
    Untested, but you could record a contiguous block of values in a cog, then process them while the next cog handles the next block. There would be some latency.

    PUB dummy
    
    DAT
    ORG 0
    
    stream_min_max_entry
                  ' get all the necessary values
                  ' set up ludicrous limits
                  neg       Alo, #1
                  neg       Blo, #1
                  mov       Ahi, #0
                  mov       Bhi, #0
    stream_run
                  ' set up for the recording phase              
                  movd      :read_value, #record_buffer
                  mov       count, num_records
                  waitcnt   timestamp, dt_clocks
    :read_value
                  mov       0-0, ina                ' get the value into our buffer
                  add       :read_value, incDest    ' move pointer to the next long
                  nop                               ' waste time, so 16 cycles per read instead of 12
                  djnz      count, #:read_value     ' go get more data
    
                  ' done recording, now process              
                  movi      [img]http://forums.parallax.com/images/smilies/tongue.gif[/img]rocess_value, #record_buffer
                  mov       count, num_records   
    [img]http://forums.parallax.com/images/smilies/tongue.gif[/img]rocess_value
                  mov       tmp, 0-0
                  add       [img]http://forums.parallax.com/images/smilies/tongue.gif[/img]rocess_value, #1
                  rev       tmp, #31 - 20
                  max       Alo, tmp
                  min       Ahi, tmp
                  and       tmp, B_mask
                  max       Blo, tmp
                  min       Bhi, tmp
                  djnz      count, #[img]http://forums.parallax.com/images/smilies/tongue.gif[/img]rocess_value
    
                  ' get the A values back into range
                  shr       Alo, #11
                  shr       Ahi, #11
    
                  ' record the values (and reset the limits...we have time due to Hub access anyway)
                  wrword    Alo, Alo_ptr
                  add       Alo_ptr, #2
                  neg       Alo, #1
                  
                  wrword    Ahi, Ahi_ptr
                  add       Ahi_ptr, #2
                  mov       Ahi, #0
                  
                  wrword    Blo, Blo_ptr
                  add       Blo_ptr, #2
                  neg       Blo, #1
                  
                  wrword    Bhi, Bhi_ptr
                  add       Bhi_ptr, #2
                  mov       Bhi, #0  
    
                  ' start over
                  jmp       #stream_run
                              
    
    
    {===== PASM parameters =====}
    num_records             long    16
    dt_clocks               long    12*16
    A_mask                  long    %1111111111 << 11
    B_mask                  long    %1111111111 << 0
    Alo_ptr                 long    0
    Ahi_ptr                 long    0
    Blo_ptr                 long    0
    Bhi_ptr                 long    0
    
    {===== PASM constants > 511 =====}
    incDest                 long    512
        
    {===== PARM scratch variables =====}
    Ahi                     res     1
    Alo                     res     1
    Bhi                     res     1
    Blo                     res     1
    tmp                     res     1
    timestamp               res     1
    count                   res     1
    record_buffer           res     128
    


    With only 4 cogs, I believe you'd have to do something like >= 10 samples in a block, to keep under your time budget. If you can cut down on the processing time per sample, that would lower the minimum number of samples you'd have to run in a block.

    Note the NOP in the recording loop...this is a bit counter-intuitive, so maybe I messed it up...but, I think if you sample every 12 clocks instead of 16, then you need to target an average of 3 instructions per sample, which would require > 4 cogs to do.

    Does that make sense, or am I missing something obvious?

    Jonathan

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    lonesock
    Piranha are people too.
  • Andrey DemenevAndrey Demenev Posts: 377
    edited 2010-04-03 01:42
    lonesock, basically, my latest example code would work in the same manner, but with unrolled loops. Both would do the job, both have t=16clock cycles. There are 2 differences:

    1. my code has lower minimum T/t of 4, while yours - 9 (and possibly 10, when ring buffer boundary and stop condition checks are added)
    2. your code allows more high-grained control over T/t - in steps of 1, while mine - in steps of 4
Sign In or Register to comment.