|
| 1 | +/** |
| 2 | + * Copyright (c) 2023 Raspberry Pi (Trading) Ltd. |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: BSD-3-Clause |
| 5 | + */ |
| 6 | + |
| 7 | +// Use the DMA engine's 'sniff' capability to calculate a CRC32 on data in a buffer. |
| 8 | +// Note: This does NOT do an actual data copy, it 'transfers' all the data to a single |
| 9 | +// dummy destination byte so as to be able to crawl over the input data using a 'DMA'. |
| 10 | +// If a data copy *with* a CRC32 sniff is required, the start address of the suitably sized |
| 11 | +// destination buffer must be supplied and the 'write_increment' set to true (see below). |
| 12 | + |
| 13 | +#include <stdio.h> |
| 14 | +#include <string.h> |
| 15 | +#include "pico/stdlib.h" |
| 16 | +#include "hardware/dma.h" |
| 17 | + |
| 18 | +#define CRC32_INIT ((uint32_t)-1l) |
| 19 | + |
| 20 | +#define DATA_TO_CHECK_LEN 9 |
| 21 | +#define CRC32_LEN 4 |
| 22 | +#define TOTAL_LEN (DATA_TO_CHECK_LEN + CRC32_LEN) |
| 23 | + |
| 24 | +// commonly used crc test data and also space for the crc value |
| 25 | +static uint8_t src[TOTAL_LEN] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x00, 0x00, 0x00, 0x00 }; |
| 26 | +static uint8_t dummy_dst[1]; |
| 27 | + |
| 28 | +// This uses a standard polynomial with the alternate 'reversed' shift direction. |
| 29 | +// It is possible to use a non-reversed algorithm here but the DMA sniff set-up |
| 30 | +// below would need to be modified to remain consistent and allow the check to pass. |
| 31 | +static uint32_t soft_crc32_block(uint32_t crc, uint8_t *bytp, uint32_t length) { |
| 32 | + while(length--) { |
| 33 | + uint32_t byte32 = (uint32_t)*bytp++; |
| 34 | + |
| 35 | + for (uint8_t bit = 8; bit; bit--, byte32 >>= 1) { |
| 36 | + crc = (crc >> 1) ^ (((crc ^ byte32) & 1ul) ? 0xEDB88320ul : 0ul); |
| 37 | + } |
| 38 | + } |
| 39 | + return crc; |
| 40 | +} |
| 41 | + |
| 42 | +int main() { |
| 43 | + uint32_t crc_res; |
| 44 | + |
| 45 | + stdio_init_all(); |
| 46 | + |
| 47 | + // calculate and append the crc |
| 48 | + crc_res = soft_crc32_block(CRC32_INIT, src, DATA_TO_CHECK_LEN); |
| 49 | + *((uint32_t *)&src[DATA_TO_CHECK_LEN]) = crc_res; |
| 50 | + |
| 51 | + printf("Buffer to DMA: "); |
| 52 | + for (int i = 0; i < TOTAL_LEN; i++) { |
| 53 | + printf("0x%02x ", src[i]); |
| 54 | + } |
| 55 | + printf("\n"); |
| 56 | + |
| 57 | + // UNcomment the next line to deliberately corrupt the buffer |
| 58 | + //src[0]++; // modify any byte, in any way, to break the CRC32 check |
| 59 | + |
| 60 | + // Get a free channel, panic() if there are none |
| 61 | + int chan = dma_claim_unused_channel(true); |
| 62 | + |
| 63 | + // 8 bit transfers. The read address increments after each transfer but |
| 64 | + // the write address remains unchanged pointing to the dummy destination. |
| 65 | + // No DREQ is selected, so the DMA transfers as fast as it can. |
| 66 | + dma_channel_config c = dma_channel_get_default_config(chan); |
| 67 | + channel_config_set_transfer_data_size(&c, DMA_SIZE_8); |
| 68 | + channel_config_set_read_increment(&c, true); |
| 69 | + channel_config_set_write_increment(&c, false); |
| 70 | + |
| 71 | + // (bit-reverse) CRC32 specific sniff set-up |
| 72 | + channel_config_set_sniff_enable(&c, true); |
| 73 | + dma_sniffer_set_data_accumulator(CRC32_INIT); |
| 74 | + dma_sniffer_set_output_reverse_enabled(true); |
| 75 | + dma_sniffer_enable(chan, DMA_SNIFF_CTRL_CALC_VALUE_CRC32R, true); |
| 76 | + |
| 77 | + dma_channel_configure( |
| 78 | + chan, // Channel to be configured |
| 79 | + &c, // The configuration we just created |
| 80 | + dummy_dst, // The (unchanging) write address |
| 81 | + src, // The initial read address |
| 82 | + TOTAL_LEN, // Total number of transfers inc. appended crc; each is 1 byte |
| 83 | + true // Start immediately. |
| 84 | + ); |
| 85 | + |
| 86 | + // We could choose to go and do something else whilst the DMA is doing its |
| 87 | + // thing. In this case the processor has nothing else to do, so we just |
| 88 | + // wait for the DMA to finish. |
| 89 | + dma_channel_wait_for_finish_blocking(chan); |
| 90 | + |
| 91 | + uint32_t sniffed_crc = dma_sniffer_get_data_accumulator(); |
| 92 | + printf("Completed DMA sniff of %d byte buffer, DMA sniff accumulator value: 0x%lx\n", TOTAL_LEN, sniffed_crc); |
| 93 | + |
| 94 | + if (0ul == sniffed_crc) { |
| 95 | + printf("CRC32 check is good\n"); |
| 96 | + } |
| 97 | + else { |
| 98 | + printf("ERROR - CRC32 check FAILED!\n"); |
| 99 | + } |
| 100 | +} |
0 commit comments