This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

EDMA for byte order swapping ( endian, sort of)



I need to change the byte order of an audio capture stream.    The McBSP is capturing the data, and the EDMA is sending it to ALSA kernel drivers on a DM365 processor.  Unfortnately, it is sending the wrong 16 bits off ( 4byte i2s stream to  2 byte pcm capture ).  Can I use the EDMA to change the byte order?

 

[brettb@maven audtest]$ ./dwav32.sh  2v1.wav | less

...
0001444 ffe73d00 ffe75d00
0001454 fffeca00 ffff1000
0001464 0016c700 00168100
0001474 002e2500 002e1e00
0001504 00455c00 0044e200
0001514 005c2400 005ba900
0001524 00725300 0071e600
0001534 0087df00 00879b00
0001544 009c5d00 009be900
0001554 00b03100 00af8b00
0001564 00c2a800 00c23700
0001574 00d43300 00d35700


[brettb@maven audtest]$ ./dwav8.sh  2v1.wav | less

...
0001444 00 3d e7 ff 00 5d e7 ff
0001454 00 ca fe ff 00 10 ff ff
0001464 00 c7 16 00 00 81 16 00
0001474 00 25 2e 00 00 1e 2e 00
0001504 00 5c 45 00 00 e2 44 00
0001514 00 24 5c 00 00 a9 5b 00
0001524 00 53 72 00 00 e6 71 00
0001534 00 df 87 00 00 9b 87 00
0001544 00 5d 9c 00 00 e9 9b 00
0001554 00 31 b0 00 00 8b af 00
0001564 00 a8 c2 00 00 37 c2 00
0001574 00 33 d4 00 00 57 d3 00
[brettb@maven audtest]$

 

[brettb@maven audtest]$ cat dwav32.sh
od --skip=44 -t x4 --width=8 $1
[brettb@maven audtest]$

 

