/** * \brief Access AES module with DMA support * * \note Read STATE using DMA channel 0. * Check DMA driver example for more information about DMA usage. * */ static void aes_dma_output(void) { // Make sure config is all zeroed out so we don't get any stray bits memset(&config, 0, sizeof(config)); dma_enable(); dma_channel_set_burst_length(&config, DMA_CH_BURSTLEN_1BYTE_gc); dma_channel_set_transfer_count(&config, BLOCK_LENGTH); dma_channel_set_src_reload_mode(&config, DMA_CH_SRCRELOAD_NONE_gc); dma_channel_set_dest_reload_mode(&config, DMA_CH_DESTRELOAD_NONE_gc); dma_channel_set_src_dir_mode(&config, DMA_CH_SRCDIR_FIXED_gc); dma_channel_set_dest_dir_mode(&config, DMA_CH_DESTDIR_INC_gc); dma_channel_set_source_address(&config, (uint16_t)(uintptr_t)&AES_STATE); dma_channel_set_destination_address(&config, (uint16_t)(uintptr_t)single_ans); dma_channel_write_config(DMA_CHANNEL_N, &config); // Use the configuration above by enabling the DMA channel in use. dma_channel_enable(DMA_CHANNEL_N); /* * Trigger a manual start since there is no trigger sources used in * this example. */ dma_channel_trigger_block_transfer(DMA_CHANNEL_N); while (!(DMA_CH0_CTRLB & DMA_CH_TRNIF_bm)); DMA_CH0_CTRLB |= DMA_CH_TRNIF_bm; dma_disable(); }
/** * \brief Setup a DMA channel for CRC calculation * * \param buf_length Length of buffer to transfer * \param src_buffer Pointer to transfer buffer * \param dst_buffer Pointer to receive buffer */ static void setup_dma_channel(uint8_t buf_length, uint8_t *src_buffer, uint8_t *dst_buffer) { struct dma_channel_config config; dma_enable(); dma_channel_write_source(CONF_TEST_DMACH, (uint16_t)src_buffer); dma_channel_write_destination(CONF_TEST_DMACH, (uint16_t)dst_buffer); dma_channel_write_burst_length(CONF_TEST_DMACH, 1); dma_channel_write_transfer_count(CONF_TEST_DMACH, buf_length); dma_channel_write_repeats(CONF_TEST_DMACH, 1); dma_channel_read_config(CONF_TEST_DMACH, &config); dma_channel_set_src_dir_mode(&config, DMA_CH_SRCDIR_INC_gc); dma_channel_set_dest_dir_mode(&config, DMA_CH_DESTDIR_INC_gc); dma_channel_write_config(CONF_TEST_DMACH, &config); dma_channel_enable(CONF_TEST_DMACH); }
/** * \brief main function */ int main(void) { struct dma_channel_config config; uint32_t checksum; pmic_init(); board_init(); sysclk_init(); sleepmgr_init(); // Randomly selected data source[0] = 0xAA; source[1] = 0xBB; source[2] = 0xCC; source[3] = 0xDD; source[4] = 0xEE; source[5] = 0xFF; // Calculate checksum for the data checksum = crc_io_checksum((void*)source, 6, CRC_16BIT); // Append the checksum to the data, big endian crc16_append_value(checksum, source+6); //Enable the CRC module for DMA crc_dma_checksum_start(DMA_CHANNEL, CRC_16BIT); // Enable DMA dma_enable(); // Set callback function for DMA completion dma_set_callback(DMA_CHANNEL, example_crc_dma_transfer_done); // Make sure config is all zeroed out so we don't get any stray bits memset(&config, 0, sizeof(config)); /** * This example will configure a DMA channel with the following * settings: * - Low interrupt priority * - 1 byte burst length * - DMA_BUFFER_SIZE bytes for each transfer * - Reload source and destination address at end of each transfer * - Increment source and destination address during transfer * - Source address is set to \ref source * - Destination address is set to \ref destination */ dma_channel_set_interrupt_level(&config, PMIC_LVL_LOW); dma_channel_set_burst_length(&config, DMA_CH_BURSTLEN_1BYTE_gc); dma_channel_set_transfer_count(&config, DMA_BUFFER_SIZE); dma_channel_set_src_reload_mode(&config, DMA_CH_SRCRELOAD_TRANSACTION_gc); dma_channel_set_dest_reload_mode(&config, DMA_CH_DESTRELOAD_TRANSACTION_gc); dma_channel_set_src_dir_mode(&config, DMA_CH_SRCDIR_INC_gc); dma_channel_set_dest_dir_mode(&config, DMA_CH_DESTDIR_INC_gc); dma_channel_set_source_address(&config, (uint16_t)(uintptr_t)source); dma_channel_set_destination_address(&config, (uint16_t)(uintptr_t)destination); dma_channel_write_config(DMA_CHANNEL, &config); // Use the configuration above by enabling the DMA channel in use. dma_channel_enable(DMA_CHANNEL); // Enable interrupts cpu_irq_enable(); // Trigger the DMA transfer dma_channel_trigger_block_transfer(DMA_CHANNEL); // Light the first LED to indicate that the DMA has started. gpio_set_pin_low(LED0_GPIO); while (true) { /* * Force a NOP instruction for an eventual placement of a debug * session breakpoint. */ asm("nop\n"); } }
/** * * \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 read from fixed location, trigger from timer and callback * * \note This test sets up a timer to trigger the DMA module, * which in turn reads the timer_overflow_counter variable and writes * it to memory sequentially. It then checks to see that the memory block * written is sequential according to the overflow count. * * \param test Current test */ static void run_dma_triggered_with_callback(const struct test_case *test) { struct dma_channel_config config_params; bool success; /* Null the buffer */ set_buffer(dest_block_tc, 0x0000); /* Null out the config parameter struct */ memset(&config_params, 0, sizeof(config_params)); /* * Enable the timer, and set it to count up. * When it overflows, it triggers the DMA to * read timer_overflow_counter. */ tc_enable(&TIMER); tc_set_direction(&TIMER, TC_UP); tc_write_period(&TIMER, TIMER_PERIOD); tc_set_resolution(&TIMER, TIMER_RESOLUTION); tc_set_overflow_interrupt_level(&TIMER, PMIC_LVL_LOW); tc_set_overflow_interrupt_callback(&TIMER, timer_overflow_callback); /* Enable the DMA module */ dma_enable(); /* Set callback for transfer done */ dma_set_callback(DMA_CHANNEL_0, dma_transfer_is_complete); /* Set low interrupt level */ dma_channel_set_interrupt_level(&config_params, PMIC_LVL_LOW); /* Set up the DMA to read the timer value * * - Single shot transfer mode * - Two byte (16-bit) burst length * - Increment on source and destination * - Reload on burst for source * - No reload for destination */ dma_channel_set_single_shot(&config_params); dma_channel_set_burst_length(&config_params, DMA_CH_BURSTLEN_1BYTE_gc); dma_channel_set_src_reload_mode(&config_params, DMA_CH_SRCRELOAD_BURST_gc); dma_channel_set_src_dir_mode(&config_params, DMA_CH_SRCDIR_FIXED_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); /* Set trigger source to TCC0's overflow */ dma_channel_set_trigger_source(&config_params, DMA_CH_TRIGSRC_TCC0_OVF_gc); /* Transfer DEST_BLOCK_TC_SIZE bytes */ dma_channel_set_transfer_count(&config_params, DEST_BLOCK_TC_SIZE); /* Set address */ dma_channel_set_source_address(&config_params, (uint16_t)(uintptr_t)&timer_overflow_counter); dma_channel_set_destination_address(&config_params, (uint16_t)(uintptr_t)dest_block_tc); /* Reset the channel */ dma_channel_reset(DMA_CHANNEL_0); /* Write the config */ dma_channel_write_config(DMA_CHANNEL_0, &config_params); /* Enable the channel */ dma_channel_enable(DMA_CHANNEL_0); /* Wait for transfer to finish */ while (!dma_has_completed) { /* Intentionally left empty */ } /* Disable DMA */ dma_disable(); /* Verify that the result is as expected */ success = block_compare(dest_block_tc, expected_result_tc, DEST_BLOCK_TC_SIZE); test_assert_true(test, success, "Result is not as expected"); }
/** * * \brief Set DMA configuration, and read it back * * \note This function writes a configuration to the DMA * controller, and reads it back to verify settings have * been correctly set. * * \param test Current test case */ static void run_dma_config_interface_test(const struct test_case *test) { struct dma_channel_config config_params; struct dma_channel_config read_config; const uint16_t transfer_count = 1024; const uint8_t repeats = 64; uint8_t channel_index; #ifdef CONFIG_HAVE_HUGEMEM hugemem_ptr_t dest_huge_addr = HUGEMEM_NULL; hugemem_ptr_t src_huge_addr = HUGEMEM_NULL; hugemem_write32(dest_huge_addr, 0xABCD1234); hugemem_write32(src_huge_addr, 0xAAAABBBB); #else const uint16_t dest_addr = 0xBEEF; const uint16_t src_addr = 0xABCD; #endif memset(&config_params, 0, sizeof(config_params)); dma_enable(); /* Apply some parameters */ dma_channel_set_burst_length(&config_params, DMA_CH_BURSTLEN_4BYTE_gc); dma_channel_set_single_shot(&config_params); dma_channel_set_interrupt_level(&config_params, PMIC_LVL_HIGH); dma_channel_set_src_reload_mode(&config_params, DMA_CH_SRCRELOAD_BLOCK_gc); dma_channel_set_dest_reload_mode(&config_params, DMA_CH_DESTRELOAD_BURST_gc); 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); dma_channel_set_trigger_source(&config_params, DMA_CH_TRIGSRC_TCC0_CCA_gc); dma_channel_set_transfer_count(&config_params, transfer_count); dma_channel_set_repeats(&config_params, repeats); #ifdef CONFIG_HAVE_HUGEMEM dma_channel_set_destination_hugemem(&config_params, dest_huge_addr); dma_channel_set_source_hugemem(&config_params, src_huge_addr); #else dma_channel_set_destination_address(&config_params, dest_addr); dma_channel_set_source_address(&config_params, src_addr); #endif /* Loop through all channels, read back config from them, and verify */ for (channel_index = 0; channel_index < DMA_NUMBER_OF_CHANNELS; channel_index++) { dma_channel_write_config(channel_index, &config_params); /* Null out the read_config struct */ memset(&read_config, 0, sizeof(read_config)); /* Read the config back from the module */ dma_channel_read_config(channel_index, &read_config); test_assert_true(test, read_config.addrctrl == config_params.addrctrl, "CH %d: Address control register does not match configuration", channel_index); test_assert_true(test, read_config.ctrla == config_params.ctrla, "CH %d: Control register A does not match configuration", channel_index); test_assert_true(test, read_config.repcnt == config_params.repcnt, "CH %d: Repeat counter register does not match configuration", channel_index); test_assert_true(test, read_config.trfcnt == config_params.trfcnt, "CH %d: Transfer counter register does not" " match configuration", channel_index); test_assert_true(test, read_config.trigsrc == config_params.trigsrc, "CH %d: Trigger source register does not match configuration", channel_index); #ifdef CONFIG_HAVE_HUGEMEM test_assert_true(test, read_config.destaddr == config_params.destaddr, "CH %d: Destination address register does not" " match configuration", channel_index); test_assert_true(test, read_config.srcaddr == config_params.srcaddr, "CH %d: Source address register does not match configuration", channel_index); #else test_assert_true(test, read_config.destaddr16 == config_params.destaddr16, "CH %d: DESTADDR16 does not match configuration", channel_index); test_assert_true(test, read_config.srcaddr16 == config_params.srcaddr16, "CH %d: SRCADDR16 does not match configuration"); #endif } /* Reset the channel */ dma_channel_reset(DMA_CHANNEL_0); /* Check set and unset single shot */ memset(&config_params, 0, sizeof(config_params)); memset(&read_config, 0, sizeof(read_config)); dma_channel_set_single_shot(&config_params); dma_channel_write_config(DMA_CHANNEL_0, &config_params); dma_channel_read_config(DMA_CHANNEL_0, &read_config); test_assert_true(test, read_config.ctrla == config_params.ctrla, "Single shot mode not set correctly"); memset(&config_params, 0, sizeof(config_params)); dma_channel_unset_single_shot(&config_params); dma_channel_write_config(DMA_CHANNEL_0, &config_params); dma_channel_read_config(DMA_CHANNEL_0, &read_config); test_assert_true(test, read_config.ctrla == config_params.ctrla, "Single shot mode not unset correctly"); /* Reset it again, and test the direct configuration functions */ memset(&read_config, 0, sizeof(read_config)); dma_channel_write_burst_length(DMA_CHANNEL_0, DMA_CH_BURSTLEN_4BYTE_gc); dma_channel_write_transfer_count(DMA_CHANNEL_0, transfer_count); dma_channel_write_repeats(DMA_CHANNEL_0, repeats); #ifdef CONFIG_HAVE_HUGEMEM dma_channel_write_source_hugemem(DMA_CHANNEL_0, src_huge_addr); dma_channel_write_destination_hugemem(DMA_CHANNEL_0, dest_huge_addr); #else dma_channel_write_source(DMA_CHANNEL_0, src_addr); dma_channel_write_destination(DMA_CHANNEL_0, dest_addr); #endif /* Verify that settings have been set correctly */ dma_channel_read_config(DMA_CHANNEL_0, &read_config); test_assert_true(test, (read_config.ctrla & DMA_CH_BURSTLEN_gm) == DMA_CH_BURSTLEN_4BYTE_gc, "Read burst length does not match configuration"); test_assert_true(test, read_config.trfcnt == transfer_count, "Read transfer count does not match configuration"); test_assert_true(test, read_config.repcnt == repeats, "Read repeat value does not match configuration"); #ifdef CONFIG_HAVE_HUGEMEM test_assert_true(test, read_config.srcaddr == src_huge_addr, "Read source address does not match configuration"); test_assert_true(test, read_config.destaddr == dest_huge_addr, "Read destination address does not match configuration"); #else test_assert_true(test, read_config.srcaddr16 == src_addr, "Read source address does not match configuration"); test_assert_true(test, read_config.destaddr16 == dest_addr, "Read destination address does not match configuration"); #endif dma_disable(); }
/** * \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); }