/** * \brief 32-bit checksum of DMA transfer data * * This test sets up DMA to do a data block transfer, and sets up * the CRC module to do a 32 bit checksum on the data. The checksum * will then be added to another buffer, the same procedure is setup * with this new buffer which should result in a checksum = 0. * * \param test Current test case */ static void run_32bit_dma_test(const struct test_case *test) { uint32_t checksum; bool success; uint8_t data_buf_8bit[LENGTH(data_8bit) + sizeof(uint32_t)]; uint8_t data_8bit_cpy[LENGTH(data_8bit) + sizeof(uint32_t)]; setup_dma_channel(LENGTH(data_8bit), (uint8_t *)data_8bit, data_buf_8bit); crc_dma_checksum_start(CONF_TEST_DMACH, CRC_32BIT); dma_channel_trigger_block_transfer(CONF_TEST_DMACH); success = wait_for_dma_transfer(test); dma_channel_disable(CONF_TEST_DMACH); dma_disable(); checksum = crc_dma_checksum_stop(); if (!success) { return; } test_assert_true(test, checksum == CRC_CHECKSUM_32BIT, "Checksum mismatch on DMA CRC-32 test"); memcpy(data_8bit_cpy, data_8bit, LENGTH(data_8bit)); crc32_append_value(checksum, &data_8bit_cpy[LENGTH(data_8bit_cpy) - sizeof(uint32_t)]); setup_dma_channel(LENGTH(data_8bit_cpy), (uint8_t *)data_8bit_cpy, data_buf_8bit); crc_dma_checksum_start(CONF_TEST_DMACH, CRC_32BIT); dma_channel_trigger_block_transfer(CONF_TEST_DMACH); success = wait_for_dma_transfer(test); dma_channel_disable(CONF_TEST_DMACH); dma_disable(); checksum = crc_dma_checksum_stop(); if (!success) { return; } test_assert_true(test, checksum == 0, "Checksum fail check failed on DMA CRC-16 test"); }
/** * \brief Test the error handling of the module * * \note Test error handling by disabling a channel which is in use * * \param test Current test */ static void run_dma_error_handling_test(const struct test_case *test) { /* Enable DMA */ dma_enable(); /* Reset the channel */ dma_channel_reset(DMA_CHANNEL_0); /* Set up channel 0 to do some work, check that is it busy, * change some settings and verify a transfer error */ dma_channel_write_burst_length(DMA_CHANNEL_0, DMA_CH_BURSTLEN_1BYTE_gc); dma_channel_write_transfer_count(DMA_CHANNEL_0, MEMORY_BLOCK_SIZE); dma_channel_write_source(DMA_CHANNEL_0, (uint16_t)(uintptr_t)memory_block_src); dma_channel_write_destination(DMA_CHANNEL_0, (uint16_t)(uintptr_t)memory_block_dest); /* Enable the channel */ dma_channel_enable(DMA_CHANNEL_0); /* Start a block transfer */ dma_channel_trigger_block_transfer(DMA_CHANNEL_0); /* Wait for the channel to become busy */ while (!dma_channel_is_busy(DMA_CHANNEL_0)) { /* Intentionally left empty */ } /* Disable the channel while it is busy */ if (dma_channel_is_busy(DMA_CHANNEL_0)) { dma_channel_disable(DMA_CHANNEL_0); } /* Test whether the channel is in error */ test_assert_true(test, dma_get_channel_status( DMA_CHANNEL_0) == DMA_CH_TRANSFER_ERROR, "DMA channel not in error after disabling during transfer" " write"); dma_disable(); }
/** * * \brief Test double buffering mode * * \note This function tests the double buffering feature of the DMA * controller by configuring channel 0 and 1 to do the same copy, * and verify that the channels enable each other according to * the double buffering process. * * \param test Current test case */ static void run_dma_double_buffering_test(const struct test_case *test) { struct dma_channel_config config_params; bool success = true; /* Assume everything goes well */ /* Fill source block with pattern data */ set_buffer(memory_block_src, 0x00); block_fill(memory_block_src, MEMORY_BLOCK_SIZE); /* Null out the destination block */ set_buffer(memory_block_dest, 0x00); /* Null out the config params */ memset(&config_params, 0, sizeof(config_params)); /* Enable DMA */ dma_enable(); /* Enable double buffering mode on channel 0 and 1 */ dma_set_double_buffer_mode(DMA_DBUFMODE_CH01_gc); /* Set channel 1 to copy from memory_block_src to memory_block_dest */ dma_channel_set_src_reload_mode(&config_params, DMA_CH_SRCRELOAD_NONE_gc); dma_channel_set_src_dir_mode(&config_params, DMA_CH_SRCDIR_INC_gc); dma_channel_set_dest_reload_mode(&config_params, DMA_CH_DESTRELOAD_NONE_gc); dma_channel_set_dest_dir_mode(&config_params, DMA_CH_DESTDIR_INC_gc); dma_channel_set_burst_length(&config_params, DMA_CH_BURSTLEN_1BYTE_gc); dma_channel_set_transfer_count(&config_params, MEMORY_BLOCK_SIZE); dma_channel_set_source_address(&config_params, (uint16_t)(uintptr_t)memory_block_src); dma_channel_set_destination_address(&config_params, (uint16_t)(uintptr_t)memory_block_dest); dma_channel_set_repeats(&config_params, DOUBLE_BUFFER_REPEATS); /* Write config and enable */ dma_channel_write_config(DMA_CHANNEL_0, &config_params); dma_channel_write_config(DMA_CHANNEL_1, &config_params); /* Enable only channel 0 */ dma_channel_enable(DMA_CHANNEL_0); /* Transfer block and wait for it to finish */ dma_channel_trigger_block_transfer(DMA_CHANNEL_0); while (dma_get_channel_status(DMA_CHANNEL_0) != DMA_CH_TRANSFER_COMPLETED) { /* Intentionally left empty */ } /* * If double buffering is working, channel 1 * will be enabled now by the controller */ if (!(dma_channel_is_enabled(DMA_CHANNEL_1))) { success = false; } /* * Disable channel 0, transfer channel 1, * and verify that channel 0 is enabled again by the controller */ dma_channel_disable(DMA_CHANNEL_0); /* Transfer block and wait for it to finish */ dma_channel_trigger_block_transfer(DMA_CHANNEL_1); while (dma_get_channel_status(DMA_CHANNEL_1) != DMA_CH_TRANSFER_COMPLETED) { /* Intentionally left empty */ } /* Verify that channel 0 is enabled again */ if (!(dma_channel_is_enabled(DMA_CHANNEL_0))) { success = false; } test_assert_true(test, success, "Double buffering mode did not function properly"); }
/** * \brief Test different directions on all channels * * \note This test copies the source memory block into the destination block * in different ways. * * \param test Current test */ static void run_dma_direction_test(const struct test_case *test) { struct dma_channel_config config_params; uint8_t channel_index; bool success = true; /* Assume everything goes well */ /* Fill the source block with our known pattern */ set_buffer(memory_block_src, 0x00); block_fill(memory_block_src, MEMORY_BLOCK_SIZE); /* Null out the config params */ memset(&config_params, 0, sizeof(config_params)); /* Enable DMA */ dma_enable(); /* No reload on source and destination */ dma_channel_set_src_reload_mode(&config_params, DMA_CH_SRCRELOAD_NONE_gc); dma_channel_set_dest_reload_mode(&config_params, DMA_CH_DESTRELOAD_NONE_gc); dma_channel_set_transfer_count(&config_params, MEMORY_BLOCK_SIZE); dma_channel_set_burst_length(&config_params, DMA_CH_BURSTLEN_1BYTE_gc); /* Test a memory transfer on all channels */ for (channel_index = 0; channel_index < DMA_NUMBER_OF_CHANNELS; channel_index++) { /* Reset channel and write the configuration */ dma_channel_reset(channel_index); /* Increment source, increment destination */ dma_channel_set_src_dir_mode(&config_params, DMA_CH_SRCDIR_INC_gc); dma_channel_set_dest_dir_mode(&config_params, DMA_CH_DESTDIR_INC_gc); /* Data starts from the first byte */ dma_channel_set_source_address(&config_params, (uint16_t)(uintptr_t)memory_block_src); dma_channel_set_destination_address(&config_params, (uint16_t)(uintptr_t)memory_block_dest); /* Write the config */ dma_channel_write_config(channel_index, &config_params); /* Clear destination */ set_buffer(memory_block_dest, 0x00); /* Enable channel, transfer, and disable it */ dma_channel_enable(channel_index); dma_transfer_block(channel_index); dma_channel_disable(channel_index); /* Check that source and destination are equal */ success = block_compare(memory_block_src, memory_block_dest, MEMORY_BLOCK_SIZE); if (!success) { break; } /* Reset channel and write the configuration */ dma_channel_reset(channel_index); /* Decrement source, increment destination */ dma_channel_set_src_dir_mode(&config_params, DMA_CH_SRCDIR_DEC_gc); dma_channel_set_dest_dir_mode(&config_params, DMA_CH_DESTDIR_INC_gc); /* Data starts from the first byte */ dma_channel_set_source_address(&config_params, (uint16_t)(uintptr_t) (memory_block_src + MEMORY_BLOCK_SIZE - 1)); dma_channel_set_destination_address(&config_params, (uint16_t)(uintptr_t)memory_block_dest); /* Write the config */ dma_channel_write_config(channel_index, &config_params); /* Clear destination */ set_buffer(memory_block_dest, 0x00); /* Enable channel, transfer, and disable it */ dma_channel_enable(channel_index); dma_transfer_block(channel_index); dma_channel_disable(channel_index); /* Check that destination is the reverse of source */ success = block_compare_reverse(memory_block_src, memory_block_dest, MEMORY_BLOCK_SIZE); if (!success) { break; } /* Reset channel and write the configuration */ dma_channel_reset(channel_index); /* Decrement source, increment destination */ dma_channel_set_src_dir_mode(&config_params, DMA_CH_SRCDIR_INC_gc); dma_channel_set_dest_dir_mode(&config_params, DMA_CH_DESTDIR_DEC_gc); /* Data starts from the first byte */ dma_channel_set_source_address(&config_params, (uint16_t)(uintptr_t)memory_block_src); dma_channel_set_destination_address(&config_params, (uint16_t)(uintptr_t) (memory_block_dest + MEMORY_BLOCK_SIZE - 1)); /* Write the config */ dma_channel_write_config(channel_index, &config_params); /* Clear destination */ set_buffer(memory_block_dest, 0x00); /* Enable channel, transfer, and disable it */ dma_channel_enable(channel_index); dma_transfer_block(channel_index); dma_channel_disable(channel_index); /* Check that destination is the reverse of source */ success = block_compare_reverse(memory_block_src, memory_block_dest, MEMORY_BLOCK_SIZE); if (!success) { break; } /* Reset channel and write the configuration */ dma_channel_reset(channel_index); /* Decrement source, Decrement destination */ dma_channel_set_src_dir_mode(&config_params, DMA_CH_SRCDIR_DEC_gc); dma_channel_set_dest_dir_mode(&config_params, DMA_CH_DESTDIR_DEC_gc); /* Data starts from the first byte */ dma_channel_set_source_address(&config_params, (uint16_t)(uintptr_t) (memory_block_src + MEMORY_BLOCK_SIZE - 1)); dma_channel_set_destination_address(&config_params, (uint16_t)(uintptr_t) (memory_block_dest + MEMORY_BLOCK_SIZE - 1)); /* Write the config */ dma_channel_write_config(channel_index, &config_params); /* Clear destination */ set_buffer(memory_block_dest, 0x00); /* Enable channel, transfer, and disable it */ dma_channel_enable(channel_index); dma_transfer_block(channel_index); dma_channel_disable(channel_index); /* Check that source and destination are equal */ success = block_compare(memory_block_src, memory_block_dest, MEMORY_BLOCK_SIZE); if (!success) { break; } } /* Disable DMA */ dma_disable(); test_assert_true(test, success, "DMA direction copy test failed on channel %d", channel_index); }