import numpy as np
from teensy_minimal_rpc import SerialProxy
import teensy_minimal_rpc.DMA as dma

# Disconnect from existing proxy (if available)
try:
    del proxy
except NameError:
    pass

proxy = SerialProxy()

"""
Explanation: Example from Section 21.5.4.1 (page 441) in K20P64M72SF1RM manual
(MK20DX128 processor used on Teensy 3.1/3.2).
From the manual:

...the following TCD entry is configured to transfer 16 bytes of
data. The eDMA is programmed for one iteration of the major loop transferring
16 bytes per iteration. The source memory has a byte wide memory port located
at 0x1000. The destination memory has a 32-bit port located at 0x2000. The
address offsets are programmed in increments to match the transfer size: one
byte for the source and four bytes for the destination. The final source and
destination addresses are adjusted to return to their beginning values.
TCDn_CITER = TCDn_BITER = 1
TCDn_NBYTES = 16
TCDn_SADDR = 0x1000
TCDn_SOFF = 1
TCDn_ATTR[SSIZE] = 0
TCDn_SLAST = -16
TCDn_DADDR = 0x2000
TCDn_DOFF = 4
TCDn_ATTR[DSIZE] = 2
TCDn_DLAST_SGA= –16
TCDn_CSR[INT_MAJ] = 1
TCDn_CSR[START] = 1 (Should be written last after all other fields have been initialized)
All other TCDn fields = 0

This generates the following event sequence:
 1. User write to the TCDn_CSR[START] bit requests channel service.
 2. The channel is selected by arbitration for servicing.
 3. eDMA engine writes: TCDn_CSR[DONE] = 0, TCDn_CSR[START] = 0,
    TCDn_CSR[ACTIVE] = 1.

Connect to device
End of explanation
"""


N = 512
proxy.free_all()

# Allocate source array
src_addr = proxy.mem_alloc(N)
# Allocate destination array
dst_addr = proxy.mem_alloc(N)

# Fill first 16 bytes of source array with the numbers 1-16
proxy.mem_cpy_host_to_device(src_addr, np.arange(1, 17, dtype='uint8'))

# Fill the destination array with all zeros
proxy.mem_fill_uint32(dst_addr, 0, N / 4)

"""
Explanation: Allocate two arrays: source and destination
End of explanation
"""


# Create Transfer Control Descriptor configuration to match the settings
# shown in the example from the manual.
XFER_REQUEST = dma.TCD(
    # TCDn_CITER = TCDn_BITER = 1
    CITER_ELINKNO=dma.R_TCD_ITER_ELINKNO(ITER=1),
    BITER_ELINKNO=dma.R_TCD_ITER_ELINKNO(ITER=1),
    # TCDn_NBYTES = 16
    NBYTES_MLNO=16,
    # TCDn_SADDR = 0x1000
    SADDR=int(src_addr),
    # TCDn_SOFF = 1
    SOFF=1,
    # TCDn_ATTR[SSIZE] = 0
    # See `TCDn_ATTR[DSIZE]` below.
    # TCDn_SLAST = -16
    SLAST=-16,
    # TCDn_DADDR = 0x2000
    DADDR=int(dst_addr),
    # TCDn_DOFF = 4
    DOFF=4,
    # TCDn_ATTR[DSIZE] = 2
    ATTR=dma.R_TCD_ATTR(SSIZE=dma.R_TCD_ATTR._8_BIT,
                        DSIZE=dma.R_TCD_ATTR._32_BIT),
    # TCDn_DLAST_SGA= –16
    DLASTSGA=-16,
    # TCDn_CSR[INT_MAJ] = 1
    # We won't use interrupts here...
    # TCDn_CSR[START] = 1 (Should be written last after all other fields have been initialized)
    CSR=dma.R_TCD_CSR(START=1)
    # All other TCDn fields = 0
)

"""
Explanation: Create Transfer Control Descriptor (TCD) configuration
End of explanation
"""


print 'SOURCE:  ', proxy.mem_cpy_device_to_host(src_addr, 16)

# Fill the destination array with all zeros
proxy.mem_fill_uint32(dst_addr, 0, N / 4)
print 'TARGET:'
print '  Before:', proxy.mem_cpy_device_to_host(dst_addr, 16)

# Apply TCD configuration to DMA channel 0 to conduct transfer.
proxy.update_dma_TCD(0, XFER_REQUEST)

print '   After:', proxy.mem_cpy_device_to_host(dst_addr, 16)

"""
Explanation: Apply Transfer Control Descriptor configuration to start transfer
End of explanation
"""


# Read serialized TCD protocol buffer message for DMA channel 0 from device.
serialized_tcd0 = proxy.read_dma_TCD(0)
# Deserialize message into Python Protocol Buffer message.
tcd0 = dma.TCD.FromString(serialized_tcd0.tostring())

# Verify DMA operation is complete
# See TCD Control and Status section for more info (21.3.29/424 in manual).
assert(tcd0.CSR.DONE)

"""
Explanation: Confirm transfer completed successfully (i.e., check DMA_TCD0.CSR[DONE])
End of explanation
"""