Can I pull off the bytes at offset McBSP.DDR+2?

  • Hello,

     

    I didnt tried this , and its a little bit messy, but it should work:

    You need to set 4 independent PaRAM sets, each for every byte in 4byte word, for example, if You have array B and L arrays (for big and little endian, respectivelly), You can configure like this:

    1st PaRAM will transfer first byte of each word from L to last byte from B;

    2nd PaRAM will transfer second byte from L to third byte from B and so on...

    You will have to play a little bit with Bidx and Cidx parameters in PaRAM set. I advice You chain them if your source is FIFO buffer (chain in such way so the 1st PaRAM will allways execute before 2nd in the same word)

     

    Another way is to trigger a software interrupt on transfer completion from EDMA to temporarly array. The software interrupt will execute:

    void DSPF_blk_eswap32(void *restrict x, void *restrict y, const int n);

    function in loop and at completion will trigger another EDMA transfer to final destination.

    The second way is a lot easier, and will cause less headeachekes to write, but will take CPU resources.

     

    Regards

    Arye

    P.S.  You change an endianness of your project.

     

  • Arye has suggested two very complete and very flexible ways to accomplish what you have asked.

    For other ideas, I would ask for some more detail on what you want to receive from the McBSP and what you want to send to the ALSA buffers.

    1. Your two tables only show half of the data, so I assume that is just for brevity since there should be 16 bytes per line yet only 8 bytes are shown; it just raises the question whether there is more to consider.
    2. Are you receiving only 24- or even 16-bit data words from the McBSP? All of your samples shown have 00 in the low 4 bits and sign-extension in the upper 4 bits. If the only relevant data is in the middle 16 bits, then this could be accounted for in some way during this byte-ordering process.
    3. How do you need the data stored when it is received? I assume the buffer shown in your posting is how it appears after receiving it from the McBSP. It might not matter how it is stored here if it is only transferred to the ALSA buffer, but it might matter if it is processed in some way while in the input buffer.
    4. How should the data be packed going to the ALSA buffer? Do you want just the middle 2 bytes to be packed in an array of 16-bit values, or what else? Please show what the ALSA buffer should look like.

    As Arye mentions in the postscript above, it is unusual to have to change the endianness within a project. It would seem likely that ALSA could use the same endianness.

    Perhaps your question is not about changing endianness, but about some different byte ordering. Your answers to the above will help us understand better.

     

  • The data capture is two channels of 24bit data.  The i2s puts each 24bits into a 32 bit chunk, so the complete capture is 2x32bits or 8 bytes. 

    Initially I want to capture 16bit pcm so the xform looks like this

         [ABCx.DEFy]    =>   [AB.DE]

    Input is 8 bytes, output is 4 bytes.    Looks like I can do it with two steps.  I can ask ALSA to do the byte reordering, but it would be better to offload the work.

     

    Thanks, Brett

  • The syntax you are using is helpful but does not tell me exactly what you want stored in memory. If you can, and if you still need help on this, please write a pair of destination tables like the word- and byte-addressed input tables from your original posting, using the data from your original posting.

    brett.bolen said:

         [ABCx.DEFy]    =>   [AB.DE]

    This does tell me that you do not want to do an endianness change, since the bytes are still in the same order. It is not clear which letter goes into byte 0, etc., and I could see logic in multiple guesses.

    But in any case for how you want the destination to be configured, the fact that the byte ordering does not change and only the bytes being extracted are what matters, this tells me you can do this easily in one step. With a single QDMA (or DMA, if you prefer), you can pull two bytes from every 4 bytes and write them to consecutive (or swapped) half-words. This can be done as long as you have direct access to all of the Param registers. This way, only a single "copy" call will need to be made to copy data from the input buffers shown in the original posting to the destination buffer to be used by the ALSA functions.

  • Here is raw data I am seeing in the pcm capture buffer

     

    address:[  Left   ].[  Right  ]

    0001154 00 b7 78 dc 00 45 52 e2
    0001164 00 37 69 e2 00 40 73 e8
    0001174 00 2a 85 e8 00 92 bb ee
    0001204 00 43 c8 ee 00 d3 1e f5
    0001214 00 91 26 f5 00 3c 91 fb
    0001224 00 7b 94 fb 00 aa 0b 02
    0001234 00 a0 0a 02 00 6d 83 08
    0001244 00 9a 7d 08 00 1a ec 0e
    0001254 00 84 e1 0e 00 4f 3f 15
    0001264 00 de 2f 15 00 4d 6f 1b
    0001274 00 b7 5c 1b 00 8f 74 21

    root@10.8.104.60:~/audtest# cat dwav8.sh
    od --skip=44 -t x1 --width=8 $1            # skip wave header
    root@10.8.104.60:~/audtest#

    There is a zeros crossing at address 1234: The left channel goes from 0xfb947b00 @ 01224 to 020aa000@ 01234 ( Note that 'od' gives addresses in Octal.

    The data I would like captured from a byte perspective is:

    0001224 94 fb 0b 02
    0001234 0a 02 83 08

    I am seeing the following when I set src_bidx=4, dest_bidx=2.

    0001224 00 7b 00 aa
    0001234 00 a0 00 6d

    These are the LSBs -- not the MSBs.  So it looks like it is not an endian swap at all, it is just starting at the wrong offset.

      [B0.B1.B2.B3]   ==> [B2.B3]  good

                      ==> [B0.B1]   bad

    Unfortnately capturing at McBSP.DRR+2 with src_bidx=4, doesn't work:

    root@10.8.104.60:~/audtest# ./ch.rec 3v1
    Recording WAVE '3v1.wav' : Signed 16 bit Little Endian, Rate 48000 Hz, Stereo
    arecord: pcm_read:1345: read error: Input/output error
    root@10.8.104.60:~/audtest#

    DEBUG:

    sdtp(lch=65,acnt(4)=4,bcnt(count)=4096,ccnt(1)=1,bcntrld(0)=0,sync_mode(ASYNC)=0)
    .......................... DMA # 6
    OPT = 0x00103000
    SRC = 0x01d02002
    A_B_CNT = 0x10000004
    DST = 0x8c220000
    SRC_DST_BIDX = 0x00020000
    LINK_BCNTRLD = 0x00004820
    SRC_DST_CIDX = 0x00000000
    CCNT = 0x00000001

    Are there regs to reorder the BYTES within a word with the McBSP or EDMA?

    Brett

     

    Brett

     

     

  • We will get to a solution, but please bear with me...

    brett.bolen said:

    There is a zeros crossing at address 1234: The left channel goes from 0xfb947b00 @ 01224 to 020aa000@ 01234 ( Note that 'od' gives addresses in Octal.

    The data I would like captured from a byte perspective is:

    0001224 94 fb 0b 02
    0001234 0a 02 83 08

    This is getting very confusing to me, and I have been staring at this trying to figure out what you are doing and what you want to do. The mixing of octal addresses and byte representations with hex addresses and word representations plus the desire to work with half-word values does not lead to an intuitive picture of your situation for me.

    I do not understand the use of octal addresses, and that has got to be a chance for translation errors along the way. At least it explains the "missing data" in the original tables, but what about the missing data as shown here?

    The DMA PARAM registers are very helpful, but please tell me if the DST address is just an input buffer or is intended to be directly the ALSA buffer. In other words, do you plan to capture the McBSP data into one place and then copy it to the ALSA buffer?

    brett.bolen said:

    Unfortnately capturing at McBSP.DRR+2 with src_bidx=4, doesn't work:

    You cannot use DRR+2 for your address. The McBSP config space must be read as a 32-bit word-aligned register.

    brett.bolen said:

    Are there regs to reorder the BYTES within a word with the McBSP or EDMA?

    No. And reordering is not what you want to do anyway, since that would not imply the removal of data, while you do want to drop bytes from the captured data.

     

    Are either of these what you want to do?

    A - Reasonably easy to do:

    1. Capture 0x1000 samples (0x0800 pairs of L/R samples) from the McBSP into a capture buffer in DDR.
    2. Generate an interrupt when that set of samples is received.
    3. Copy data from the capture buffer to an ALSA buffer of 16-bit samples.
    4. Call the ALSA function to operate on that copied buffer.
    5. Nothing else needs to be done with the capture buffer.

    B - May be tricky or difficult:

    1. Capture 0x1000 16-bit samples (0x0800 pairs of L/R samples) only retaining the 16 MSBs from the McBSP into a capture buffer in DDR.
    2. Generate an interrupt when that set of samples is received.
    3. Call the ALSA function to operate on that capture buffer.
  • I did not realize the output of  "od / 'octal dump' " would use octal addresses.  The are only used in the output of the dump command.  They should not have any effect on the capture operation.  (btw, I don't understand the reference about missing data.)

    DST is just a memory buffer that get's passed to ALSA from sound/soc/davinci/davinci-pcm.c.  I

    Options: 

    Option #1 (  Program EDMA to reorder the bytes), but I'd like to get option #2 ( manually reording the data) working first. 

    with #2, what hooks do I need to put into the code? 

    Here is the usage ( mine is similar, from an earlier patch on 2.6.18)

    http://lxr.linux.no/#linux+v2.6.32/sound/soc/davinci/davinci-pcm.c#L103

     

    Much thanks.

     

     

  • Maybe a problem description reset is in order:

    My audio codec is giving me the the data in a different byte order than the TI DM365 EVM.   I am using the kernel 2.6.18 kernel from TI/MontaVista that came with the development board.

    When I use audio driver code based on the TI DM365 EVM ( sound/soc/codec/tlv320a1c3x.[hc], sound/soc/davinci/davinci-i2s-mcbsp.[hc] and davinci-pcm.c, i have some problems.

    The main problem is that I want to get a 16 bit Stereo capture at 48khz.   The codec is providing the data in a 32bit i2s stream, but the BYTE order is reversed S16_LE (little endian),  so the driver code is truncating the MostSignificant BYTES:


          Address N, N+1, N+2, N+3

    Input       [B0,  B1,  B2, B3]              sample MS Byte is B3, LSByte is B0

     

    Driver Output    [ B0, B1]

    but I want       [ B2, B3]

    I would like to find a solution, that pulls off other two bytes and puts them in the ALSA sound buffers.  I do not have control of the codec, so the options available to me are reording via the MCBSP serial capture, or with the EDMA controller, or handle it manually with an additional step.

    I have not had success in getting ALSA to change the ENDIAN.

    Similar code is here:

      http://lxr.linux.no/#linux+v2.6.32/sound/soc/davinci/davinci-pcm.c#L103

    What Method should I use to get the data BYTES in the correct order? 

    Thanks, Brett

     

  • clarification:

       I have not had success in getting ALSA to change the ENDIAN.

    UMmmm.  Sorry, this is not an endian issue.. this was an intermediate step I was trying ( setting up 32bit big endian capture)... ignore this statement.

  • Does ALSA require a buffer of 32-bit words with the 16-bit value in the lower half, or does it require Left/Right 16-bit values to be packed?

  • ALSA is able to handle different formats.  I'm experimenting with hardcoding the the input to Signed 32 bit Little endian capture. now.

     

    Here are some of the formats in include/sound/asound.h

     

    #define SNDRV_PCM_FORMAT_S8     ((__force snd_pcm_format_t) 0)
    #define SNDRV_PCM_FORMAT_U8     ((__force snd_pcm_format_t) 1)
    #define SNDRV_PCM_FORMAT_S16_LE ((__force snd_pcm_format_t) 2)
    #define SNDRV_PCM_FORMAT_S16_BE ((__force snd_pcm_format_t) 3)
    #define SNDRV_PCM_FORMAT_U16_LE ((__force snd_pcm_format_t) 4)
    #define SNDRV_PCM_FORMAT_U16_BE ((__force snd_pcm_format_t) 5)
    #define SNDRV_PCM_FORMAT_S24_LE ((__force snd_pcm_format_t) 6) /* low three bytes */
    #define SNDRV_PCM_FORMAT_S24_BE ((__force snd_pcm_format_t) 7) /* low three bytes */
    #define SNDRV_PCM_FORMAT_U24_LE ((__force snd_pcm_format_t) 8) /* low three bytes */
    #define SNDRV_PCM_FORMAT_U24_BE ((__force snd_pcm_format_t) 9) /* low three bytes */
    #define SNDRV_PCM_FORMAT_S32_LE ((__force snd_pcm_format_t) 10)
    #define SNDRV_PCM_FORMAT_S32_BE ((__force snd_pcm_format_t) 11)
    #define SNDRV_PCM_FORMAT_U32_LE ((__force snd_pcm_format_t) 12)
    #define SNDRV_PCM_FORMAT_U32_BE ((__force snd_pcm_format_t) 13)
    #define SNDRV_PCM_FORMAT_FLOAT_LE       ((__force snd_pcm_format_t) 14) /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */
    #define SNDRV_PCM_FORMAT_FLOAT_BE       ((__force snd_pcm_format_t) 15) /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */

     

    Plus 40 more....

     

  • ALL,

    It looks like I can always capture at S32_LE ( signed 32bit little endian capture) and have ALSA do the conversion.  I'm still interested in being able to truncate the bytes in HW, but I'm going to run with it as it is.  It does not look like there is very much overhead to have it done in software. 

    I want to learn more about EDMA, if you have input in doing this with the dma controller, I'll code it up and post the results.

    Thanks.

      Brett

  • That is great that you have ALSA working with your codec by using the S32_LE format flag. I am glad that ALSA has so much flexibility, especially since many other users would not be anywhere near as sophisticated as you at figuring out how data is stored and manipulated.

    For EDMA3 training, there is a video training module for the C6474 that provides an introduction to EDMA3. Most of the information there is common to any device with EDMA3. Some details may vary, like the number of channels, number of Transfer Controllers, etc. This training can be found at C6474 Training , then look for the EDMA3/QDMA/IDMA training module.

    Now that you have it working, let's discuss some ideas for how you might implement what you really want, maybe just as an intellectual exercise. My assumption is that what you want is packed S16_LE where each 32-bit word contains a 16-bit Left sample and a 16-bit Right sample. Here are the scenarios I envision (some may work and some may not):

    CURRENT:

    You are capturing from the McBSP to the external memory buffer into an array of S32_LE samples, storing Left and Right in 32-bit words with 24-bits of data in the top 3 bytes of each word. The PARAM is likely the following, slightly modified from what you show above:

    .......................... DMA # 6
    OPT = 0x00103000            [inten, tcc=3]
    SRC = 0x01d02000            [McBSP.DRR]
    A_B_CNT = 0x10000004        [buf depth=0x1000, store 32-bits each]
    DST = 0x8c220000            [memory buffer]
    SRC_DST_BIDX = 0x00040000   [srcidx=0 dstidx=4]
    LINK_BCNTRLD = 0x00004820
    SRC_DST_CIDX = 0x00000000
    CCNT = 0x00000001


    IDEAL:

    Ideally, you would be able to pick off just the top two bytes from the McBSP, but the McBSP Config bus requires 32-bit accesses. If we ignore this restriction and disregard any possible problems, then a solution would be the following which might be just a small change from what you tried before. I do not really expect this to work, but mostly because I try to closely follow the rules on things like access widths. But this might work.

    .......................... DMA # 6
    OPT = 0x00103000            [inten, tcc=3]
    SRC = 0x01d02002            [McBSP.DRR+2]
    A_B_CNT = 0x10000002        [buf depth=0x1000, store 16-bits each]
    DST = 0x8c220000            [memory buffer]
    SRC_DST_BIDX = 0x00020000   [srcidx=0 dstidx=2]
    LINK_BCNTRLD = 0x00004820
    SRC_DST_CIDX = 0x00000000
    CCNT = 0x00000001


    TRICKY:

    This idea will be to read 4 bytes as two consective half-words and write them as two overlapping/overwritten half-words. This counts on the Transfer Controller being smart enough to combine the two 16-bit reads into a single 32-bit read, which it should do easily. And this uses 3D to write 2 lower bytes to the memory buffer and then 2 higher bytes to the same address in the memory buffer, overwriting the lower bytes. We will use AB-Sync to get the 2D operation with each event and the 3rd D to get the full count. Hopefully, you do have full control over all of these parameters with your function calls.

    .......................... DMA # 6
    OPT = 0x00103000            [inten, tcc=3]
    SRC = 0x01d02000            [McBSP.DRR]
    A_B_CNT = 0x00020002        [store twice with BCnt, store 16-bits each with ACnt]
    DST = 0x8c220000            [memory buffer]
    SRC_DST_BIDX = 0x00000002   [Bsrcidx=2 Bdstidx=0]
    LINK_BCNTRLD = 0x00024820
      [BCntRld=2, not req'd but good documentation]
    SRC_DST_CIDX = 0xfffe0002   [Csrcidx=-2 so net per read-pair is 0, Cdstidx=2 so net per write-pair is 2]
    CCNT = 0x00001000           [buf depth=0x1000]

    2-stage techniques:

    If neither IDEAL nor TRICKY work well enough, then you can definitely use a 2nd stage to automatically pack the high bytes. There are three different ways to do this with just the EDMA by chaining to a second DMA channel to make everything right: STAGED, SHORT, and LONG.All three will require that you allocate another DMA channel and another PARAM set, possibly one more PARAM set for a link set in some cases. I will assume that channel A is available and PARAM sets B and C are available.

    STAGED will use a 1-word buffer to capture the McBSP data for every word and then chain to a channel that will copy the 2 high bytes to the destination. No link set is required for the McBSP capture, so the link set at 4820 will be used for link for the 2nd stage.

    IN SITU SHORT will let the McBSP data be copied as before in 32-bit words into a packed array and then the 2nd channel will manipulate the data to move the 2 high bytes down to the right position.. This will require an extra half-word of space at the end of the buffer for overflow. This will require the extra PARAM set C for linking.

    IN SITU LONG will let the McBSP data be copied as before in 32-bit words, and when the full buffer is captured it will chain to the 2nd channel A to pack the data in place and then generate the interrupt. This will require the extra PARAM set C for linking.


    STAGED:

    DMA 6 will now copy 4 bytes to a staging location called MemStage. This let's the McBSP.DRR be read as a full 32-bit register so there are no ill-effects on the Config Bus. Then DMA 6 will chain to channel A to copy 2 high bytes into the packed array. Channel A will generate the interrupt when the whole buffer has been filled. Since Channel 6 always does the same thing, it can be set to be STATIC with the caveat that bad things may happen if Channel A is not kept valid; this will not be a problem if you are always reloading to the same buffer or using a double-buffer scheme and keep the link set updated (or use two link sets like in the training video).

    .......................... DMA # 6
    OPT = 0x0020a008            [tcchen, tcc=A, STATIC]
    SRC = 0x01d02000            [McBSP.DRR]
    A_B_CNT = 0x00010004        [BCnt=1, store 32-bits each]
    DST = &MemStage             [staging buffer]
    SRC_DST_BIDX = 0x00000000   [srcidx=0 dstidx=0]
    LINK_BCNTRLD = 0x00000000
    SRC_DST_CIDX = 0x00000000
    CCNT = 0x00000001

    .......................... DMA # A - PARAM B
    OPT = 0x00103000            [inten, tcc=3]
    SRC =
    &MemStage+2           [staging buffer+2 is where the high bytes are captured]
    A_B_CNT = 0x10000002        [buf depth=0x1000, move 16-bits each time]
    DST = 0x8c220000            [memory buffer]
    SRC_DST_BIDX = 0x00020000   [srcidx=0 dstidx=2]
    LINK_BCNTRLD = 0x00004820
    SRC_DST_CIDX = 0x00000000
    CCNT = 0x00000001


    IN SITU SHORT:

    DMA Channel 6 is much the same as the original, but increments the dst addr by 2 for packing and instead of generating an interrupt at the end it will chain to channel A every time. Channel A will move the high 2 bytes down to leave the data the way you want it to be in the packed buffer. There will need to be 2 bytes of overflow at the end of the buffer when the last sample is captured, so the whole buffer is 0x1000*2+2 bytes.

    .......................... DMA # 6
    OPT = 0x0080a000            [itcchen, tcc=A]
    SRC = 0x01d02000            [McBSP.DRR]
    A_B_CNT = 0x
    10000004        [BCnt=buf depth=0x1000, store 32-bits each]
    DST =
    0x8c220000            [memory buffer]
    SRC_DST_BIDX = 0x00020000   [srcidx=0 dstidx=2]
    LINK_BCNTRLD = 0x0000
    4820
    SRC_DST_CIDX = 0x00000000
    CCNT = 0x00000001

    .......................... DMA # A - PARAM B
    OPT = 0x00103000            [inten, tcc=3]
    SRC = 
    0x8c220002            [memory buffer+2 is where the high bytes start]
    A_B_CNT = 0x10000002        [buf depth=0x1000, move 16-bits each time]
    DST = 0x8c220000            [memory buffer]
    SRC_DST_BIDX = 0x00020002   [srcidx=2 dstidx=2]
    LINK_BCNTRLD = 0x0000----   [link to new link set C]
    SRC_DST_CIDX = 0x00000000
    CCNT = 0x00000001


    IN SITU LONG:

    DMA Channel 6 is the same as the original, but instead of generating an interrupt at the end it will chain to channel A at the end. Channel A will move the high 2 bytes from every word into a packed position to leave the data the way you want it to be in the low half of the full buffer. The whole buffer is still 0x1000*4 bytes, but only the lower half will be passed to ALSA. The disadvantage of this method is the extra latency from the last sample until the interrupt is generated since channel A has to run through the whole buffer at one time.

    .......................... DMA # 6
    OPT = 0x0020a000            [tcchen, tcc=A]
    SRC = 0x01d02000            [McBSP.DRR]
    A_B_CNT = 0x
    10000004        [BCnt=buf depth=0x1000, store 32-bits each]
    DST =
    0x8c220000            [memory buffer]
    SRC_DST_BIDX = 0x00040000   [srcidx=0 dstidx=4]
    LINK_BCNTRLD = 0x0000
    4820
    SRC_DST_CIDX = 0x00000000
    CCNT = 0x00000001

    .......................... DMA # A - PARAM B
    OPT = 0x00103004            [inten, tcc=3, ABSync]
    SRC = 
    0x8c220002            [memory buffer+2 is where the high bytes start]
    A_B_CNT = 0x10000002        [buf depth=0x1000, move 16-bits each time]
    DST = 0x8c220000            [memory buffer]
    SRC_DST_BIDX = 0x00020004   [srcidx=4 dstidx=2]
    LINK_BCNTRLD = 0x0000----   [link to new link set C]
    SRC_DST_CIDX = 0x00000000
    CCNT = 0x00000001


    If you have a chance to try any of these, please post back your results and your opinions on the different techniques. My apologies in advance for any errors above.

  • Much thanks..  There is a lot to try here.  I'll post what I find out.

     

    1.  Current -- your params are correct, this is what is working but not ideal.

    2.  Ideal - I tried this , but no data flows into edma.  This address needs to be aligned.

    3. Tricky.... I like this.  I'll give it a try.

    4.  Rest -- I need to investigate/understand these a bit more. 

     

    Thanks, Brett